From d4a62634cfbb1513e4e4d34c9202f99fe00054e3 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Tue, 4 Apr 2017 15:13:41 +0200 Subject: [PATCH 001/144] upsmon: add support for alarms Add general support for alarms to upsmon, which relates to the ups.status variable of a device holding the flag "ALARM". upsmon now has a "notify type" alarm, which allows to react on such events, as with other events Signed-off-by: Arnaud Quette --- clients/upsmon.c | 24 +++++++++++++++++++++++- clients/upsmon.h | 19 ++++++++++++------- conf/upsmon.conf.sample.in | 3 +++ docs/man/upsmon.conf.txt | 2 ++ docs/man/upsmon.txt | 3 +++ 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 12823fa1ee..9321488e68 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -2,7 +2,8 @@ Copyright (C) 1998 Russell Kroll - 2012 Arnaud Quette + 2012 Arnaud Quette + 2017 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 @@ -741,6 +742,22 @@ static void upsreplbatt(utype_t *ups) } } +static void device_on_alarm(utype_t *ups) +{ + if (flag_isset(ups->status, ST_ALARM)) { /* potentially no change */ + /* TODO: add device.alarm.count */ + upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); + return; + } + + upsdebugx(3, "%s: %s (first time)", __func__, ups->sys); + + /* must have changed from !ALARM to ALARM, so notify */ + + do_notify(ups, NOTIFY_ALARM); + setflag(&ups->status, ST_ALARM); +} + static void ups_fsd(utype_t *ups) { if (flag_isset(ups->status, ST_FSD)) { /* no change */ @@ -1468,6 +1485,8 @@ static void parse_status(utype_t *ups, char *status) clearflag(&ups->status, ST_LOWBATT); if (!strstr(status, "FSD")) clearflag(&ups->status, ST_FSD); + if (!strstr(status, "ALARM")) + clearflag(&ups->status, ST_ALARM); statword = status; @@ -1487,6 +1506,8 @@ static void parse_status(utype_t *ups, char *status) ups_low_batt(ups); if (!strcasecmp(statword, "RB")) upsreplbatt(ups); + if (!strcasecmp(statword, "ALARM")) + device_on_alarm(ups); /* do it last to override any possible OL */ if (!strcasecmp(statword, "FSD")) @@ -1502,6 +1523,7 @@ static void parse_status(utype_t *ups, char *status) static void pollups(utype_t *ups) { char status[SMALLBUF]; + char *alarms; /* try a reconnect here */ if (!flag_isset(ups->status, ST_CONNECTED)) diff --git a/clients/upsmon.h b/clients/upsmon.h index 2504370cf1..8127ff59ce 100644 --- a/clients/upsmon.h +++ b/clients/upsmon.h @@ -1,6 +1,8 @@ /* upsmon.h - headers and other useful things for upsmon.h - Copyright (C) 2000 Russell Kroll + Copyright (C) + 2000 Russell Kroll + 2017 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 @@ -22,10 +24,11 @@ #define ST_ONLINE (1 << 0) /* UPS is on line (OL) */ #define ST_ONBATT (1 << 1) /* UPS is on battery (OB) */ #define ST_LOWBATT (1 << 2) /* UPS has a low battery (LB) */ -#define ST_FSD (1 << 3) /* master has set forced shutdown flag */ -#define ST_MASTER (1 << 4) /* we are the master on this UPS */ -#define ST_LOGIN (1 << 5) /* we are logged into this UPS */ -#define ST_CONNECTED (1 << 6) /* upscli_connect returned OK */ +#define ST_ALARM (1 << 3) /* UPS has alarm(s) (ALARM) */ +#define ST_FSD (1 << 4) /* master has set forced shutdown flag */ +#define ST_MASTER (1 << 5) /* we are the master on this UPS */ +#define ST_LOGIN (1 << 6) /* we are logged into this UPS */ +#define ST_CONNECTED (1 << 7) /* upscli_connect returned OK */ /* required contents of flag file */ #define SDMAGIC "upsmon-shutdown-file" @@ -39,7 +42,7 @@ extern "C" { /* UPS tracking structure */ typedef struct { - UPSCONN_t conn; /* upsclient state descriptor */ + UPSCONN_t conn; /* upsclient state descriptor */ char *sys; /* raw system name from .conf */ char *upsname; /* just upsname */ @@ -75,6 +78,7 @@ typedef struct { #define NOTIFY_REPLBATT 7 /* UPS battery needs to be replaced */ #define NOTIFY_NOCOMM 8 /* UPS hasn't been contacted in awhile */ #define NOTIFY_NOPARENT 9 /* privileged parent process died */ +#define NOTIFY_ALARM 10 /* UPS has 1 or more alarm(s) */ /* notify flag values */ @@ -97,7 +101,8 @@ struct { { NOTIFY_ONLINE, "ONLINE", NULL, "UPS %s on line power", NOTIFY_SYSLOG | NOTIFY_WALL }, { NOTIFY_ONBATT, "ONBATT", NULL, "UPS %s on battery", NOTIFY_SYSLOG | NOTIFY_WALL }, { NOTIFY_LOWBATT, "LOWBATT", NULL, "UPS %s battery is low", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_FSD, "FSD", NULL, "UPS %s: forced shutdown in progress", NOTIFY_SYSLOG | NOTIFY_WALL }, + { NOTIFY_ALARM, "ALARM", NULL, "UPS %s has one or more alarms (check ups.alarm)", NOTIFY_SYSLOG | NOTIFY_WALL }, + { NOTIFY_FSD, "FSD", NULL, "UPS %s: forced shutdown in progress", NOTIFY_SYSLOG | NOTIFY_WALL }, { NOTIFY_COMMOK, "COMMOK", NULL, "Communications with UPS %s established", NOTIFY_SYSLOG | NOTIFY_WALL }, { NOTIFY_COMMBAD, "COMMBAD", NULL, "Communications with UPS %s lost", NOTIFY_SYSLOG | NOTIFY_WALL }, { NOTIFY_SHUTDOWN, "SHUTDOWN", NULL, "Auto logout and shutdown proceeding", NOTIFY_SYSLOG | NOTIFY_WALL }, diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index 5ceffeb8a1..02961fbc12 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -240,6 +240,7 @@ POWERDOWNFLAG /etc/killpower # NOTIFYMSG ONLINE "UPS %s on line power" # NOTIFYMSG ONBATT "UPS %s on battery" # NOTIFYMSG LOWBATT "UPS %s battery is low" +# NOTIFYMSG ALARM "UPS %s has one or more alarms (check ups.alarm)" # NOTIFYMSG FSD "UPS %s: forced shutdown in progress" # NOTIFYMSG COMMOK "Communications with UPS %s established" # NOTIFYMSG COMMBAD "Communications with UPS %s lost" @@ -255,6 +256,7 @@ POWERDOWNFLAG /etc/killpower # ONLINE : UPS is back online # ONBATT : UPS is on battery # LOWBATT : UPS has a low battery (if also on battery, it's "critical") +# ALARM : UPS has one or more alarms (look at "ups.alarm" for details) # FSD : UPS is being shutdown by the master (FSD = "Forced Shutdown") # COMMOK : Communications established with the UPS # COMMBAD : Communications lost to the UPS @@ -274,6 +276,7 @@ POWERDOWNFLAG /etc/killpower # NOTIFYFLAG ONLINE SYSLOG+WALL # NOTIFYFLAG ONBATT SYSLOG+WALL # NOTIFYFLAG LOWBATT SYSLOG+WALL +# NOTIFYFLAG ALARM SYSLOG+WALL # NOTIFYFLAG FSD SYSLOG+WALL # NOTIFYFLAG COMMOK SYSLOG+WALL # NOTIFYFLAG COMMBAD SYSLOG+WALL diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 3f7e6740e2..f7f5155466 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -201,6 +201,8 @@ ONBATT;; UPS is on battery LOWBATT;; UPS is on battery and has a low battery (is critical) +ALARM;; UPS has one or more alarms (check ups.alarm) + FSD;; UPS is being shutdown by the master (FSD = "Forced Shutdown") COMMOK;; Communications established with the UPS diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index a330644216..77528da14b 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -127,6 +127,9 @@ The UPS is on battery. *LOWBATT*:: The UPS battery is low (as determined by the driver). +*ALARM*:: +The UPS has one or more alarms (look at "ups.alarm" for details). + *FSD*:: The UPS has been commanded into the "forced shutdown" mode. From f7de20aee19918b6c2d4d7c52feb7f129ed1ae16 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Tue, 4 Apr 2017 15:15:44 +0200 Subject: [PATCH 002/144] upsstats (CGI): add support for alarms Whenever a device publishes the ALARM flag in ups.status, the CGI will publish it part of the Status Signed-off-by: Arnaud Quette --- clients/status.h | 1 + 1 file changed, 1 insertion(+) diff --git a/clients/status.h b/clients/status.h index c98c489a93..7dbee753b7 100644 --- a/clients/status.h +++ b/clients/status.h @@ -33,6 +33,7 @@ struct { { "OL", "ONLINE", 0 }, { "OB", "ON BATTERY", 2 }, { "LB", "LOW BATTERY", 2 }, + { "ALARM", "ALARM", 2 }, { "RB", "REPLACE BATTERY", 2 }, { "OVER", "OVERLOAD", 2 }, { "TRIM", "VOLTAGE TRIM", 1 }, From f6eb991971ed03e631b09227460ec2c3c12582e3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Nov 2024 19:40:25 +0100 Subject: [PATCH 003/144] Introduce "sdcommands" setting for all drivers [#2670] Signed-off-by: Jim Klimov --- NEWS.adoc | 11 ++++++ conf/ups.conf.sample | 6 +++ docs/man/ups.conf.txt | 24 ++++++++++++ docs/nut.dict | 3 +- drivers/main.c | 89 ++++++++++++++++++++++++++++++++++++++++++- drivers/main.h | 20 +++++++++- 6 files changed, 149 insertions(+), 4 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index fe174f6b1f..3e67816990 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -314,6 +314,17 @@ during a NUT build. for `bcmxcp_usb`, `richcomm_usb` and `nutdrv_atcl_usb` drivers for now [#1763, #1764, #1768, #2580] + - all drivers should now support the optional `sdcommands` setting with + a site-local list of instant commands to handle `upsdrv_shutdown()`, + which may be useful in cases when the driver's built-in commands + (or their order) do not meet the goals of particular NUT deployment. + This can also help with shutdown endgame testing, using a mock command like + starting the beeper (where supported) to verify that the UPS communications + happen as expected, without compromising the load connected to the UPS. ++ +NOTE: during this overhaul, many older drivers got their first ever supported +INSTCMD such as `shutdown.return`, `shutdown.stayoff` or `load.off`. [#2670] + - common code: * introduced a `NUT_DEBUG_SYSLOG` environment variable to tweak activation of syslog message emission (and related detachment of `stderr` when diff --git a/conf/ups.conf.sample b/conf/ups.conf.sample index c9b011cd0a..6a7a72c8fd 100644 --- a/conf/ups.conf.sample +++ b/conf/ups.conf.sample @@ -160,6 +160,12 @@ maxretry = 3 # # The default value for this parameter is 0. # +# sdcommands: OPTIONAL. Comma-separated list of instant command name(s) +# to send to the UPS when you request its shutdown. For more +# details about relevant use-cases see the ups.conf manual page. +# +# The default value is built into each driver (where supported). +# # desc: optional, to keep a note of the UPS purpose, location, etc. # # nolock: optional, and not recommended for use in this file. diff --git a/docs/man/ups.conf.txt b/docs/man/ups.conf.txt index 50fea2d27f..09b32a4126 100644 --- a/docs/man/ups.conf.txt +++ b/docs/man/ups.conf.txt @@ -215,6 +215,30 @@ set this to -1. + The default value for this parameter is 0. +*sdcommands*:: + +Optional. Comma-separated list of instant command name(s) to send to +the UPS when you request its shutdown. ++ +Default value is built into each driver (where supported). ++ +The primary use-case is for devices whose drivers "natively" support +trying several commands, but the built-in order of those calls a +command that is mis-handled by the specific device model (so the +handling is reported as successful and the loop stops, but nothing +happens as far as the load power-down is concerned). ++ +Another use-case is differentiation of automated power-off scenarios +where the UPS and its load should stay "OFF" (e.g. by building emergency +power-off) vs. those where the load should return to work automatically +when it is safe to do so. NOTE: This would *currently* need editing of +`ups.conf` for such cases before `nutshutdown` sees the file; but could +be better automated in future NUT releases. ++ +NOTE: User-provided commands may be something other than actual shutdown, +e.g. a beeper to test that the INSTCMD happened such and when expected, +and the device was contacted, without impacting the load fed by the UPS. + *allow_killpower*:: Optional. This allows you to request `driver.killpower` instant command, to immediately call the driver-specific default implementation of diff --git a/docs/nut.dict b/docs/nut.dict index 2338eba070..5b8d3fa341 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 3244 utf-8 +personal_ws-1.1 en 3245 utf-8 AAC AAS ABI @@ -2751,6 +2751,7 @@ screenshots scriptname sd sdcmd +sdcommands sddelay sdk sdl diff --git a/drivers/main.c b/drivers/main.c index cb21941146..fdaa17e4f4 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -38,7 +38,11 @@ /* data which may be useful to the drivers */ TYPE_FD upsfd = ERROR_FD; -char *device_path = NULL; +/* Cached values of dstate_getinfo("driver.parameter.port") + * and dstate_getinfo("driver.parameter.sdcommands") - set + * during their assignment when reading program parameters + * from CLI or config file. */ +char *device_path = NULL, *device_sdcommands = NULL; const char *progname = NULL, *upsname = NULL, *device_name = NULL; /* may be set by the driver to wake up while in dstate_poll_fds */ @@ -665,7 +669,7 @@ int testval_reloadable(const char *var, const char *oldval, const char *newval, /* Similar to testvar_reloadable() above which is for addvar*() defined * entries, but for less streamlined stuff defined right here in main.c. - * See if (by name saved in dstate) can be (re-)loaded now: + * See if (by saved in dstate) can be (re-)loaded now: * either it is reloadable by parameter definition, or no value has been * saved into it yet ( is NULL). * Returns "-1" if nothing needs to be done and that is not a failure @@ -741,6 +745,77 @@ void addvar(int vartype, const char *name, const char *desc) do_addvar(vartype, name, desc, 0); } +/* Try each instant command in the comma-separated list of + * sdcmds, until the first one that reports it was handled. + * Returns STAT_INSTCMD_HANDLED if one of those was accepted + * by the device, or STAT_INSTCMD_INVALID if none succeeded. + * If cmdused is not NULL, it is populated by the command + * string which succeeded (or NULL if none), and the caller + * should free() it eventually. + */ +int do_loop_shutdown_commands(const char *sdcmds, char **cmdused) { + int cmdret = STAT_INSTCMD_UNKNOWN; + char *buf = NULL, *s = NULL; + + upsdebugx(1, "%s(%s)...", __func__, NUT_STRARG(sdcmds)); + + if (cmdused) + *cmdused = NULL; + + if (!sdcmds || !*sdcmds) { + upsdebugx(1, "This driver or its configuration did not pass any instant commands to run"); + goto done; + } + + if (upsh.instcmd == NULL) { + upsdebugx(1, "This driver does not implement INSTCMD support"); + goto done; + } + + buf = xstrdup(sdcmds); + while ((s = strtok(s == NULL ? buf : NULL, ",")) != NULL) { + if (!*s) + continue; + if ((cmdret = upsh.instcmd(s, NULL)) == STAT_INSTCMD_HANDLED) { + /* Shutdown successful */ + if (cmdused) + *cmdused = xstrdup(s); + upsdebugx(1, "%s(): command '%s' was handled successfully", __func__, NUT_STRARG(s)); + goto done; + } + } + +done: + if (buf) + free(buf); + if (cmdret != STAT_INSTCMD_HANDLED) + cmdret = STAT_INSTCMD_INVALID; + upsdebugx(1, "%s(%s): %d", __func__, NUT_STRARG(sdcmds), cmdret); + return cmdret; +} + +/* Use driver-provided sdcmds_default, unless a custom driver parameter value + * "sdcommands" is set - then use it instead. Call do_loop_shutdown_commands() + * for actual work; return STAT_INSTCMD_HANDLED or STAT_INSTCMD_HANDLED as + * applicable; if caller-provided cmdused is not NULL, populate it with the + * command that was used successfully (if any). + */ +int loop_shutdown_commands(const char *sdcmds_default, char **cmdused) { + const char *sdcmds_custom = device_sdcommands; + + /* Belts and suspenders... */ + if (!sdcmds_custom) + sdcmds_custom = dstate_getinfo("driver.parameter.sdcommands"); + + if (sdcmds_custom) { + upsdebugx(1, "%s: call do_loop_shutdown_commands() with custom sdcommands", __func__); + return do_loop_shutdown_commands(sdcmds_custom, cmdused); + } else { + upsdebugx(1, "%s: call do_loop_shutdown_commands() with driver-default sdcommands", __func__); + return do_loop_shutdown_commands(sdcmds_default, cmdused); + } +} + /* handle instant commands common for all drivers */ int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { char buf[SMALLBUF]; @@ -1058,6 +1133,16 @@ static int main_arg(char *var, char *val) if (!strcmp(var, "desc")) return 1; /* handled */ + if (!strcmp(var, "sdcommands")) { + if (testinfo_reloadable(var, "driver.parameter.sdcommands", val, 1) > 0) { + if (device_sdcommands) + free(device_sdcommands); + device_sdcommands = xstrdup(val); + dstate_setinfo("driver.parameter.sdcommands", "%s", val); + } + return 1; /* handled */ + } + /* Allow each driver to specify its minimal debugging level - * admins can set more with command-line args, but can't set * less without changing config. Should help debug of services. diff --git a/drivers/main.h b/drivers/main.h index 5276c98ece..255ff93b40 100644 --- a/drivers/main.h +++ b/drivers/main.h @@ -12,7 +12,7 @@ /* public functions & variables from main.c */ extern const char *progname, *upsname, *device_name; -extern char *device_path; +extern char *device_path, *device_sdcommands; extern int broken_driver, experimental_driver, do_lock_port, exit_flag; extern TYPE_FD upsfd, extrafd; extern time_t poll_interval; @@ -30,6 +30,24 @@ void set_exit_flag(int sig); /* --- details for the variable/value sharing --- */ +/* Try each instant command in the comma-separated list of + * sdcmds, until the first one that reports it was handled. + * Returns STAT_INSTCMD_HANDLED if one of those was accepted + * by the device, or STAT_INSTCMD_INVALID if none succeeded. + * If cmdused is not NULL, it is populated by the command + * string which succeeded (or NULL if none), and the caller + * should free() it eventually. + */ +int do_loop_shutdown_commands(const char *sdcmds, char **cmdused); + +/* Use driver-provided sdcmds_default, unless a custom driver parameter value + * "sdcommands" is set - then use it instead. Call do_loop_shutdown_commands() + * for actual work; return STAT_INSTCMD_HANDLED or STAT_INSTCMD_HANDLED as + * applicable; if caller-provided cmdused is not NULL, populate it with the + * command that was used successfully (if any). + */ +int loop_shutdown_commands(const char *sdcmds_default, char **cmdused); + /* handle instant commands common for all drivers * (returns STAT_INSTCMD_* state values per enum in upshandler.h) */ From ccddee5e12a95eeabfc46799584191a6dd00f17a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 11:05:23 +0100 Subject: [PATCH 004/144] drivers/mge-utalk.c: update to use "sdcommands" [#2670] Signed-off-by: Jim Klimov --- drivers/mge-utalk.c | 59 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/drivers/mge-utalk.c b/drivers/mge-utalk.c index dca2a14fe6..1bf2edf19f 100644 --- a/drivers/mge-utalk.c +++ b/drivers/mge-utalk.c @@ -69,7 +69,7 @@ /* --------------------------------------------------------------- */ #define DRIVER_NAME "MGE UPS SYSTEMS/U-Talk driver" -#define DRIVER_VERSION "0.96" +#define DRIVER_VERSION "0.97" /* driver description structure */ @@ -475,10 +475,54 @@ void upsdrv_updateinfo(void) /* --------------------------------------------------------------- */ +/* See comment in method below */ +static char handling_instcmd_shutdown = 0; void upsdrv_shutdown(void) { - char buf[BUFFLEN]; - /* static time_t lastcmd = 0; */ + char buf[BUFFLEN]; + /* static time_t lastcmd = 0; */ + + /* We can enter this method either by handling of INSTCMD (whether it + * was called by user, or by ourselves here via loop method), or by + * handling of `drivername -k`. Avoid infinite loops with this flag + * here and with handling_instcmd_shutdown above. + */ + static char already_shutting_down = 0; + + if (!handling_instcmd_shutdown + && !already_shutting_down + && device_sdcommands + ) { + char *cmdstr = NULL; + int cmdret = -1; + + already_shutting_down = 1; + handling_instcmd_shutdown = -1; + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + cmdret = loop_shutdown_commands(NULL, &cmdstr); + if (cmdret != STAT_INSTCMD_HANDLED) { + upslogx(LOG_WARNING, "Failed to command the UPS to '%s'", NUT_STRARG(cmdstr)); + if (cmdstr) + free(cmdstr); + } + + /* Reset the flags if not going down */ + already_shutting_down = 0; + handling_instcmd_shutdown = 0; + return; + } + /* just in case */ + already_shutting_down = 1; + + /* Here we are if handling explicit INSTCMD to shut down, + * or this method was called and works to handle default + * "sdcommands", or is recursively called with a custom + * value of "sdcommands" pointing here. + */ memset(buf, 0, sizeof(buf)); if (sdtype == SD_RETURN) { @@ -488,8 +532,8 @@ void upsdrv_shutdown(void) upslogx(LOG_INFO, "UPS response to Automatic Restart was %s", buf); } - /* Only call the effective shutoff if restart is ok */ - /* or if we need only a stayoff... */ + /* Only call the effective shutoff if restart is ok, + * or if we need (caller asked for) only a stayoff... */ if (!strcmp(buf, "OK") || (sdtype == SD_STAYOFF)) { /* shutdown UPS */ mge_command(buf, sizeof(buf), "Sx 0"); @@ -498,6 +542,7 @@ void upsdrv_shutdown(void) } /* if(strcmp(buf, "OK")) */ + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ /* call the cleanup to disable/close the comm link */ upsdrv_cleanup(); } @@ -545,12 +590,16 @@ int instcmd(const char *cmdname, const char *extra) if (!strcasecmp(cmdname, "shutdown.stayoff")) { sdtype = SD_STAYOFF; + if (!handling_instcmd_shutdown) + handling_instcmd_shutdown = 1; upsdrv_shutdown(); } if (!strcasecmp(cmdname, "shutdown.return")) { sdtype = SD_RETURN; + if (!handling_instcmd_shutdown) + handling_instcmd_shutdown = 1; upsdrv_shutdown(); } From 4cf357a51f40eff474b985738d064dddfbb4a96a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 12:11:56 +0100 Subject: [PATCH 005/144] drivers/everups.c: update to use "sdcommands" and have an instcmd() at all [#2670] Signed-off-by: Jim Klimov --- drivers/everups.c | 59 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/drivers/everups.c b/drivers/everups.c index 6a3b09d57f..8e89c6944b 100644 --- a/drivers/everups.c +++ b/drivers/everups.c @@ -21,7 +21,7 @@ #include "serial.h" #define DRIVER_NAME "Ever UPS driver (serial)" -#define DRIVER_VERSION "0.06" +#define DRIVER_VERSION "0.07" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -34,6 +34,9 @@ upsdrv_info_t upsdrv_info = { static unsigned char upstype = 0; +/* Forward decls */ +static int instcmd(const char *cmdname, const char *extra); + static void init_serial(void) { ser_set_dtr(upsfd, 0); @@ -86,6 +89,15 @@ void upsdrv_initinfo(void) { dstate_setinfo("ups.mfr", "Ever"); dstate_setinfo("ups.model", "%s", GetTypeUpsName()); + + /* commands ----------------------------------------------- */ + /* FIXME: Check with the device what our instcmd + * (nee upsdrv_shutdown() contents) actually does! + */ + dstate_addcmd("load.off"); + + /* install handlers */ + upsh.instcmd = instcmd; } void upsdrv_updateinfo(void) @@ -161,20 +173,41 @@ void upsdrv_updateinfo(void) dstate_dataok(); } -void upsdrv_shutdown(void) +/* handler for commands to be sent to UPS */ +static +int instcmd(const char *cmdname, const char *extra) { - if (!Code(2)) { - upslog_with_errno(LOG_INFO, "Code failed"); - return; - } - ser_send_char(upsfd, 28); - ser_send_char(upsfd, 1); /* 1.28 sec */ - if (!Code(1)) { - upslog_with_errno(LOG_INFO, "Code failed"); - return; + NUT_UNUSED_VARIABLE(extra); + + /* FIXME: Which one is this - "load.off", + * "shutdown.stayoff" or "shutdown.return"? */ + + /* Shutdown UPS */ + if (!strcasecmp(cmdname, "load.off")) + { + if (!Code(2)) { + upslog_with_errno(LOG_INFO, "Code failed"); + return STAT_INSTCMD_UNKNOWN; + } + ser_send_char(upsfd, 28); + ser_send_char(upsfd, 1); /* 1.28 sec */ + if (!Code(1)) { + upslog_with_errno(LOG_INFO, "Code failed"); + return STAT_INSTCMD_UNKNOWN; + } + ser_send_char(upsfd, 13); + ser_send_char(upsfd, 8); + + return STAT_INSTCMD_HANDLED; } - ser_send_char(upsfd, 13); - ser_send_char(upsfd, 8); + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} + +void upsdrv_shutdown(void) +{ + loop_shutdown_commands("load.off", NULL); } void upsdrv_help(void) From aa08ae525fe85e55cb326e8f0c8d37dff2114d24 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 13:14:56 +0100 Subject: [PATCH 006/144] drivers/adelsystem_cbi.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.stayoff") [#2670] Signed-off-by: Jim Klimov --- drivers/adelsystem_cbi.c | 72 ++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index 2247b6e6a2..bc9e98427d 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -30,7 +30,7 @@ #include #define DRIVER_NAME "NUT ADELSYSTEM DC-UPS CB/CBI driver" -#define DRIVER_VERSION "0.03" +#define DRIVER_VERSION "0.04" /* variables */ static modbus_t *mbctx = NULL; /* modbus memory context */ @@ -49,6 +49,7 @@ static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ +static char handling_upsdrv_shutdown = 0; /* initialize alarm structs */ void alrminit(void); @@ -193,6 +194,11 @@ void upsdrv_initinfo(void) /* register instant commands */ dstate_addcmd("load.off"); + /* FIXME: Check with the device what this instcmd + * (nee upsdrv_shutdown() contents) actually does! + */ + dstate_addcmd("shutdown.stayoff"); + /* set callback for instant commands */ upsh.instcmd = upscmd; } @@ -464,33 +470,8 @@ void upsdrv_updateinfo(void) /* shutdown UPS */ void upsdrv_shutdown(void) { - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslog_with_errno(LOG_ERR, "upscmd: gettimeofday"); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); + handling_upsdrv_shutdown = 1; + loop_shutdown_commands("shutdown.stayoff", NULL); } /* print driver usage info */ @@ -832,6 +813,41 @@ int upscmd(const char *cmd, const char *arg) upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); rval = STAT_INSTCMD_HANDLED; } + } else if (!strcasecmp(cmd, "shutdown.stayoff")) { + /* FIXME: Which one is this actually - + * "shutdown.stayoff" or "shutdown.return"? */ + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslog_with_errno(LOG_ERR, "upscmd: gettimeofday"); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + if (handling_upsdrv_shutdown) + fatalx(EXIT_FAILURE, "shutdown failed"); + upslog_with_errno(LOG_ERR, "instcmd: %s failed", cmd); + break; + case STAT_INSTCMD_UNKNOWN: + if (handling_upsdrv_shutdown) + fatalx(EXIT_FAILURE, "shutdown not supported"); + upslog_with_errno(LOG_ERR, "instcmd: %s not supported", cmd); + break; + default: + upslogx(LOG_INFO, "shutdown command executed"); + break; + } } else { upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); rval = STAT_INSTCMD_UNKNOWN; From e775b01d2679cb6f2d34023c678b40e78bad1ba5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 13:24:15 +0100 Subject: [PATCH 007/144] drivers/al175.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/al175.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/al175.c b/drivers/al175.c index 8438882144..8ad4e548ea 100644 --- a/drivers/al175.c +++ b/drivers/al175.c @@ -52,7 +52,7 @@ typedef uint8_t byte_t; #define DRIVER_NAME "Eltek AL175/COMLI driver" -#define DRIVER_VERSION "0.15" +#define DRIVER_VERSION "0.16" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -1275,8 +1275,17 @@ void upsdrv_shutdown(void) it doesn't respond at first if possible */ /* replace with a proper shutdown function */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ From 12867757bbe542b4ffa633500a2fa402a58fda20 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 13:37:32 +0100 Subject: [PATCH 008/144] drivers/apc_modbus.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/apc_modbus.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/apc_modbus.c b/drivers/apc_modbus.c index 07f6a46354..3651d82148 100644 --- a/drivers/apc_modbus.c +++ b/drivers/apc_modbus.c @@ -38,7 +38,7 @@ #else # define DRIVER_NAME "NUT APC Modbus driver without USB support" #endif -#define DRIVER_VERSION "0.10" +#define DRIVER_VERSION "0.11" #if defined NUT_MODBUS_HAS_USB @@ -1569,6 +1569,16 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + + /* FIXME: Make a name for default original shutdown: + * got no direct equivalent in apc_modbus_command_map[] + * used for instcmd above + */ modbus_write_register(modbus_ctx, APC_MODBUS_OUTLETCOMMAND_BF_REG, APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_SHUTDOWN | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP); } From f8da375b368bd88acd707780732a2bde75ede9dc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 14:56:19 +0100 Subject: [PATCH 009/144] drivers/apcsmart-old.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/apcsmart-old.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/apcsmart-old.c b/drivers/apcsmart-old.c index 5a56c99106..3d78a0f2f7 100644 --- a/drivers/apcsmart-old.c +++ b/drivers/apcsmart-old.c @@ -25,7 +25,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "APC Smart protocol driver (old)" -#define DRIVER_VERSION "2.33" +#define DRIVER_VERSION "2.34" static upsdrv_info_t table_info = { "APC command table", @@ -1067,6 +1067,12 @@ void upsdrv_shutdown(void) ssize_t ret; long status; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + if (!smartmode()) upsdebugx(1, "SM detection failed. Trying a shutdown command anyway"); From b8fb714635ebcaf6113a1dc21653c0051c99bab0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 14:57:37 +0100 Subject: [PATCH 010/144] drivers/apcsmart.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/apcsmart.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 54c04dcda7..d12b85c402 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -37,7 +37,7 @@ #include "apcsmart_tabs.h" #define DRIVER_NAME "APC Smart protocol driver" -#define DRIVER_VERSION "3.34" +#define DRIVER_VERSION "3.35" #ifdef WIN32 # ifndef ECANCELED @@ -1755,6 +1755,12 @@ void upsdrv_shutdown(void) { char temp[APC_LBUF]; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + if (!smartmode(1)) upslogx(LOG_WARNING, "%s: %s", __func__, "setting SmartMode failed !"); From c18e0aae3166e897476a68242cbe18c034557c47 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 15:00:06 +0100 Subject: [PATCH 011/144] drivers/apcupsd-ups.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/apcupsd-ups.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/apcupsd-ups.c b/drivers/apcupsd-ups.c index 9f4b71bc20..26fba8294f 100644 --- a/drivers/apcupsd-ups.c +++ b/drivers/apcupsd-ups.c @@ -57,7 +57,7 @@ typedef struct pollfd { #include "nut_stdint.h" #define DRIVER_NAME "apcupsd network client UPS driver" -#define DRIVER_VERSION "0.72" +#define DRIVER_VERSION "0.73" #define POLL_INTERVAL_MIN 10 @@ -364,8 +364,17 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { /* replace with a proper shutdown function */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } } void upsdrv_help(void) From ac1b463b43f2f0f294c7633400a855e948b5744f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 15:01:29 +0100 Subject: [PATCH 012/144] drivers/asem.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/asem.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/asem.c b/drivers/asem.c index 82b4746627..9f28a73c53 100644 --- a/drivers/asem.c +++ b/drivers/asem.c @@ -67,7 +67,7 @@ #endif #define DRIVER_NAME "ASEM" -#define DRIVER_VERSION "0.13" +#define DRIVER_VERSION "0.14" /* Valid on ASEM PB1300 UPS */ #define BQ2060_ADDRESS 0x0B @@ -332,8 +332,17 @@ void upsdrv_shutdown(void) it doesn't respond at first if possible */ /* replace with a proper shutdown function */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ From cd19aa9f53e610b97a65ea2278f14c1216690897 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 15:04:02 +0100 Subject: [PATCH 013/144] drivers/bcmxcp.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/bcmxcp.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/bcmxcp.c b/drivers/bcmxcp.c index 63822ae876..c26eccc081 100644 --- a/drivers/bcmxcp.c +++ b/drivers/bcmxcp.c @@ -116,7 +116,7 @@ TODO List: #include "bcmxcp.h" #define DRIVER_NAME "BCMXCP UPS driver" -#define DRIVER_VERSION "0.35" +#define DRIVER_VERSION "0.36" #define MAX_NUT_NAME_LENGTH 128 #define NUT_OUTLET_POSITION 7 @@ -1950,19 +1950,15 @@ void upsdrv_shutdown(void) { upsdebugx(1, "upsdrv_shutdown..."); - /* Try to shutdown with delay */ - if (instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) { - /* Shutdown successful */ - return; - } - - /* If the above doesn't work, try shutdown.stayoff */ - if (instcmd("shutdown.stayoff", NULL) == STAT_INSTCMD_HANDLED) { + /* First try to shutdown with delay; + * if the above doesn't work, try shutdown.stayoff */ + if (loop_shutdown_commands("shutdown.return,shutdown.stayoff", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ return; } upslogx(LOG_ERR, "Shutdown failed!"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); } From 14a62df94e1b0bcbb93f4e1ea49124852a2c647d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 15:08:17 +0100 Subject: [PATCH 014/144] drivers/belkin.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.return") [#2670] Signed-off-by: Jim Klimov --- drivers/belkin.c | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/drivers/belkin.c b/drivers/belkin.c index 8b339d2260..b4124211ef 100644 --- a/drivers/belkin.c +++ b/drivers/belkin.c @@ -29,7 +29,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Belkin Smart protocol driver" -#define DRIVER_VERSION "0.27" +#define DRIVER_VERSION "0.28" static ssize_t init_communication(void); static ssize_t get_belkin_reply(char *buf); @@ -352,25 +352,7 @@ void upsdrv_updateinfo(void) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - ssize_t res; - - res = init_communication(); - if (res < 0) { - printf("Detection failed. Trying a shutdown command anyway.\n"); - } - - /* tested on a F6C525-SER: this works when OL and OB */ - - /* shutdown type 2 (UPS system) */ - send_belkin_command(CONTROL, "SDT", "2"); - - /* SDR means "do SDT and SDA, then reboot after n minutes" */ - send_belkin_command(CONTROL, "SDR", "1"); - - printf("UPS should power off load in 5 seconds\n"); - - /* shutdown in 5 seconds */ - send_belkin_command(CONTROL, "SDA", "5"); + loop_shutdown_commands("shutdown.return", NULL); } /* handle "beeper.disable" */ @@ -432,6 +414,30 @@ static int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } + if (!strcasecmp(cmdname, "shutdown.return")) { + ssize_t res; + + res = init_communication(); + if (res < 0) { + printf("Detection failed. Trying a shutdown command anyway.\n"); + } + + /* tested on a F6C525-SER: this works when OL and OB */ + + /* shutdown type 2 (UPS system) */ + send_belkin_command(CONTROL, "SDT", "2"); + + /* SDR means "do SDT and SDA, then reboot after n minutes" */ + send_belkin_command(CONTROL, "SDR", "1"); + + printf("UPS should power off load in 5 seconds\n"); + + /* shutdown in 5 seconds */ + send_belkin_command(CONTROL, "SDA", "5"); + + return STAT_INSTCMD_HANDLED; + } + if (!strcasecmp(cmdname, "load.off")) { do_off(); return STAT_INSTCMD_HANDLED; @@ -530,6 +536,7 @@ void upsdrv_initinfo(void) dstate_addcmd("beeper.disable"); dstate_addcmd("beeper.enable"); + dstate_addcmd("shutdown.return"); dstate_addcmd("load.off"); dstate_addcmd("load.on"); dstate_addcmd("test.battery.start.quick"); From 2ab7f2cc91205c7d235e85d0cffa3560f8cae03b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 15:11:51 +0100 Subject: [PATCH 015/144] drivers/belkinunv.c: update to use "sdcommands" (if customized by user); revise warning message [#2670] Signed-off-by: Jim Klimov --- drivers/belkinunv.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/belkinunv.c b/drivers/belkinunv.c index 0c82865321..11183d856c 100644 --- a/drivers/belkinunv.c +++ b/drivers/belkinunv.c @@ -94,7 +94,7 @@ #include "serial.h" #define DRIVER_NAME "Belkin 'Universal UPS' driver" -#define DRIVER_VERSION "0.10" +#define DRIVER_VERSION "0.11" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -1180,9 +1180,19 @@ void upsdrv_shutdown(void) Don't use this! Use the solution involving the "-x wait" option instead, as suggested on the belkinunv(8) man - page. */ + page. + */ - upslogx(LOG_WARNING, "You are using the -k option, which is broken for this driver.\nShutting down for 10 minutes and hoping for the best"); + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + + upslogx(LOG_WARNING, + "You are using the -k option, which is broken for this driver.\n" + "Check belkinunv(8) man page about '-x wait' option instead.\n" + "Shutting down for 10 minutes and hoping for the best"); belkin_nut_write_int(REG_RESTARTTIMER, 10); /* 10 minutes */ belkin_nut_write_int(REG_SHUTDOWNTIMER, 1); /* 1 second */ From b0d683a65f146cfc1c582400ad14b3f83b5ccf94 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 15:20:21 +0100 Subject: [PATCH 016/144] drivers/bestfcom.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.return"); newly support INSTCMD at all [#2670] Signed-off-by: Jim Klimov --- drivers/bestfcom.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/bestfcom.c b/drivers/bestfcom.c index 5402114652..aea0820759 100644 --- a/drivers/bestfcom.c +++ b/drivers/bestfcom.c @@ -45,7 +45,7 @@ #include "serial.h" #define DRIVER_NAME "Best Ferrups/Fortress driver" -#define DRIVER_VERSION "0.15" +#define DRIVER_VERSION "0.16" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -103,6 +103,7 @@ static struct { static int inverter_status; /* Forward decls */ +static int instcmd(const char *cmdname, const char *extra); /* Set up all the funky shared memory stuff used to communicate with upsd */ void upsdrv_initinfo (void) @@ -158,6 +159,12 @@ void upsdrv_initinfo (void) fc.fullvolts, fc.lowvolts, fc.emptyvolts); + + /* commands ----------------------------------------------- */ + dstate_addcmd("shutdown.return"); + + /* install handlers */ + upsh.instcmd = instcmd; } @@ -461,12 +468,30 @@ static void ups_sync(void) ser_get_line(upsfd, buf, sizeof(buf), '>', "\012", 3, 0); } +/* handler for commands to be sent to UPS */ +static +int instcmd(const char *cmdname, const char *extra) +{ + NUT_UNUSED_VARIABLE(extra); + + if (!strcasecmp(cmdname, "shutdown.return")) { + /* NB: hard-wired password */ + ser_send(upsfd, "pw377\r"); + /* power off in 10 seconds and restart when line power returns, + * FE7K required a min of 5 seconds for off to function */ + ser_send(upsfd, "o 10 a\r"); + + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} + /* power down the attached load immediately */ void upsdrv_shutdown(void) { - /* NB: hard-wired password */ - ser_send(upsfd, "pw377\r"); - ser_send(upsfd, "o 10 a\r"); /* power off in 10 seconds and restart when line power returns, FE7K required a min of 5 seconds for off to function */ + loop_shutdown_commands("shutdown.return", NULL); } /* list flags and values that you want to receive via -x */ From 675db7736576b200bd9cc4ae2d38a95a65ff8247 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 15:27:34 +0100 Subject: [PATCH 017/144] drivers/bestfortress.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.return") [#2670] Signed-off-by: Jim Klimov --- drivers/bestfortress.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/bestfortress.c b/drivers/bestfortress.c index 8c650abb60..d690d51d50 100644 --- a/drivers/bestfortress.c +++ b/drivers/bestfortress.c @@ -35,7 +35,7 @@ #endif #define DRIVER_NAME "Best Fortress UPS driver" -#define DRIVER_VERSION "0.09" +#define DRIVER_VERSION "0.10" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -514,24 +514,9 @@ static int upsdrv_setvar (const char *var, const char * data) { */ void upsdrv_shutdown(void) { - const char *grace; - upsdebugx(2, "%s: begin", __func__); - grace = dstate_getinfo("ups.delay.shutdown"); - if (!grace) { - upsdebugx(1, "%s: ups.delay.shutdown is NULL!", __func__); - /* Pick a different value than 20 so we can see it in the logs. */ - grace = "30"; - } - - upslogx(LOG_CRIT, "%s: OFF/restart in %s seconds", __func__, grace); - - /* Start again, overriding front panel setting. */ - autorestart (1); - - upssend ("OFF%s\r", grace); - /* I'm nearly dead, Jim */ + loop_shutdown_commands("shutdown.return", NULL); upsdebugx(2, "%s: end", __func__); } @@ -546,8 +531,26 @@ static int instcmd (const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } else if (!strcasecmp(cmdname, "shutdown.return")) { + const char *grace; + upsdebugx(2, "%s: %s: start", __func__, cmdname); - upsdrv_shutdown(); + + grace = dstate_getinfo("ups.delay.shutdown"); + if (!grace) { + upsdebugx(1, "%s: ups.delay.shutdown is NULL!", __func__); + /* Pick a different value than 20 so we can see it in the logs. */ + grace = "30"; + } + + upslogx(LOG_CRIT, "%s: OFF/restart in %s seconds", __func__, grace); + + /* Start again, overriding front panel setting. */ + autorestart (1); + + upssend ("OFF%s\r", grace); + /* I'm nearly dead, Jim */ + + upsdebugx(2, "%s: %s: end", __func__, cmdname); return STAT_INSTCMD_HANDLED; } /* \todo Software error or user error? */ From 5fe157637223db50579b0856e69f84e29f845caa Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 15:36:38 +0100 Subject: [PATCH 018/144] drivers/bestuferrups.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.return"); newly support INSTCMD at all [#2670] Signed-off-by: Jim Klimov --- drivers/bestuferrups.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/bestuferrups.c b/drivers/bestuferrups.c index 1585d6a903..41d45a6d8c 100644 --- a/drivers/bestuferrups.c +++ b/drivers/bestuferrups.c @@ -33,7 +33,7 @@ #include "serial.h" #define DRIVER_NAME "Best Ferrups Series ME/RE/MD driver" -#define DRIVER_VERSION "0.06" +#define DRIVER_VERSION "0.07" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -77,6 +77,7 @@ static struct { /* Forward decls */ +static int instcmd(const char *cmdname, const char *extra); /* Set up all the funky shared memory stuff used to communicate with upsd */ void upsdrv_initinfo (void) @@ -101,9 +102,15 @@ void upsdrv_initinfo (void) fprintf(stderr, "Best Power %s detected\n", dstate_getinfo("ups.model")); fprintf(stderr, "Battery voltages %5.1f nominal, %5.1f full, %5.1f empty\n", - fc.idealbvolts, - fc.fullvolts, - fc.emptyvolts); + fc.idealbvolts, + fc.fullvolts, + fc.emptyvolts); + + /* commands ----------------------------------------------- */ + dstate_addcmd("shutdown.return"); + + /* install handlers */ + upsh.instcmd = instcmd; } @@ -361,12 +368,29 @@ static void ups_sync(void) } } +/* handler for commands to be sent to UPS */ +static +int instcmd(const char *cmdname, const char *extra) +{ + NUT_UNUSED_VARIABLE(extra); + + if (!strcasecmp(cmdname, "shutdown.return")) { + /* NB: hard-wired password */ + ser_send(upsfd, "pw377\r"); + /* power off in 1 second and restart when line power returns */ + ser_send(upsfd, "off 1 a\r"); + + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} + /* power down the attached load immediately */ void upsdrv_shutdown(void) { -/* NB: hard-wired password */ - ser_send(upsfd, "pw377\r"); - ser_send(upsfd, "off 1 a\r"); /* power off in 1 second and restart when line power returns */ + loop_shutdown_commands("shutdown.return", NULL); } /* list flags and values that you want to receive via -x */ From fd42c1bb2caa72e9068d487b460861718bcf63ee Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 15:45:21 +0100 Subject: [PATCH 019/144] drivers/bestups.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/bestups.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/bestups.c b/drivers/bestups.c index b34e804c5b..4689dfd4df 100644 --- a/drivers/bestups.c +++ b/drivers/bestups.c @@ -29,7 +29,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Best UPS driver" -#define DRIVER_VERSION "1.09" +#define DRIVER_VERSION "1.10" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -319,6 +319,12 @@ static int ups_on_line(void) void upsdrv_shutdown(void) { + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + printf("The UPS will shut down in approximately one minute.\n"); if (ups_on_line()) From 465552cc08fa2572ce8bddb03afb2a08311c897a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 15:56:13 +0100 Subject: [PATCH 020/144] drivers/bicker_ser.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/bicker_ser.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/bicker_ser.c b/drivers/bicker_ser.c index a5c2c4db49..4093ffc3d4 100644 --- a/drivers/bicker_ser.c +++ b/drivers/bicker_ser.c @@ -108,7 +108,7 @@ #include "serial.h" #define DRIVER_NAME "Bicker serial protocol" -#define DRIVER_VERSION "0.02" +#define DRIVER_VERSION "0.03" #define BICKER_SOH 0x01 #define BICKER_EOT 0x04 @@ -862,14 +862,22 @@ void upsdrv_shutdown(void) { int retry; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + for (retry = 1; retry <= BICKER_RETRIES; retry++) { if (bicker_shutdown() > 0) { + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-2); /* EXIT_SUCCESS */ return; } } upslogx(LOG_ERR, "Shutdown failed!"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); } From 42300329544d2742b4ab2460b0e5944aca4e9e59 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 16:02:28 +0100 Subject: [PATCH 021/144] drivers/blazer*.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/blazer.c | 8 ++++++++ drivers/blazer_ser.c | 2 +- drivers/blazer_usb.c | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/blazer.c b/drivers/blazer.c index af224fb5bb..744cb72d27 100644 --- a/drivers/blazer.c +++ b/drivers/blazer.c @@ -853,6 +853,12 @@ void upsdrv_shutdown(void) { int retry; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + /* Stop pending shutdowns */ for (retry = 1; retry <= MAXTRIES; retry++) { @@ -876,10 +882,12 @@ void upsdrv_shutdown(void) } upslogx(LOG_ERR, "Shutting down in %ld seconds", offdelay); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-2); /* EXIT_SUCCESS */ return; } upslogx(LOG_ERR, "Shutdown failed!"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); } diff --git a/drivers/blazer_ser.c b/drivers/blazer_ser.c index 5f830e3e6a..8bdc2c423f 100644 --- a/drivers/blazer_ser.c +++ b/drivers/blazer_ser.c @@ -31,7 +31,7 @@ #include "blazer.h" #define DRIVER_NAME "Megatec/Q1 protocol serial driver" -#define DRIVER_VERSION "1.62" +#define DRIVER_VERSION "1.63" /* driver description structure */ upsdrv_info_t upsdrv_info = { diff --git a/drivers/blazer_usb.c b/drivers/blazer_usb.c index fb0f75a2d5..f2cf3432aa 100644 --- a/drivers/blazer_usb.c +++ b/drivers/blazer_usb.c @@ -37,7 +37,7 @@ #endif #define DRIVER_NAME "Megatec/Q1 protocol USB driver" -#define DRIVER_VERSION "0.20" +#define DRIVER_VERSION "0.21" /* driver description structure */ upsdrv_info_t upsdrv_info = { From e738dbc939b6b3eddfc4d3a509d86239059ab875 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 16:04:39 +0100 Subject: [PATCH 022/144] drivers/clone-outlet.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/clone-outlet.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/clone-outlet.c b/drivers/clone-outlet.c index 791a9648c9..f8d9ec2af5 100644 --- a/drivers/clone-outlet.c +++ b/drivers/clone-outlet.c @@ -31,7 +31,7 @@ #endif #define DRIVER_NAME "Clone outlet UPS driver" -#define DRIVER_VERSION "0.06" +#define DRIVER_VERSION "0.07" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -513,10 +513,19 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { /* replace with a proper shutdown function */ + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ /* - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); */ + } } From fd6b9516eafcad845db784c5f7ab4cd76e0ba3b5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 16:05:41 +0100 Subject: [PATCH 023/144] drivers/clone.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/clone.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/clone.c b/drivers/clone.c index b15819eb78..4a1b2ac753 100644 --- a/drivers/clone.c +++ b/drivers/clone.c @@ -33,7 +33,7 @@ #endif #define DRIVER_NAME "Clone UPS driver" -#define DRIVER_VERSION "0.06" +#define DRIVER_VERSION "0.07" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -661,8 +661,17 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { /* replace with a proper shutdown function */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } } From e79cdea30c102fc2dbe16818fd0c5c76519a6d42 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Nov 2024 16:19:25 +0100 Subject: [PATCH 024/144] drivers/dummy-ups.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/dummy-ups.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index 939bf16b1b..0d66ef3137 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -2,7 +2,7 @@ Copyright (C) 2005 - 2015 Arnaud Quette - 2014 - 2023 Jim Klimov + 2014 - 2024 Jim Klimov 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 @@ -48,7 +48,7 @@ #include "dummy-ups.h" #define DRIVER_NAME "Device simulation and repeater driver" -#define DRIVER_VERSION "0.19" +#define DRIVER_VERSION "0.20" /* driver description structure */ upsdrv_info_t upsdrv_info = @@ -383,8 +383,17 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { /* replace with a proper shutdown function */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } } static int instcmd(const char *cmdname, const char *extra) From 34b24b64c6668b462a5ab3f4a956e805c735bd65 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 12:15:16 +0100 Subject: [PATCH 025/144] drivers/etapro.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.return") [#2670] Signed-off-by: Jim Klimov --- drivers/etapro.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/etapro.c b/drivers/etapro.c index 6e4f593fe8..8393d59619 100644 --- a/drivers/etapro.c +++ b/drivers/etapro.c @@ -55,7 +55,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "ETA PRO driver" -#define DRIVER_VERSION "0.07" +#define DRIVER_VERSION "0.08" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -66,6 +66,14 @@ upsdrv_info_t upsdrv_info = { { NULL } }; +/* TODO: delays should be tunable, the UPS supports max 32767 minutes. */ + +/* Shutdown command to off delay in seconds. */ +#define SHUTDOWN_GRACE_TIME 10 + +/* Shutdown to return delay in seconds. */ +#define SHUTDOWN_TO_RETURN_TIME 15 + static int etapro_get_response(const char *resp_type) { @@ -192,7 +200,8 @@ static int instcmd(const char *cmdname, const char *extra) } if (!strcasecmp(cmdname, "shutdown.return")) { - upsdrv_shutdown(); + etapro_set_on_timer(SHUTDOWN_GRACE_TIME + SHUTDOWN_TO_RETURN_TIME); + etapro_set_off_timer(SHUTDOWN_GRACE_TIME); return STAT_INSTCMD_HANDLED; } @@ -331,19 +340,10 @@ upsdrv_updateinfo(void) dstate_dataok(); } -/* TODO: delays should be tunable, the UPS supports max 32767 minutes. */ - -/* Shutdown command to off delay in seconds. */ -#define SHUTDOWN_GRACE_TIME 10 - -/* Shutdown to return delay in seconds. */ -#define SHUTDOWN_TO_RETURN_TIME 15 - void upsdrv_shutdown(void) { - etapro_set_on_timer(SHUTDOWN_GRACE_TIME + SHUTDOWN_TO_RETURN_TIME); - etapro_set_off_timer(SHUTDOWN_GRACE_TIME); + loop_shutdown_commands("shutdown.return", NULL); } void From 80d583d551d2c5c10137488ef6d809cb63fc0086 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 12:22:54 +0100 Subject: [PATCH 026/144] drivers/gamatronic.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.return"); newly support INSTCMD at all [#2670] Signed-off-by: Jim Klimov --- drivers/gamatronic.c | 52 +++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/drivers/gamatronic.c b/drivers/gamatronic.c index 9c1b6fa6eb..f3237d5c9d 100644 --- a/drivers/gamatronic.c +++ b/drivers/gamatronic.c @@ -33,7 +33,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Gamatronic UPS driver" -#define DRIVER_VERSION "0.06" +#define DRIVER_VERSION "0.07" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -63,6 +63,9 @@ upsdrv_info_t upsdrv_info = { * This should normally be a parameter! */ #define GAMATRONIC_BUF_LEN 140 +/* Forward decls */ +static int instcmd(const char *cmdname, const char *extra); + static int sec_upsrecv (char *buf) { char lenbuf[4]; @@ -312,6 +315,12 @@ void upsdrv_initinfo(void) sec_poll(FLAG_POLLONCE); printf("UPS: %s %s\n", dstate_getinfo("ups.mfr"), dstate_getinfo("ups.model")); + + /* commands ----------------------------------------------- */ + dstate_addcmd("shutdown.return"); + + /* install handlers */ + upsh.instcmd = instcmd; } void upsdrv_updateinfo(void) @@ -326,34 +335,41 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - ssize_t msglen; - char msgbuf[SMALLBUF]; - - msglen = snprintf(msgbuf, sizeof(msgbuf), "-1"); - sec_cmd(SEC_SETCMD, SEC_SHUTDOWN, msgbuf, &msglen); - - msglen = snprintf(msgbuf, sizeof(msgbuf), "1"); - sec_cmd(SEC_SETCMD, SEC_AUTORESTART, msgbuf, &msglen); - - msglen = snprintf(msgbuf, sizeof(msgbuf), "2"); - sec_cmd(SEC_SETCMD, SEC_SHUTTYPE,msgbuf, &msglen); - - msglen = snprintf(msgbuf, sizeof(msgbuf), "5"); - sec_cmd(SEC_SETCMD, SEC_SHUTDOWN, msgbuf, &msglen); + loop_shutdown_commands("shutdown.return", NULL); } -/* +static int instcmd(const char *cmdname, const char *extra) { +/* if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; } +*/ + + if (!strcasecmp(cmdname, "shutdown.return")) { + ssize_t msglen; + char msgbuf[SMALLBUF]; + + msglen = snprintf(msgbuf, sizeof(msgbuf), "-1"); + sec_cmd(SEC_SETCMD, SEC_SHUTDOWN, msgbuf, &msglen); + + msglen = snprintf(msgbuf, sizeof(msgbuf), "1"); + sec_cmd(SEC_SETCMD, SEC_AUTORESTART, msgbuf, &msglen); + + msglen = snprintf(msgbuf, sizeof(msgbuf), "2"); + sec_cmd(SEC_SETCMD, SEC_SHUTTYPE,msgbuf, &msglen); - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + msglen = snprintf(msgbuf, sizeof(msgbuf), "5"); + sec_cmd(SEC_SETCMD, SEC_SHUTDOWN, msgbuf, &msglen); + + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } -*/ void upsdrv_help(void) { From 37af6517866fc71d351f0331fbf88815787ce0dc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 12:27:59 +0100 Subject: [PATCH 027/144] drivers/gamatronic.c: fix misplaced upslogx(1, ...) by upsdebugx() Signed-off-by: Jim Klimov --- drivers/gamatronic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gamatronic.c b/drivers/gamatronic.c index f3237d5c9d..e9ffe12902 100644 --- a/drivers/gamatronic.c +++ b/drivers/gamatronic.c @@ -83,7 +83,7 @@ static int sec_upsrecv (char *buf) lenbuf[3] = '\0'; ret = atoi(lenbuf); if (ret > GAMATRONIC_BUF_LEN) { - upslogx(1, "%s: got a longer response message " + upsdebugx(1, "%s: got a longer response message " "than expected for protocol: %d (%s) > %d", __func__, ret, lenbuf, GAMATRONIC_BUF_LEN); ret = GAMATRONIC_BUF_LEN; @@ -96,7 +96,7 @@ static int sec_upsrecv (char *buf) } /* else (ret <= 0) : */ - upslogx(1, "%s: invalid response message length: %s", + upsdebugx(1, "%s: invalid response message length: %s", __func__, lenbuf); return (-2); From 889c77e31d0f2c83ff76eb16444c27809204862e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 12:29:06 +0100 Subject: [PATCH 028/144] drivers/generic_gpio_common.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/generic_gpio_common.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/generic_gpio_common.c b/drivers/generic_gpio_common.c index 6457f8c7d8..891f7d8593 100644 --- a/drivers/generic_gpio_common.c +++ b/drivers/generic_gpio_common.c @@ -472,8 +472,17 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { /* replace with a proper shutdown function */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } } void upsdrv_help(void) From 37d51c4a9ab59b5c739ad7511a10435622990e5a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 14:56:40 +0100 Subject: [PATCH 029/144] drivers/generic_modbus.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.stayoff") [#2670] Signed-off-by: Jim Klimov --- drivers/generic_modbus.c | 74 +++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 4aa87f17e7..384aaaf897 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -27,7 +27,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "NUT Generic Modbus driver" -#define DRIVER_VERSION "0.05" +#define DRIVER_VERSION "0.06" /* variables */ static modbus_t *mbctx = NULL; /* modbus memory context */ @@ -47,6 +47,7 @@ static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus res static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ +static char handling_upsdrv_shutdown = 0; /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); @@ -95,6 +96,11 @@ void upsdrv_initinfo(void) { /* register instant commands */ if (sigar[FSD_T].addr != NOTUSED) { dstate_addcmd("load.off"); + + /* FIXME: Check with the device what this instcmd + * (nee upsdrv_shutdown() contents) actually does! + */ + dstate_addcmd("shutdown.stayoff"); } /* set callback for instant commands */ @@ -316,37 +322,8 @@ void upsdrv_updateinfo(void) /* shutdown UPS */ void upsdrv_shutdown(void) { - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslog_with_errno(LOG_ERR, "upscmd: gettimeofday"); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - upslogx(LOG_ERR, "shutdown failed"); - set_exit_flag(-1); - return; - case STAT_INSTCMD_UNKNOWN: - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); - return; - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); + handling_upsdrv_shutdown = 1; + loop_shutdown_commands("shutdown.stayoff", NULL); } /* print driver usage info */ @@ -630,6 +607,39 @@ int upscmd(const char *cmd, const char *arg) ); rval = STAT_INSTCMD_FAILED; } + } else if (!strcasecmp(cmd, "shutdown.stayoff")) { + /* FIXME: Which one is this actually - + * "shutdown.stayoff" or "shutdown.return"? */ + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslog_with_errno(LOG_ERR, "upscmd: gettimeofday"); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + upslogx(LOG_ERR, "shutdown failed"); + if (handling_upsdrv_shutdown) + set_exit_flag(-1); + return rval; + case STAT_INSTCMD_UNKNOWN: + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown) + set_exit_flag(-1); + return rval; + default: + upslogx(LOG_INFO, "shutdown command executed"); + break; + } } else { upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); rval = STAT_INSTCMD_UNKNOWN; From d16a41aa44aa0a66e616bd56650ab603d9bdc464 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 15:01:42 +0100 Subject: [PATCH 030/144] drivers/genericups.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/genericups.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/genericups.c b/drivers/genericups.c index fe9f1dbc9b..870d4baef1 100644 --- a/drivers/genericups.c +++ b/drivers/genericups.c @@ -31,7 +31,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Generic contact-closure UPS driver" -#define DRIVER_VERSION "1.40" +#define DRIVER_VERSION "1.41" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -312,8 +312,15 @@ void upsdrv_shutdown(void) { int flags, ret; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + if (upstype == -1) { upslogx(LOG_ERR, "No upstype set - see help text / man page!"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); return; } @@ -322,6 +329,7 @@ void upsdrv_shutdown(void) if (flags == -1) { upslogx(LOG_ERR, "No shutdown command defined for this model!"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); return; } @@ -340,6 +348,7 @@ void upsdrv_shutdown(void) if (ret != 0) { upslog_with_errno(LOG_ERR, "tcsendbreak"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); } @@ -354,6 +363,7 @@ void upsdrv_shutdown(void) if (ret != 0) { upslog_with_errno(LOG_ERR, "ioctl TIOCMSET"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); return; } @@ -434,8 +444,8 @@ void upsdrv_initups(void) } /* - See if the user wants to override the output signal definitions - this must be done here, since we might go to upsdrv_shutdown() + See if the user wants to override the output signal definitions? + This must be done here, since we might go to upsdrv_shutdown() immediately. Input signal definition override is handled in upsdrv_initinfo() */ From dea5b93f1cbbfea10b5f374f3a1fa6840a2e97c8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 15:15:40 +0100 Subject: [PATCH 031/144] drivers/huawei-ups2000.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/huawei-ups2000.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/huawei-ups2000.c b/drivers/huawei-ups2000.c index 403eb1bdcd..cf1d0d61b4 100644 --- a/drivers/huawei-ups2000.c +++ b/drivers/huawei-ups2000.c @@ -51,7 +51,7 @@ #include "timehead.h" /* fallback gmtime_r() variants if needed (e.g. some WIN32) */ #define DRIVER_NAME "NUT Huawei UPS2000 (1kVA-3kVA) RS-232 Modbus driver" -#define DRIVER_VERSION "0.07" +#define DRIVER_VERSION "0.08" #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) #define MODBUS_SLAVE_ID 1 @@ -1803,10 +1803,11 @@ void upsdrv_shutdown(void) { int r; - r = instcmd("shutdown.reboot", ""); + r = loop_shutdown_commands("shutdown.reboot", NULL); if (r != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "upsdrv_shutdown failed!"); - set_exit_flag(-1); + if (!device_sdcommands) + set_exit_flag(-1); } } From 2377d06a820afa293d757c99cae411bed438a73f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 15:16:46 +0100 Subject: [PATCH 032/144] drivers/hwmon_ina219.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/hwmon_ina219.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon_ina219.c b/drivers/hwmon_ina219.c index 988208a2a1..3e24f2d6a8 100644 --- a/drivers/hwmon_ina219.c +++ b/drivers/hwmon_ina219.c @@ -35,8 +35,9 @@ #define SYSFS_HWMON_DIR "/sys/class/hwmon" #define BATTERY_CHARGE_LOW 15 + #define DRIVER_NAME "hwmon-INA219 UPS driver" -#define DRIVER_VERSION "0.01" +#define DRIVER_VERSION "0.02" upsdrv_info_t upsdrv_info = { DRIVER_NAME, @@ -462,8 +463,18 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + /* replace with a proper shutdown function */ + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } } void upsdrv_help(void) From fb898031589be072e20fe645d0c463c9666dcde9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 15:34:53 +0100 Subject: [PATCH 033/144] drivers/isbmex.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.stayoff"); newly support INSTCMD at all [#2670] Signed-off-by: Jim Klimov --- drivers/isbmex.c | 73 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/drivers/isbmex.c b/drivers/isbmex.c index eff64b12f1..f46c86d781 100644 --- a/drivers/isbmex.c +++ b/drivers/isbmex.c @@ -27,7 +27,7 @@ #include #define DRIVER_NAME "ISBMEX UPS driver" -#define DRIVER_VERSION "0.10" +#define DRIVER_VERSION "0.11" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -53,6 +53,9 @@ upsdrv_info_t upsdrv_info = { #define MAXTRIES 15 /* #define IGNCHARS "" */ +/* Forward decls */ +static int instcmd(const char *cmdname, const char *extra); + static float lagrange(unsigned int vbyte) { float f0, f1, f2, f3, f4, f5, f6; @@ -145,6 +148,15 @@ void upsdrv_initinfo(void) /* addinfo(INFO_, "", 0, 0); */ /*printf("Using %s %s on %s\n", getdata(INFO_MFR), getdata(INFO_MODEL), device_path);*/ + + /* commands ----------------------------------------------- */ + /* FIXME: Check with the device what our instcmd + * (nee upsdrv_shutdown() contents) actually does! + */ + dstate_addcmd("shutdown.stayoff"); + + /* install handlers */ + upsh.instcmd = instcmd; } static const char *getpacket(int *we_know){ @@ -346,30 +358,53 @@ void upsdrv_updateinfo(void) return; } -void upsdrv_shutdown(void) +/* handler for commands to be sent to UPS */ +static +int instcmd(const char *cmdname, const char *extra) { - /* shutdown is supported on models with - * contact closure. Some ISB models with serial - * support support contact closure, some don't. - * If yours does support it, then a 12V signal - * on pin 9 does the trick (only when ups is - * on OB condition) */ - /* - * here try to do the pin 9 trick, if it does not - * work, else:*/ + NUT_UNUSED_VARIABLE(extra); + + /* FIXME: Which one is this - "load.off", + * "shutdown.stayoff" or "shutdown.return"? */ + + /* Shutdown UPS */ + if (!strcasecmp(cmdname, "shutdown.stayoff")) + { + /* shutdown is supported on models with + * contact closure. Some ISB models with serial + * support support contact closure, some don't. + * If yours does support it, then a 12V signal + * on pin 9 does the trick (only when ups is + * on OB condition) */ + /* + * here try to do the pin 9 trick, if it does not + * work, else:*/ /* - upslogx(LOG_ERR, "Shutdown only supported with the Generic Driver, type 6 and special cable"); - //upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + upslogx(LOG_ERR, "Shutdown only supported with the Generic Driver, type 6 and special cable"); + //upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); */ - int i; - for(i=0;i<=5;i++) - { - ser_send_char(upsfd, '#'); - usleep(50000); + int i; + for(i=0;i<=5;i++) + { + ser_send_char(upsfd, '#'); + usleep(50000); + } + + return STAT_INSTCMD_HANDLED; } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; } +void upsdrv_shutdown(void) +{ + /* FIXME: Check with the device what our instcmd + * (nee upsdrv_shutdown() contents) actually does! + */ + loop_shutdown_commands("shutdown.stayoff", NULL); +} void upsdrv_help(void) { From fceaa02f9b6a1da2a639d01e8a928c9ef285aee3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 17:52:10 +0100 Subject: [PATCH 034/144] drivers/ivtscd.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/ivtscd.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/ivtscd.c b/drivers/ivtscd.c index 318e09c241..e724fa2319 100644 --- a/drivers/ivtscd.c +++ b/drivers/ivtscd.c @@ -25,7 +25,7 @@ #include "attribute.h" #define DRIVER_NAME "IVT Solar Controller driver" -#define DRIVER_VERSION "0.05" +#define DRIVER_VERSION "0.06" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -183,8 +183,13 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - while (1) { + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + while (1) { if (ivt_status() < 7) { continue; } From e6c5f05b01455769a77271978dd52624a8880703 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 17:52:55 +0100 Subject: [PATCH 035/144] drivers/liebert-esp2.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/liebert-esp2.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/liebert-esp2.c b/drivers/liebert-esp2.c index 10b67b789e..ad441cf0bb 100644 --- a/drivers/liebert-esp2.c +++ b/drivers/liebert-esp2.c @@ -28,7 +28,7 @@ #define IsBitSet(val, bit) ((val) & (1 << (bit))) #define DRIVER_NAME "Liebert ESP-II serial UPS driver" -#define DRIVER_VERSION "0.07" +#define DRIVER_VERSION "0.08" #define UPS_SHUTDOWN_DELAY 12 /* it means UPS will be shutdown 120 sec */ #define SHUTDOWN_CMD_LEN 8 @@ -555,6 +555,12 @@ void upsdrv_shutdown(void) { char reply[8]; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + if(!(do_command(cmd_setOutOffMode, reply, 8) != -1) && (do_command(cmd_setOutOffDelay, reply, 8) != -1) && (do_command(cmd_sysLoadKey, reply, 6) != -1) && From a5940185cc1d23fbb8daaf67957d9f2c8d7d75ac Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 17:55:41 +0100 Subject: [PATCH 036/144] drivers/liebert-gxe.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/liebert-gxe.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/liebert-gxe.c b/drivers/liebert-gxe.c index f4c344a0da..81d21eadf3 100644 --- a/drivers/liebert-gxe.c +++ b/drivers/liebert-gxe.c @@ -24,7 +24,7 @@ #include "ydn23.h" #define DRIVER_NAME "Liebert GXE Series UPS driver" -#define DRIVER_VERSION "0.01" +#define DRIVER_VERSION "0.02" #define PROBE_RETRIES 3 #define DEFAULT_STALE_RETRIES 3 @@ -511,6 +511,13 @@ void upsdrv_initups(void) void upsdrv_shutdown(void) { + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + + /* FIXME: There seems to be instcmd(load.off), why not that? */ upslogx(LOG_INFO, "Liebert GXE UPS can't fully shutdown, NOOP"); } From 3392211c2c25d578b14fffe6eae5497b31c447de Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 17:58:53 +0100 Subject: [PATCH 037/144] drivers/liebert.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/liebert.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/liebert.c b/drivers/liebert.c index 0c1bc176cc..372bb34fb4 100644 --- a/drivers/liebert.c +++ b/drivers/liebert.c @@ -27,7 +27,7 @@ #include "attribute.h" #define DRIVER_NAME "Liebert MultiLink UPS driver" -#define DRIVER_VERSION "1.05" +#define DRIVER_VERSION "1.06" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -47,7 +47,14 @@ void upsdrv_shutdown(void) /* worse yet: stock cables don't support shutdown at all */ + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); } From ac118f49da6117969e9339018c0e941ccbb7ccdc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 18:52:01 +0100 Subject: [PATCH 038/144] drivers/macosx-ups.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/macosx-ups.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/macosx-ups.c b/drivers/macosx-ups.c index b35585ef93..66e3181ea7 100644 --- a/drivers/macosx-ups.c +++ b/drivers/macosx-ups.c @@ -29,7 +29,7 @@ #include "IOKit/ps/IOPSKeys.h" #define DRIVER_NAME "Mac OS X UPS meta-driver" -#define DRIVER_VERSION "1.41" +#define DRIVER_VERSION "1.42" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -272,8 +272,17 @@ void upsdrv_shutdown(void) /* NOTE: Mac OS X already has shutdown routines - this driver is more for monitoring and notification purposes. Still, there is a key that might be useful to set in SystemConfiguration land. */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ From 6ad244c2a20022054f4f48f5e2819db6b7e52e8f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Nov 2024 18:58:01 +0100 Subject: [PATCH 039/144] drivers/masterguard.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.return"); newly support INSTCMD at all [#2670] Signed-off-by: Jim Klimov --- drivers/masterguard.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/masterguard.c b/drivers/masterguard.c index f5bbb397ce..5e15ffe49f 100644 --- a/drivers/masterguard.c +++ b/drivers/masterguard.c @@ -9,6 +9,8 @@ generally covers all Megatec/Qx protocol family and aggregates device support from such legacy drivers over time. + FIXME: `if(DEBUG) print(...)` ==> `upsdebugx()` + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -29,7 +31,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "MASTERGUARD UPS driver" -#define DRIVER_VERSION "0.27" +#define DRIVER_VERSION "0.28" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -53,6 +55,9 @@ static int type; static char name[31]; static char firmware[6]; +/* Forward decls */ +static int instcmd(const char *cmdname, const char *extra); + /******************************************************************** * * Helper function to split a sting into words by splitting at the @@ -460,6 +465,11 @@ void upsdrv_initinfo(void) dstate_addcmd("test.battery.start"); */ + dstate_addcmd("shutdown.return"); + + /* install handlers */ + upsh.instcmd = instcmd; + if( strlen( name ) > 0 ) dstate_setinfo("ups.model", "%s", name); if( strlen( firmware ) > 0 ) @@ -529,6 +539,25 @@ void upsdrv_updateinfo(void) } } +/* handler for commands to be sent to UPS */ +static +int instcmd(const char *cmdname, const char *extra) +{ + NUT_UNUSED_VARIABLE(extra); + + /* Shutdown UPS */ + if (!strcasecmp(cmdname, "shutdown.return")) + { + /* ups will come up within a minute if utility is restored */ + ser_send_pace(upsfd, UPS_PACE, "%s", "S.2R0001\x0D" ); + + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} + /******************************************************************** * * Called if the driver wants to shutdown the UPS. @@ -540,8 +569,7 @@ void upsdrv_updateinfo(void) ********************************************************************/ void upsdrv_shutdown(void) { - /* ups will come up within a minute if utility is restored */ - ser_send_pace(upsfd, UPS_PACE, "%s", "S.2R0001\x0D" ); + loop_shutdown_commands("shutdown.return", NULL); } /******************************************************************** From 360a6b7fc397601290aad77a29fac7181d115850 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Nov 2024 19:47:28 +0100 Subject: [PATCH 040/144] drivers/metasys.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/metasys.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/metasys.c b/drivers/metasys.c index cae06a174b..cdb6e91d25 100644 --- a/drivers/metasys.c +++ b/drivers/metasys.c @@ -28,7 +28,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Metasystem UPS driver" -#define DRIVER_VERSION "0.10" +#define DRIVER_VERSION "0.11" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -870,9 +870,17 @@ void upsdrv_shutdown(void) { unsigned char command[10], answer[10]; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + /* Ensure that the ups is configured for automatically restart after a complete battery discharge - and when the power comes back after a shutdown */ + and when the power comes back after a shutdown. + Similar code to "shutdown.restart" but different timeouts. + */ if (! autorestart) { command[0]=UPS_SET_TIMES_ON_BATTERY; command[1]=0x00; /* max time on */ @@ -885,7 +893,7 @@ void upsdrv_shutdown(void) command_write_sequence(command, 6, answer); } - /* shedule a shutdown in 120 seconds */ + /* schedule a shutdown in 120 seconds */ command[0]=UPS_SET_SCHEDULING; command[1]=0x96; /* remaining */ command[2]=0x00; /* time */ @@ -939,7 +947,7 @@ static int instcmd(const char *cmdname, const char *extra) command[5]=0x01; /* autorestart after battery depleted enabled */ command_write_sequence(command, 6, answer); } - /* shedule a shutdown in 30 seconds */ + /* schedule a shutdown in 30 seconds */ command[0]=UPS_SET_SCHEDULING; command[1]=0x1e; /* remaining */ command[2]=0x00; /* time */ @@ -955,7 +963,7 @@ static int instcmd(const char *cmdname, const char *extra) } if (!strcasecmp(cmdname, "shutdown.stayoff")) { - /* shedule a shutdown in 30 seconds with no restart (-1) */ + /* schedule a shutdown in 30 seconds with no restart (-1) */ command[0]=UPS_SET_SCHEDULING; command[1]=0x1e; /* remaining */ command[2]=0x00; /* time */ From 9e89bd195b284452690a42ee4e35b52afb108fb7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Nov 2024 22:29:08 +0100 Subject: [PATCH 041/144] drivers/microdowell.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/microdowell.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/microdowell.c b/drivers/microdowell.c index 0d491b1c7e..2a2a94c6f3 100644 --- a/drivers/microdowell.c +++ b/drivers/microdowell.c @@ -44,7 +44,7 @@ #define MAX_SHUTDOWN_DELAY_LEN 5 #define DRIVER_NAME "MICRODOWELL UPS driver" -#define DRIVER_VERSION "0.04" +#define DRIVER_VERSION "0.05" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -949,6 +949,12 @@ void upsdrv_shutdown(void) unsigned char *p ; unsigned char BatteryFlag=0 ; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + OutBuff[0] = CMD_GET_STATUS ; /* get UPS status */ if ((p = CmdSerial(OutBuff, LEN_GET_STATUS, InpBuff)) != NULL) { From ae2ae3a75c3368165a8a5a81462f9e4671791443 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Nov 2024 22:35:45 +0100 Subject: [PATCH 042/144] drivers/microsol*.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/microsol-apc.c | 2 +- drivers/microsol-common.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/microsol-apc.c b/drivers/microsol-apc.c index c8979b67b1..71c16d8775 100644 --- a/drivers/microsol-apc.c +++ b/drivers/microsol-apc.c @@ -35,7 +35,7 @@ #include "microsol-apc.h" #define DRIVER_NAME "APC Back-UPS BR series UPS driver" -#define DRIVER_VERSION "0.71" +#define DRIVER_VERSION "0.72" /* driver description structure */ upsdrv_info_t upsdrv_info = { diff --git a/drivers/microsol-common.c b/drivers/microsol-common.c index 2ba496b36f..e92e87114a 100644 --- a/drivers/microsol-common.c +++ b/drivers/microsol-common.c @@ -754,6 +754,12 @@ void upsdrv_updateinfo(void) */ void upsdrv_shutdown(void) { + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + if (!line_unpowered) { /* on line */ upslogx(LOG_NOTICE, "On line, sending power cycle command..."); ser_send_char(upsfd, CMD_SHUTRET); From 3f6b070d3d254dad16d111c2c509650110efcadb Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Nov 2024 22:37:50 +0100 Subject: [PATCH 043/144] drivers/netxml-ups.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/netxml-ups.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/netxml-ups.c b/drivers/netxml-ups.c index cfdc31632c..f3a21c05cb 100644 --- a/drivers/netxml-ups.c +++ b/drivers/netxml-ups.c @@ -42,7 +42,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "network XML UPS" -#define DRIVER_VERSION "0.46" +#define DRIVER_VERSION "0.47" /** *_OBJECT query multi-part body boundary */ #define FORM_POST_BOUNDARY "NUT-NETXML-UPS-OBJECTS" @@ -414,6 +414,15 @@ void upsdrv_shutdown(void) { object_query_t *resp = NULL; object_query_t *req = NULL; + /* FIXME: Make a name for default original shutdown + * in particular to make it one of the options and + * call protocol cleanup below, if needed. + */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + /* Pragmatic do { ... } while (0) loop allowing break to cleanup */ do { /* Create SET_OBJECT request */ @@ -453,6 +462,7 @@ void upsdrv_shutdown(void) { if (STAT_SET_HANDLED != status) { upslogx(LOG_ERR, "Shutdown failed: %d", status); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); } } From 3c1643770379f6c43956b509c2c4081c54197bbb Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Nov 2024 22:38:19 +0100 Subject: [PATCH 044/144] drivers/nut-ipmipsu.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/nut-ipmipsu.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/nut-ipmipsu.c b/drivers/nut-ipmipsu.c index 95845285a3..55cfcb5f8f 100644 --- a/drivers/nut-ipmipsu.c +++ b/drivers/nut-ipmipsu.c @@ -27,7 +27,7 @@ #include "nut-ipmi.h" #define DRIVER_NAME "IPMI PSU driver" -#define DRIVER_VERSION "0.33" +#define DRIVER_VERSION "0.34" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -146,8 +146,17 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { /* replace with a proper shutdown function */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } } /* From 30b6a16936332734980827363a41f0119d1f13d1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Nov 2024 22:43:45 +0100 Subject: [PATCH 045/144] drivers/nutdrv_atcl_usb.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.stayoff"); newly support INSTCMD at all [#2670] Signed-off-by: Jim Klimov --- drivers/nutdrv_atcl_usb.c | 102 ++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 32 deletions(-) diff --git a/drivers/nutdrv_atcl_usb.c b/drivers/nutdrv_atcl_usb.c index 5c52057a33..5de62e568b 100644 --- a/drivers/nutdrv_atcl_usb.c +++ b/drivers/nutdrv_atcl_usb.c @@ -28,7 +28,7 @@ /* driver version */ #define DRIVER_NAME "'ATCL FOR UPS' USB driver" -#define DRIVER_VERSION "1.18" +#define DRIVER_VERSION "1.19" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -65,6 +65,9 @@ static usb_dev_handle *udev = NULL; static USBDevice_t usbdevice; static unsigned int comm_failures = 0; +/* Forward decls */ +static int instcmd(const char *cmdname, const char *extra); + static int device_match_func(USBDevice_t *device, void *privdata) { char *requested_vendor; @@ -582,6 +585,15 @@ void upsdrv_initinfo(void) dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID); dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID); + + /* commands ----------------------------------------------- */ + /* FIXME: Check with the device what our instcmd + * (nee upsdrv_shutdown() contents) actually does! + */ + dstate_addcmd("shutdown.stayoff"); + + /* install handlers */ + upsh.instcmd = instcmd; } void upsdrv_updateinfo(void) @@ -641,44 +653,70 @@ void upsdrv_updateinfo(void) status_commit(); } -/* If the UPS is on battery, it should shut down about 30 seconds after - * receiving this packet. - */ -void upsdrv_shutdown(void) +/* handler for commands to be sent to UPS */ +static +int instcmd(const char *cmdname, const char *extra) { - /* Not "const" because this mismatches arg type of usb_interrupt_write() */ - char shutdown_packet[SHUTDOWN_PACKETSIZE] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - int ret; + NUT_UNUSED_VARIABLE(extra); + + /* FIXME: Which one is this - "load.off", + * "shutdown.stayoff" or "shutdown.return"? */ + + /* Shutdown UPS */ + if (!strcasecmp(cmdname, "shutdown.stayoff")) { + /* If the UPS is on battery, it should shut down + * about 30 seconds after receiving this packet. + */ + + /* Not "const" because this mismatches arg type + * of usb_interrupt_write() */ + char shutdown_packet[SHUTDOWN_PACKETSIZE] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + int ret; + + upslogx(LOG_DEBUG, + "%s: attempting to call usb_interrupt_write(01 00 00 00 00 00 00 00)", + __func__); + + ret = usb_interrupt_write(udev, + SHUTDOWN_ENDPOINT, (usb_ctrl_charbuf)shutdown_packet, + SHUTDOWN_PACKETSIZE, ATCL_USB_TIMEOUT); + + if (ret <= 0) { + upslogx(LOG_NOTICE, + "%s: first usb_interrupt_write() failed: %s", + __func__, + ret ? nut_usb_strerror(ret) : "timeout"); + } - upslogx(LOG_DEBUG, - "%s: attempting to call usb_interrupt_write(01 00 00 00 00 00 00 00)", - __func__); + /* Totally guessing from the .pcap file here. + * TODO: configurable delay? + */ + usleep(170*1000); - ret = usb_interrupt_write(udev, - SHUTDOWN_ENDPOINT, (usb_ctrl_charbuf)shutdown_packet, - SHUTDOWN_PACKETSIZE, ATCL_USB_TIMEOUT); + ret = usb_interrupt_write(udev, + SHUTDOWN_ENDPOINT, (usb_ctrl_charbuf)shutdown_packet, + SHUTDOWN_PACKETSIZE, ATCL_USB_TIMEOUT); - if (ret <= 0) { - upslogx(LOG_NOTICE, - "%s: first usb_interrupt_write() failed: %s", - __func__, - ret ? nut_usb_strerror(ret) : "timeout"); - } - - /* Totally guessing from the .pcap file here. TODO: configurable delay? */ - usleep(170*1000); - - ret = usb_interrupt_write(udev, - SHUTDOWN_ENDPOINT, (usb_ctrl_charbuf)shutdown_packet, - SHUTDOWN_PACKETSIZE, ATCL_USB_TIMEOUT); + if (ret <= 0) { + upslogx(LOG_ERR, + "%s: second usb_interrupt_write() failed: %s", + __func__, + ret ? nut_usb_strerror(ret) : "timeout"); + } - if (ret <= 0) { - upslogx(LOG_ERR, - "%s: second usb_interrupt_write() failed: %s", - __func__, - ret ? nut_usb_strerror(ret) : "timeout"); + return STAT_INSTCMD_HANDLED; } + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} + +void upsdrv_shutdown(void) +{ + /* FIXME: Check with the device what our instcmd + * (nee upsdrv_shutdown() contents) actually does! + */ + loop_shutdown_commands("shutdown.stayoff", NULL); } void upsdrv_help(void) From 9758ee035c5025c68e8ef626d4ee645d7dfa77a7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Nov 2024 22:45:44 +0100 Subject: [PATCH 046/144] drivers/nutdrv_siemens_sitop.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/nutdrv_siemens_sitop.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/nutdrv_siemens_sitop.c b/drivers/nutdrv_siemens_sitop.c index f45bbc6dd4..daf88152f5 100644 --- a/drivers/nutdrv_siemens_sitop.c +++ b/drivers/nutdrv_siemens_sitop.c @@ -56,7 +56,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Siemens SITOP UPS500 series driver" -#define DRIVER_VERSION "0.05" +#define DRIVER_VERSION "0.06" #define RX_BUFFER_SIZE 100 @@ -248,8 +248,9 @@ void upsdrv_updateinfo(void) { } void upsdrv_shutdown(void) { - /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ - instcmd("shutdown.return", NULL); + /* by default, tell the UPS to shut down, + * then return - DO NOT SLEEP HERE */ + loop_shutdown_commands("shutdown.return", NULL); } From 699311f650554ab27a02eefe664972f6607cbd16 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Nov 2024 22:56:23 +0100 Subject: [PATCH 047/144] drivers/nutdrv_qx.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/nutdrv_qx.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 3bc1d8675a..1530ecf057 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -58,7 +58,7 @@ # define DRIVER_NAME "Generic Q* Serial driver" #endif /* QX_USB */ -#define DRIVER_VERSION "0.37" +#define DRIVER_VERSION "0.38" #ifdef QX_SERIAL # include "serial.h" @@ -2775,6 +2775,15 @@ void upsdrv_shutdown(void) upsdebugx(1, "%s...", __func__); + /* FIXME: Make a name for default original shutdown + * and note this common "sdcommands" feature can + * replace tunables used below ("stayoff" etc). + */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + /* Get user-defined delays */ /* Start delay */ From c76f86479436fe3c74d41d1476dd314838a22af2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Nov 2024 22:58:22 +0100 Subject: [PATCH 048/144] drivers/oneac.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/oneac.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/oneac.c b/drivers/oneac.c index dcbac8e744..217e3418e8 100644 --- a/drivers/oneac.c +++ b/drivers/oneac.c @@ -48,7 +48,7 @@ int setcmd(const char* varname, const char* setvalue); int instcmd(const char *cmdname, const char *extra); #define DRIVER_NAME "Oneac EG/ON/OZ/OB UPS driver" -#define DRIVER_VERSION "0.83" +#define DRIVER_VERSION "0.84" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -814,7 +814,14 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - ser_send(upsfd,"%s",SHUTDOWN); + /* FIXME: before loop_shutdown_commands(), code directly called + * here was identical to "shutdown.reboot", while the driver + * shutdown should more reasonably be "shutdown.return" (when + * wall power is back) or "shutdown.stayoff". All of these are + * implemented in instcmd() here nominally (not sure if named + * correctly - better re-check on hardware). + */ + loop_shutdown_commands("shutdown.reboot", NULL); } void upsdrv_help(void) From 14fa7505ccb8818f9be4a129ba2465f8a1414ade Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 17 Nov 2024 08:34:33 +0100 Subject: [PATCH 049/144] drivers/optiups.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/optiups.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/optiups.c b/drivers/optiups.c index 9f4344d53c..d0cb4616ea 100644 --- a/drivers/optiups.c +++ b/drivers/optiups.c @@ -28,7 +28,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Opti-UPS driver" -#define DRIVER_VERSION "1.05" +#define DRIVER_VERSION "1.06" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -300,7 +300,7 @@ static int instcmd(const char *cmdname, const char *extra) } else if (!strcasecmp(cmdname, "shutdown.stop")) { - /* Aborts a shutdown that is couting down via the Cs command */ + /* Aborts a shutdown that is counting down via the Cs command */ optiquery( "Cs-0000001" ); return STAT_INSTCMD_HANDLED; } @@ -542,8 +542,15 @@ void upsdrv_shutdown(void) /* If get no response, assume on battery & battery low */ long s = OPTISBIT_ON_BATTERY_POWER | OPTISBIT_LOW_BATTERY; + ssize_t r; - ssize_t r = optiquery( "AG" ); + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + + r = optiquery( "AG" ); if ( r < 1 ) { upslogx(LOG_ERR, "can't retrieve ups status during shutdown" ); From ecd5bc9812ff9029535cac147e733f809b5348b6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 17 Nov 2024 18:22:44 +0100 Subject: [PATCH 050/144] drivers/phoenixcontact_modbus.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/phoenixcontact_modbus.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/phoenixcontact_modbus.c b/drivers/phoenixcontact_modbus.c index ab447e19af..e27faf77ad 100644 --- a/drivers/phoenixcontact_modbus.c +++ b/drivers/phoenixcontact_modbus.c @@ -24,7 +24,7 @@ #include #define DRIVER_NAME "NUT PhoenixContact Modbus driver" -#define DRIVER_VERSION "0.04" +#define DRIVER_VERSION "0.05" #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) #define MODBUS_SLAVE_ID 192 @@ -134,8 +134,17 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { /* replace with a proper shutdown function */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } } void upsdrv_help(void) From a74c8e9e062b6fc2e0544274bd457223af3fa525 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 17 Nov 2024 18:27:54 +0100 Subject: [PATCH 051/144] drivers/pijuice.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.stayoff"); newly support INSTCMD at all [#2670] Signed-off-by: Jim Klimov --- drivers/pijuice.c | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index 09499f099a..4fda7adf82 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -23,7 +23,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "PiJuice UPS driver" -#define DRIVER_VERSION "0.12" +#define DRIVER_VERSION "0.13" /* * Linux I2C userland is a bit of a mess until distros refresh to @@ -46,6 +46,9 @@ # endif #endif +/* Forward decls */ +static int instcmd(const char *cmdname, const char *extra); + /* * i2c-tools pre-4.0 has a userspace header with a name that conflicts * with a kernel header, so it may be ignored/removed by distributions @@ -798,6 +801,15 @@ void upsdrv_initinfo(void) get_i2c_address(); get_battery_profile(); get_battery_profile_ext(); + + /* commands ----------------------------------------------- */ + /* FIXME: Check with the device what our instcmd + * (nee upsdrv_shutdown() contents) actually does! + */ + dstate_addcmd("shutdown.stayoff"); + + /* install handlers */ + upsh.instcmd = instcmd; } void upsdrv_updateinfo(void) @@ -817,9 +829,32 @@ void upsdrv_updateinfo(void) dstate_dataok(); } +/* handler for commands to be sent to UPS */ +static +int instcmd(const char *cmdname, const char *extra) +{ + NUT_UNUSED_VARIABLE(extra); + + /* FIXME: Which one is this - "load.off", + * "shutdown.stayoff" or "shutdown.return"? */ + + /* Shutdown UPS */ + if (!strcasecmp(cmdname, "shutdown.stayoff")) { + set_power_off(); + + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} + void upsdrv_shutdown(void) { - set_power_off(); + /* FIXME: Check with the device what our instcmd + * (nee upsdrv_shutdown() contents) actually does! + */ + loop_shutdown_commands("shutdown.stayoff", NULL); } void upsdrv_help(void) From 0a92a24e36a1601a03f103545c88d458f6a9fec9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 10:41:00 +0100 Subject: [PATCH 052/144] drivers/powercom.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/powercom.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/powercom.c b/drivers/powercom.c index d31e730b7c..0b072844e4 100644 --- a/drivers/powercom.c +++ b/drivers/powercom.c @@ -86,7 +86,7 @@ #include "nut_float.h" #define DRIVER_NAME "PowerCom protocol UPS driver" -#define DRIVER_VERSION "0.23" +#define DRIVER_VERSION "0.24" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -297,6 +297,7 @@ static void shutdown_halt(void) ser_send_char (upsfd, types[type].shutdown_arguments.delay[1]); upslogx(LOG_INFO, "Shutdown (stayoff) initiated."); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-2); /* EXIT_SUCCESS */ } @@ -309,6 +310,7 @@ static void shutdown_ret(void) ser_send_char (upsfd, types[type].shutdown_arguments.delay[1]); upslogx(LOG_INFO, "Shutdown (return) initiated."); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-2); /* EXIT_SUCCESS */ } @@ -842,9 +844,12 @@ void upsdrv_updateinfo(void) /* shutdown UPS */ void upsdrv_shutdown(void) { - /* power down the attached load immediately */ - printf("Forced UPS shutdown (and wait for power)...\n"); - shutdown_ret(); + if (!device_sdcommands) { + /* default: power down the attached load immediately */ + printf("Forced UPS shutdown (and wait for power)...\n"); + } + + loop_shutdown_commands("shutdown.return", NULL); } /* initialize UPS */ From bcf91c7566c1dde2540ff933716855ed9643974f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 10:45:23 +0100 Subject: [PATCH 053/144] drivers/powerman.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/powerman-pdu.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/powerman-pdu.c b/drivers/powerman-pdu.c index 277bd4d347..ccea743111 100644 --- a/drivers/powerman-pdu.c +++ b/drivers/powerman-pdu.c @@ -23,7 +23,7 @@ #include /* pm_err_t and other beasts */ #define DRIVER_NAME "Powerman PDU client driver" -#define DRIVER_VERSION "0.14" +#define DRIVER_VERSION "0.15" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -135,12 +135,21 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { - /* FIXME: shutdown all outlets? */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); - - /* OL: this must power cycle the load if possible */ - /* OB: the load must remain off until the power returns */ + /* replace with a proper shutdown function */ + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + /* FIXME: shutdown all outlets? */ + /* OL: this must power cycle the load if possible */ + /* OB: the load must remain off until the power returns */ + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } } /* From d5d5dcb67fa0336c3aad7dbbc91230d9755ad675 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 10:49:19 +0100 Subject: [PATCH 054/144] drivers/powerp*.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/powerp-bin.c | 2 +- drivers/powerp-txt.c | 2 +- drivers/powerpanel.c | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/powerp-bin.c b/drivers/powerp-bin.c index 3c6d74f00b..4c3076040e 100644 --- a/drivers/powerp-bin.c +++ b/drivers/powerp-bin.c @@ -34,7 +34,7 @@ #include "nut_stdint.h" #include "nut_float.h" -#define POWERPANEL_BIN_VERSION "Powerpanel-Binary 0.60" +#define POWERPANEL_BIN_VERSION "Powerpanel-Binary 0.61" typedef struct { unsigned char start; diff --git a/drivers/powerp-txt.c b/drivers/powerp-txt.c index d13e8899a0..a5d58135d4 100644 --- a/drivers/powerp-txt.c +++ b/drivers/powerp-txt.c @@ -36,7 +36,7 @@ #include -#define POWERPANEL_TEXT_VERSION "Powerpanel-Text 0.60" +#define POWERPANEL_TEXT_VERSION "Powerpanel-Text 0.61" typedef struct { float i_volt; diff --git a/drivers/powerpanel.c b/drivers/powerpanel.c index 5600ad4f7c..86fd8be2b5 100644 --- a/drivers/powerpanel.c +++ b/drivers/powerpanel.c @@ -104,6 +104,12 @@ void upsdrv_shutdown(void) { int i, ret = -1; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + /* * Try to shutdown with delay and automatic reboot if the power * returned in the mean time (newer models support this). From 705e4f74d2d4565af3c1e1e2ca48688122371f40 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 10:56:02 +0100 Subject: [PATCH 055/144] drivers/rhino.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/rhino.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/rhino.c b/drivers/rhino.c index f312fab798..142a1f8e51 100644 --- a/drivers/rhino.c +++ b/drivers/rhino.c @@ -38,7 +38,7 @@ #include "timehead.h" #define DRIVER_NAME "Microsol Rhino UPS driver" -#define DRIVER_VERSION "0.54" +#define DRIVER_VERSION "0.55" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -749,13 +749,23 @@ void upsdrv_updateinfo(void) /* power down the attached load immediately */ void upsdrv_shutdown(void) { + /* FIXME: Make a name for default original shutdown; + * note that currently it just does "shutdown.stayoff" + * in both cases, just with different logged messages. + */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + /* basic idea: find out line status and send appropriate command */ /* on line: send normal shutdown, ups will return by itself on utility */ /* on battery: send shutdown+return, ups will cycle and return soon */ if (!SourceFail) /* on line */ { - /* FIXME: Both legs of the if-clause send CMD_SHUT, where is the "forcing"? */ + /* FIXME: Both legs of the if-clause send CMD_SHUT, + * where is the "forcing"? */ printf("On line, forcing shutdown command...\n"); /* send_command( CMD_SHUT ); */ sendshut(); From 2c144c6fa407e0220feeebc7999f1219e81d98aa Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 11:09:09 +0100 Subject: [PATCH 056/144] drivers/richcomm_usb.c: update to use "sdcommands" and move logic from upsdrv_shutdown() to upscmd("shutdown.return"); newly support INSTCMD at all [#2670] Signed-off-by: Jim Klimov --- drivers/richcomm_usb.c | 113 ++++++++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 35 deletions(-) diff --git a/drivers/richcomm_usb.c b/drivers/richcomm_usb.c index 766d3bba60..1328a8c1c4 100644 --- a/drivers/richcomm_usb.c +++ b/drivers/richcomm_usb.c @@ -30,7 +30,7 @@ /* driver version */ #define DRIVER_NAME "Richcomm dry-contact to USB driver" -#define DRIVER_VERSION "0.13" +#define DRIVER_VERSION "0.14" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -66,6 +66,9 @@ static usb_dev_handle *udev = NULL; static USBDevice_t usbdevice; static unsigned int comm_failures = 0; +/* Forward decls */ +static int instcmd(const char *cmdname, const char *extra); + static int device_match_func(USBDevice_t *device, void *privdata) { NUT_UNUSED_VARIABLE(privdata); @@ -636,6 +639,15 @@ void upsdrv_initinfo(void) dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID); dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID); + + /* commands ----------------------------------------------- */ + /* FIXME: Check with the device what our instcmd + * (nee upsdrv_shutdown() contents) actually does! + */ + dstate_addcmd("shutdown.return"); + + /* install handlers */ + upsh.instcmd = instcmd; } void upsdrv_updateinfo(void) @@ -696,46 +708,77 @@ void upsdrv_updateinfo(void) status_commit(); } -/* - * The shutdown feature is a bit strange on this UPS IMHO, it - * switches the polarity of the 'Shutdown UPS' signal, at which - * point it will automatically power down once it loses power. - * - * It will still, however, be possible to poll the UPS and - * reverse the polarity _again_, at which point it will - * start back up once power comes back. - * - * Maybe this is the normal way, it just seems a bit strange. - * - * Please note, this function doesn't power the UPS off if - * line power is connected. - */ -void upsdrv_shutdown(void) +/* handler for commands to be sent to UPS */ +static +int instcmd(const char *cmdname, const char *extra) { - /* - * This packet shuts down the UPS, that is, - * if it is not currently on line power - */ - char prepare[QUERY_PACKETSIZE] = { 0x02, 0x00, 0x00, 0x00 }; + NUT_UNUSED_VARIABLE(extra); - /* - * This should make the UPS turn itself back on once the - * power comes back on; which is probably what we want - */ - char restart[QUERY_PACKETSIZE] = { 0x02, 0x01, 0x00, 0x00 }; - char reply[REPLY_PACKETSIZE]; + /* Shutdown UPS */ + if (!strcasecmp(cmdname, "shutdown.return")) + { + /* FIXME: Which one is this - "load.off", + * "shutdown.stayoff" or "shutdown.return"? + * Per legacy comments below it seems to + * best fit "load.off", and then we would + * want a "load.on" as well (is it different + * given the talk of polarity inversion?), + * except that "load.*" are to be immediate + * and here it depends on line power state... + */ + + /* + * The shutdown feature is a bit strange on this UPS IMHO, it + * switches the polarity of the 'Shutdown UPS' signal, at which + * point it will automatically power down once it loses power. + * + * It will still, however, be possible to poll the UPS and + * reverse the polarity _again_, at which point it will + * start back up once power comes back. + * + * Maybe this is the normal way, it just seems a bit strange. + * + * Please note, this function doesn't power the UPS off if + * line power is connected. + */ + + /* + * This packet shuts down the UPS, that is, + * if it is not currently on line power + */ + char prepare[QUERY_PACKETSIZE] = { 0x02, 0x00, 0x00, 0x00 }; + + /* + * This should make the UPS turn itself back on once the + * power comes back on; which is probably what we want + */ + char restart[QUERY_PACKETSIZE] = { 0x02, 0x01, 0x00, 0x00 }; + char reply[REPLY_PACKETSIZE]; + + execute_and_retrieve_query(prepare, reply); + + /* + * have to wait a bit, the previous command seems + * to be ignored if the second command comes right + * behind it + */ + sleep(1); - execute_and_retrieve_query(prepare, reply); + execute_and_retrieve_query(restart, reply); - /* - * have to, the previous command seems to be - * ignored if the second command comes right - * behind it - */ - sleep(1); + return STAT_INSTCMD_HANDLED; + } + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} - execute_and_retrieve_query(restart, reply); +void upsdrv_shutdown(void) +{ + /* FIXME: Check with the device what our instcmd + * (nee upsdrv_shutdown() contents) actually does! + */ + loop_shutdown_commands("shutdown.return", NULL); } void upsdrv_help(void) From 4c5d3c364be70335fe687d7ad386d62e311243ef Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 11:22:36 +0100 Subject: [PATCH 057/144] drivers/riello_ser.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/riello_ser.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/riello_ser.c b/drivers/riello_ser.c index f15c785ef5..b344cb5739 100644 --- a/drivers/riello_ser.c +++ b/drivers/riello_ser.c @@ -48,7 +48,7 @@ #include "riello.h" #define DRIVER_NAME "Riello serial driver" -#define DRIVER_VERSION "0.11" +#define DRIVER_VERSION "0.12" #define DEFAULT_OFFDELAY 5 /*!< seconds (max 0xFF) */ #define DEFAULT_BOOTDELAY 5 /*!< seconds (max 0xFF) */ @@ -1166,6 +1166,12 @@ void upsdrv_shutdown(void) /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ int retry; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ @@ -1181,7 +1187,8 @@ void upsdrv_shutdown(void) upsdebugx(2, "upsdrv Shutdown execute"); for (retry = 1; retry <= MAXTRIES; retry++) { - + /* By default, abort a previously requested shutdown + * (if any) and schedule a new one from this moment. */ if (riello_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { continue; } @@ -1191,11 +1198,13 @@ void upsdrv_shutdown(void) } upslogx(LOG_ERR, "Shutting down"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-2); /* EXIT_SUCCESS */ return; } upslogx(LOG_ERR, "Shutdown failed!"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); } From 964d7403b5a39e0f2ee60871376c5b582ba8eda5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 11:23:58 +0100 Subject: [PATCH 058/144] drivers/riello_usb.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/riello_usb.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c index cde20fbae9..d45f6785f8 100644 --- a/drivers/riello_usb.c +++ b/drivers/riello_usb.c @@ -36,7 +36,7 @@ #include "riello.h" #define DRIVER_NAME "Riello USB driver" -#define DRIVER_VERSION "0.13" +#define DRIVER_VERSION "0.14" #define DEFAULT_OFFDELAY 5 /*!< seconds (max 0xFF) */ #define DEFAULT_BOOTDELAY 5 /*!< seconds (max 0xFF) */ @@ -1137,6 +1137,12 @@ void upsdrv_shutdown(void) /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ int retry; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ @@ -1151,7 +1157,8 @@ void upsdrv_shutdown(void) /* OB: the load must remain off until the power returns */ for (retry = 1; retry <= MAXTRIES; retry++) { - + /* By default, abort a previously requested shutdown + * (if any) and schedule a new one from this moment. */ if (riello_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { continue; } @@ -1161,11 +1168,13 @@ void upsdrv_shutdown(void) } upslogx(LOG_ERR, "Shutting down"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-2); /* EXIT_SUCCESS */ return; } upslogx(LOG_ERR, "Shutdown failed!"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); } From 7a7aea1952d0898774d7c65ed2073f2858b55a20 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 11:28:10 +0100 Subject: [PATCH 059/144] drivers/safenet.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/safenet.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/safenet.c b/drivers/safenet.c index 351187419e..65baf4970a 100644 --- a/drivers/safenet.c +++ b/drivers/safenet.c @@ -41,7 +41,7 @@ #include "safenet.h" #define DRIVER_NAME "Generic SafeNet UPS driver" -#define DRIVER_VERSION "1.81" +#define DRIVER_VERSION "1.82" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -421,6 +421,12 @@ void upsdrv_shutdown(void) { int retry = 3; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + /* * Since we may have arrived here before the hardware is initialized, * try to initialize it here. @@ -435,6 +441,7 @@ void upsdrv_shutdown(void) } upslogx(LOG_ERR, "SafeNet protocol compatible UPS not found on %s", device_path); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); return; } From fc8cc459445bcfba477338885a24a756532af3e0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 11:34:28 +0100 Subject: [PATCH 060/144] drivers/skel.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/skel.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/skel.c b/drivers/skel.c index 9b45023832..ab65098522 100644 --- a/drivers/skel.c +++ b/drivers/skel.c @@ -22,7 +22,7 @@ /* #define IGNCHARS "" */ #define DRIVER_NAME "Skeleton UPS driver" -#define DRIVER_VERSION "0.05" +#define DRIVER_VERSION "0.06" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -33,6 +33,9 @@ upsdrv_info_t upsdrv_info = { { NULL } }; +/* Forward decls */ +/*static int instcmd(const char *cmdname, const char *extra);*/ + void upsdrv_initinfo(void) { /* try to detect the UPS here - call fatal_with_errno(EXIT_FAILURE, ...) @@ -44,6 +47,9 @@ void upsdrv_initinfo(void) /* dstate_setinfo("device.mfr", "skel manufacturer"); */ /* dstate_setinfo("device.model", "longrun 15000"); */ + /* commands ----------------------------------------------- */ + /*dstate_addcmd("shutdown.return");*/ + /*dstate_addcmd("test.battery.stop);*/ /* upsh.instcmd = instcmd; */ } @@ -102,8 +108,17 @@ void upsdrv_shutdown(void) it doesn't respond at first if possible */ /* replace with a proper shutdown function */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ @@ -121,7 +136,12 @@ static int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + if (!strcasecmp(cmdname, "shutdown.stayoff")) { + ser_send_buf(upsfd, ...); + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } */ From 7355b93bd29ac3aab0e584a7a38ad9861a34bb84 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 11:36:57 +0100 Subject: [PATCH 061/144] drivers/sms_ser.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/sms_ser.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/sms_ser.c b/drivers/sms_ser.c index 7b0f840896..4eb0e69bb1 100644 --- a/drivers/sms_ser.c +++ b/drivers/sms_ser.c @@ -31,7 +31,7 @@ #define ENDCHAR '\r' #define DRIVER_NAME "SMS Brazil UPS driver" -#define DRIVER_VERSION "1.01" +#define DRIVER_VERSION "1.02" #define QUERY_SIZE 7 #define BUFFER_SIZE 18 @@ -517,6 +517,12 @@ void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ int retry; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + /* maybe try to detect the UPS here, but try a shutdown even if * it doesn't respond at first if possible */ @@ -531,6 +537,8 @@ void upsdrv_shutdown(void) { upsdebugx(2, "upsdrv Shutdown execute"); for (retry = 1; retry <= MAXTRIES; retry++) { + /* By default, abort a previously requested shutdown + * (if any) and schedule a new one from this moment. */ if (sms_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { continue; } @@ -540,11 +548,13 @@ void upsdrv_shutdown(void) { } upslogx(LOG_ERR, "Shutting down"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-2); /* EXIT_SUCCESS */ return; } upslogx(LOG_ERR, "Shutdown failed!"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); } From a0fe95ca4be394485eca064cef0c4a24880edc53 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 11:46:56 +0100 Subject: [PATCH 062/144] drivers/*.c: for networked drivers (snmp, netxml, IPMI, modbus/TCP) comment in upsdrv_shutdown() some limitations of the media that networked UPS-shutdown may not be generally possible in an endgame [#2770] Signed-off-by: Jim Klimov --- drivers/adelsystem_cbi.c | 12 ++++++++++++ drivers/apc_modbus.c | 13 +++++++++++++ drivers/generic_modbus.c | 13 +++++++++++++ drivers/netxml-ups.c | 13 +++++++++++++ drivers/nut-ipmipsu.c | 13 +++++++++++++ drivers/phoenixcontact_modbus.c | 13 +++++++++++++ drivers/powerman-pdu.c | 13 +++++++++++++ drivers/snmp-ups.c | 18 +++++++++++------- 8 files changed, 101 insertions(+), 7 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index bc9e98427d..b152105d6d 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -470,6 +470,18 @@ void upsdrv_updateinfo(void) /* shutdown UPS */ void upsdrv_shutdown(void) { + /* + * WARNING: When using RTU TCP, this driver will probably + * never support shutdowns properly, except on some systems: + * In order to be of any use, the driver should be called + * near the end of the system halt script (or a service + * management framework's equivalent, if any). By that + * time we, in all likelyhood, won't have basic network + * capabilities anymore, so we could never send this + * command to the UPS. This is not an error, but rather + * a limitation (on some platforms) of the interface/media + * used for these devices. + */ handling_upsdrv_shutdown = 1; loop_shutdown_commands("shutdown.stayoff", NULL); } diff --git a/drivers/apc_modbus.c b/drivers/apc_modbus.c index 3651d82148..8265b1739a 100644 --- a/drivers/apc_modbus.c +++ b/drivers/apc_modbus.c @@ -1569,6 +1569,19 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* + * WARNING: When using RTU TCP, this driver will probably + * never support shutdowns properly, except on some systems: + * In order to be of any use, the driver should be called + * near the end of the system halt script (or a service + * management framework's equivalent, if any). By that + * time we, in all likelyhood, won't have basic network + * capabilities anymore, so we could never send this + * command to the UPS. This is not an error, but rather + * a limitation (on some platforms) of the interface/media + * used for these devices. + */ + /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { loop_shutdown_commands(NULL, NULL); diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 384aaaf897..779b7b4e54 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -322,6 +322,19 @@ void upsdrv_updateinfo(void) /* shutdown UPS */ void upsdrv_shutdown(void) { + /* + * WARNING: When using RTU TCP, this driver will probably + * never support shutdowns properly, except on some systems: + * In order to be of any use, the driver should be called + * near the end of the system halt script (or a service + * management framework's equivalent, if any). By that + * time we, in all likelyhood, won't have basic network + * capabilities anymore, so we could never send this + * command to the UPS. This is not an error, but rather + * a limitation (on some platforms) of the interface/media + * used for these devices. + */ + handling_upsdrv_shutdown = 1; loop_shutdown_commands("shutdown.stayoff", NULL); } diff --git a/drivers/netxml-ups.c b/drivers/netxml-ups.c index f3a21c05cb..40cd6806fe 100644 --- a/drivers/netxml-ups.c +++ b/drivers/netxml-ups.c @@ -394,6 +394,19 @@ void upsdrv_updateinfo(void) } void upsdrv_shutdown(void) { + /* + * WARNING: + * This driver will probably never support this properly: + * In order to be of any use, the driver should be called + * near the end of the system halt script (or a service + * management framework's equivalent, if any). By that + * time we, in all likelyhood, won't have basic network + * capabilities anymore, so we could never send this + * command to the UPS. This is not an error, but rather + * a limitation (on some platforms) of the interface/media + * used for these devices. + */ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ /* maybe try to detect the UPS here, but try a shutdown even if diff --git a/drivers/nut-ipmipsu.c b/drivers/nut-ipmipsu.c index 55cfcb5f8f..5accb26444 100644 --- a/drivers/nut-ipmipsu.c +++ b/drivers/nut-ipmipsu.c @@ -145,6 +145,19 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* + * WARNING: + * This driver will probably never support this properly: + * In order to be of any use, the driver should be called + * near the end of the system halt script (or a service + * management framework's equivalent, if any). By that + * time we, in all likelyhood, won't have basic network + * capabilities anymore, so we could never send this + * command to the UPS. This is not an error, but rather + * a limitation (on some platforms) of the interface/media + * used for these devices. + */ + /* replace with a proper shutdown function */ /* NOTE: User-provided commands may be something other diff --git a/drivers/phoenixcontact_modbus.c b/drivers/phoenixcontact_modbus.c index e27faf77ad..8b4a6c2404 100644 --- a/drivers/phoenixcontact_modbus.c +++ b/drivers/phoenixcontact_modbus.c @@ -133,6 +133,19 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* + * WARNING: When using RTU TCP, this driver will probably + * never support shutdowns properly, except on some systems: + * In order to be of any use, the driver should be called + * near the end of the system halt script (or a service + * management framework's equivalent, if any). By that + * time we, in all likelyhood, won't have basic network + * capabilities anymore, so we could never send this + * command to the UPS. This is not an error, but rather + * a limitation (on some platforms) of the interface/media + * used for these devices. + */ + /* replace with a proper shutdown function */ /* NOTE: User-provided commands may be something other diff --git a/drivers/powerman-pdu.c b/drivers/powerman-pdu.c index ccea743111..25624f3f4c 100644 --- a/drivers/powerman-pdu.c +++ b/drivers/powerman-pdu.c @@ -135,6 +135,19 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { + /* + * WARNING: + * This driver will probably never support this properly: + * In order to be of any use, the driver should be called + * near the end of the system halt script (or a service + * management framework's equivalent, if any). By that + * time we, in all likelyhood, won't have basic network + * capabilities anymore, so we could never send this + * command to the UPS. This is not an error, but rather + * a limitation (on some platforms) of the interface/media + * used for these devices. + */ + /* replace with a proper shutdown function */ /* NOTE: User-provided commands may be something other diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index d6e2f6a89b..9c8e317eb0 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -342,13 +342,17 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { /* - This driver will probably never support this. In order to - be any use, the driver should be called near the end of - the system halt script. By that time we in all likelyhood - we won't have network capabilities anymore, so we could - never send this command to the UPS. This is not an error, - but a limitation of the interface used. - */ + * WARNING: + * This driver will probably never support this properly: + * In order to be of any use, the driver should be called + * near the end of the system halt script (or a service + * management framework's equivalent, if any). By that + * time we, in all likelyhood, won't have basic network + * capabilities anymore, so we could never send this + * command to the UPS. This is not an error, but rather + * a limitation (on some platforms) of the interface/media + * used for these devices. + */ upsdebugx(1, "%s...", __func__); From 55bf0c01f1735a69ca7133cc03e78a96e1509065 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 11:53:59 +0100 Subject: [PATCH 063/144] drivers/snmp-ups.c: update to use "sdcommands" (if customized by user), by default chain the originally used sequence in upsdrv_shutdown() [#2670] Signed-off-by: Jim Klimov --- drivers/snmp-ups.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 9c8e317eb0..67a9069c61 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -174,7 +174,7 @@ static const char *mibname; static const char *mibvers; #define DRIVER_NAME "Generic SNMP UPS driver" -#define DRIVER_VERSION "1.31" +#define DRIVER_VERSION "1.32" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -353,31 +353,26 @@ void upsdrv_shutdown(void) * a limitation (on some platforms) of the interface/media * used for these devices. */ + char *cmd_used = NULL; upsdebugx(1, "%s...", __func__); /* set shutdown and autostart delay */ set_delays(); - /* Try to shutdown with delay */ - if (su_instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) { - /* Shutdown successful */ - return; - } - - /* If the above doesn't work, try shutdown.reboot */ - if (su_instcmd("shutdown.reboot", NULL) == STAT_INSTCMD_HANDLED) { - /* Shutdown successful */ - return; - } - - /* If the above doesn't work, try load.off.delay */ - if (su_instcmd("load.off.delay", NULL) == STAT_INSTCMD_HANDLED) { - /* Shutdown successful */ + /* By default: + * - Try to shutdown with delay + * - If the above doesn't work, try shutdown.reboot + * - If the above doesn't work, try load.off.delay + * - Finally, try shutdown.stayoff + */ + if (loop_shutdown_commands("shutdown.return,shutdown.reboot,load.off.delay,shutdown.stayoff", &cmd_used) == STAT_INSTCMD_HANDLED) { + upslogx(LOG_INFO, "Shutdown successful with '%s'", NUT_STRARG(cmd_used)); return; } upslogx(LOG_ERR, "Shutdown failed!"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); } From 23fd46f7249a2eb6b418ef3892b5bba360ef4d97 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 11:56:38 +0100 Subject: [PATCH 064/144] drivers/socomec_jbus.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/socomec_jbus.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/socomec_jbus.c b/drivers/socomec_jbus.c index 76534512be..0770ddde3c 100644 --- a/drivers/socomec_jbus.c +++ b/drivers/socomec_jbus.c @@ -31,7 +31,7 @@ #include #define DRIVER_NAME "Socomec jbus driver" -#define DRIVER_VERSION "0.08" +#define DRIVER_VERSION "0.09" #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) #define MODBUS_SLAVE_ID 1 @@ -429,8 +429,17 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { /* replace with a proper shutdown function */ - upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ + if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "shutdown not supported"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(-1); + } } void upsdrv_help(void) From 349d4212645ab687c9ddc2736c12605040c03086 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 12:01:57 +0100 Subject: [PATCH 065/144] drivers/solis.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/solis.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/solis.c b/drivers/solis.c index 11120580bb..27ee4a1e75 100644 --- a/drivers/solis.c +++ b/drivers/solis.c @@ -48,7 +48,7 @@ #include "timehead.h" #define DRIVER_NAME "Microsol Solis UPS driver" -#define DRIVER_VERSION "0.70" +#define DRIVER_VERSION "0.71" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -897,6 +897,8 @@ static void get_update_info(void) { static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "shutdown.return")) { /* shutdown and restart */ + /* FIXME: check with HW if this is not + * a "shutdown.reboot" instead (or also)? */ ser_send_char(upsfd, CMD_SHUTRET); /* 0xDE */ /* ser_send_char(upsfd, ENDCHAR); */ return STAT_INSTCMD_HANDLED; @@ -960,12 +962,20 @@ void upsdrv_updateinfo(void) { * - on line: send shutdown+return, UPS will cycle and return soon. */ void upsdrv_shutdown(void) { + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + if (!SourceFail) { /* on line */ upslogx(LOG_NOTICE, "On line, sending shutdown+return command...\n"); ser_send_char(upsfd, CMD_SHUTRET ); + /* Seems AKA: instcmd("shutdown.return", NULL); */ } else { upslogx(LOG_NOTICE, "On battery, sending normal shutdown command...\n"); ser_send_char(upsfd, CMD_SHUT); + /* Seems AKA: instcmd("shutdown.stayoff", NULL); */ } } From ac930003cac0664bc1755a770e384235be707c12 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 12:03:13 +0100 Subject: [PATCH 066/144] drivers/tripplite.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/tripplite.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tripplite.c b/drivers/tripplite.c index cfb4d2230f..d0a571a239 100644 --- a/drivers/tripplite.c +++ b/drivers/tripplite.c @@ -117,7 +117,7 @@ #include #define DRIVER_NAME "Tripp-Lite SmartUPS driver" -#define DRIVER_VERSION "0.96" +#define DRIVER_VERSION "0.97" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -378,7 +378,7 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { - soft_shutdown(); + loop_shutdown_commands("shutdown.return", NULL); } void upsdrv_updateinfo(void) From 57f7d071caedee316a46fd1e590274840fad8e51 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 12:09:33 +0100 Subject: [PATCH 067/144] drivers/tripplite_usb.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/tripplite_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index d441e783a4..a034bbfcd0 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -137,7 +137,7 @@ #include "usb-common.h" #define DRIVER_NAME "Tripp Lite OMNIVS / SMARTPRO driver" -#define DRIVER_VERSION "0.38" +#define DRIVER_VERSION "0.39" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -1261,7 +1261,7 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { - soft_shutdown(); + loop_shutdown_commands("shutdown.return", NULL); } void upsdrv_updateinfo(void) From db20fcc8571f45c1bf7fd8c4daeab3e0fbbcba4a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 12:10:28 +0100 Subject: [PATCH 068/144] drivers/tripplitesu.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/tripplitesu.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/tripplitesu.c b/drivers/tripplitesu.c index fb6f42076a..da778221c3 100644 --- a/drivers/tripplitesu.c +++ b/drivers/tripplitesu.c @@ -126,7 +126,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Tripp Lite SmartOnline driver" -#define DRIVER_VERSION "0.08" +#define DRIVER_VERSION "0.09" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -848,6 +848,12 @@ void upsdrv_shutdown(void) { char parm[20]; + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + if (!init_comm()) printf("Status failed. Assuming it's on battery and trying a shutdown anyway.\n"); auto_reboot(1); From 1908ca64568e6c2532b8c5b51c838c906f696928 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 12:11:23 +0100 Subject: [PATCH 069/144] drivers/upscode2.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/upscode2.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/upscode2.c b/drivers/upscode2.c index ee88ba74f8..1bd446c286 100644 --- a/drivers/upscode2.c +++ b/drivers/upscode2.c @@ -43,7 +43,7 @@ #include "nut_float.h" #define DRIVER_NAME "UPScode II UPS driver" -#define DRIVER_VERSION "0.92" +#define DRIVER_VERSION "0.93" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -876,6 +876,12 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + upslogx(LOG_EMERG, "Shutting down..."); /* send shutdown command twice, just to be sure */ From a0fcfac363eebc6f394e8d6d78544005f062501d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 12:13:18 +0100 Subject: [PATCH 070/144] drivers/victronups.c: update to use "sdcommands" (if customized by user) [#2670] Signed-off-by: Jim Klimov --- drivers/victronups.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/victronups.c b/drivers/victronups.c index ad11b4b3cb..adfe93a978 100644 --- a/drivers/victronups.c +++ b/drivers/victronups.c @@ -32,7 +32,7 @@ #include "serial.h" #define DRIVER_NAME "GE/IMV/Victron UPS driver" -#define DRIVER_VERSION "0.23" +#define DRIVER_VERSION "0.24" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -498,6 +498,12 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* FIXME: Make a name for default original shutdown */ + if (device_sdcommands) { + loop_shutdown_commands(NULL, NULL); + return; + } + ser_send(upsfd, "vCc0!%c", ENDCHAR); usleep(UPS_DELAY); From 3fb5481f6fe587d3bc70241ef26e614f51abcf79 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 12:16:30 +0100 Subject: [PATCH 071/144] drivers/usbhid-ups.c: update to use "sdcommands" (if customized by user), by default chain the originally used sequence in upsdrv_shutdown() [#2670] Signed-off-by: Jim Klimov --- drivers/usbhid-ups.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index d8c13a5a89..f389fee0a8 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -29,7 +29,7 @@ */ #define DRIVER_NAME "Generic HID driver" -#define DRIVER_VERSION "0.58" +#define DRIVER_VERSION "0.59" #define HU_VAR_WAITBEFORERECONNECT "waitbeforereconnect" @@ -987,27 +987,23 @@ int setvar(const char *varname, const char *val) void upsdrv_shutdown(void) { - upsdebugx(1, "upsdrv_shutdown..."); + char *cmd_used = NULL; - /* Try to shutdown with delay */ - if (instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) { - /* Shutdown successful */ - return; - } - - /* If the above doesn't work, try shutdown.reboot */ - if (instcmd("shutdown.reboot", NULL) == STAT_INSTCMD_HANDLED) { - /* Shutdown successful */ - return; - } + upsdebugx(1, "%s...", __func__); - /* If the above doesn't work, try load.off.delay */ - if (instcmd("load.off.delay", NULL) == STAT_INSTCMD_HANDLED) { - /* Shutdown successful */ + /* By default: + * - Try to shutdown with delay + * - If the above doesn't work, try shutdown.reboot + * - If the above doesn't work, try load.off.delay + * - Finally, try shutdown.stayoff + */ + if (loop_shutdown_commands("shutdown.return,shutdown.reboot,load.off.delay,shutdown.stayoff", &cmd_used) == STAT_INSTCMD_HANDLED) { + upslogx(LOG_INFO, "Shutdown successful with '%s'", NUT_STRARG(cmd_used)); return; } upslogx(LOG_ERR, "Shutdown failed!"); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ set_exit_flag(-1); } From 3612f7a120f3651e0bfbb6f23e72296345623d43 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 12:29:51 +0100 Subject: [PATCH 072/144] include/common.h, docs/new-drivers.txt, NEWS.adoc: define common EF_EXIT_FAILURE and EF_EXIT_SUCCESS for set_exit_flag(int) methods [#2670] Signed-off-by: Jim Klimov --- NEWS.adoc | 2 ++ docs/new-drivers.txt | 7 ++++--- include/common.h | 9 +++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 3e67816990..0809fa3e3e 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -363,6 +363,8 @@ INSTCMD such as `shutdown.return`, `shutdown.stayoff` or `load.off`. [#2670] `gcc-13`+ whose static analyzers on NUT CI farm complained about some imperfections after adding newer OS revisions to the population of build agents. [#2585, #2588] + * defined `EF_EXIT_SUCCESS` and `EF_EXIT_FAILURE` in `include/common.h` + to avoid magic numbers in code like `set_exit_flag(-2)`. [#2670] - updated `docs/nut-names.txt` with items defined by 42ITy NUT fork. [#2339] diff --git a/docs/new-drivers.txt b/docs/new-drivers.txt index b425a32b56..ab9f175aa8 100644 --- a/docs/new-drivers.txt +++ b/docs/new-drivers.txt @@ -162,9 +162,10 @@ process. This method should not directly `exit()` the driver program (neither should it call `fatalx()` nor `fatal_with_errno()` methods). It can `upslogx(LOG_ERR, ...)` or `upslog_with_errno(LOG_ERR, ...)`, and then -`set_exit_flag(N)` if required (`-1` for `EXIT_FAILURE` and `-2` for -`EXIT_SUCCESS` which would be handled in the standard driver loop or -`forceshutdown()` method of `main.c`). +`set_exit_flag(N)` if required, using values `EF_EXIT_FAILURE` (`-1`) +for eventual `exit(EXIT_FAILURE)` and `EF_EXIT_SUCCESS` (`-2`) for +`exit(EXIT_SUCCESS)`, which would be handled in the standard driver +loop or in `forceshutdown()` method of `main.c`. Data types ---------- diff --git a/include/common.h b/include/common.h index bc171f7ce8..77b81934c3 100644 --- a/include/common.h +++ b/include/common.h @@ -560,6 +560,15 @@ extern int optind; # define setegid(x) setresgid(-1,x,-1) /* Works for HP-UX 10.20 */ #endif +/* Several NUT programs define their set_exit_flag(int) methods + * which accept a signal code or similar parameter. Commonly they + * also accept a few negative values summarized below, to just + * exit (typically after completing a processing loop) with one + * of C standard exit codes. + */ +#define EF_EXIT_FAILURE -1 /* eventually exit(EXIT_FAILURE); */ +#define EF_EXIT_SUCCESS -2 /* eventually exit(EXIT_SUCCESS); */ + #ifdef WIN32 /* FIXME : this might not be the optimal mapping between syslog and ReportEvent*/ #define LOG_ERR EVENTLOG_ERROR_TYPE From 33d3b212527f147840f09ec7f393ed99557ac440 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 12:45:01 +0100 Subject: [PATCH 073/144] drivers/*.c: use common EF_EXIT_FAILURE and EF_EXIT_SUCCESS arg values for set_exit_flag(int) methods [#2670] Signed-off-by: Jim Klimov --- drivers/al175.c | 2 +- drivers/apcupsd-ups.c | 2 +- drivers/asem.c | 2 +- drivers/bcmxcp.c | 2 +- drivers/bicker_ser.c | 4 ++-- drivers/blazer.c | 4 ++-- drivers/clone-outlet.c | 2 +- drivers/clone.c | 2 +- drivers/dummy-ups.c | 2 +- drivers/generic_gpio_common.c | 2 +- drivers/generic_modbus.c | 4 ++-- drivers/genericups.c | 10 +++++----- drivers/huawei-ups2000.c | 2 +- drivers/hwmon_ina219.c | 2 +- drivers/isbmex.c | 2 +- drivers/ivtscd.c | 3 ++- drivers/liebert.c | 2 +- drivers/macosx-ups.c | 2 +- drivers/main.c | 6 +++--- drivers/netxml-ups.c | 2 +- drivers/nut-ipmipsu.c | 2 +- drivers/nutdrv_qx.c | 18 ++++++++++++------ drivers/phoenixcontact_modbus.c | 2 +- drivers/powercom.c | 4 ++-- drivers/powerman-pdu.c | 2 +- drivers/riello_ser.c | 4 ++-- drivers/riello_usb.c | 4 ++-- drivers/safenet.c | 2 +- drivers/skel.c | 2 +- drivers/sms_ser.c | 4 ++-- drivers/snmp-ups.c | 2 +- drivers/socomec_jbus.c | 2 +- drivers/usbhid-ups.c | 2 +- 33 files changed, 58 insertions(+), 51 deletions(-) diff --git a/drivers/al175.c b/drivers/al175.c index 8ad4e548ea..f4dcbc6da1 100644 --- a/drivers/al175.c +++ b/drivers/al175.c @@ -1284,7 +1284,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } /* you may have to check the line status since the commands diff --git a/drivers/apcupsd-ups.c b/drivers/apcupsd-ups.c index 26fba8294f..99971ccfb7 100644 --- a/drivers/apcupsd-ups.c +++ b/drivers/apcupsd-ups.c @@ -373,7 +373,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/asem.c b/drivers/asem.c index 9f28a73c53..e3bd3a30bc 100644 --- a/drivers/asem.c +++ b/drivers/asem.c @@ -341,7 +341,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } /* you may have to check the line status since the commands diff --git a/drivers/bcmxcp.c b/drivers/bcmxcp.c index c26eccc081..dd4038ab96 100644 --- a/drivers/bcmxcp.c +++ b/drivers/bcmxcp.c @@ -1959,7 +1959,7 @@ void upsdrv_shutdown(void) upslogx(LOG_ERR, "Shutdown failed!"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } diff --git a/drivers/bicker_ser.c b/drivers/bicker_ser.c index 4093ffc3d4..2107cceaf3 100644 --- a/drivers/bicker_ser.c +++ b/drivers/bicker_ser.c @@ -871,14 +871,14 @@ void upsdrv_shutdown(void) for (retry = 1; retry <= BICKER_RETRIES; retry++) { if (bicker_shutdown() > 0) { /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-2); /* EXIT_SUCCESS */ + set_exit_flag(EF_EXIT_SUCCESS); return; } } upslogx(LOG_ERR, "Shutdown failed!"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/blazer.c b/drivers/blazer.c index 744cb72d27..7dd882c73b 100644 --- a/drivers/blazer.c +++ b/drivers/blazer.c @@ -883,11 +883,11 @@ void upsdrv_shutdown(void) upslogx(LOG_ERR, "Shutting down in %ld seconds", offdelay); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-2); /* EXIT_SUCCESS */ + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } diff --git a/drivers/clone-outlet.c b/drivers/clone-outlet.c index f8d9ec2af5..b7a301d87e 100644 --- a/drivers/clone-outlet.c +++ b/drivers/clone-outlet.c @@ -523,7 +523,7 @@ void upsdrv_shutdown(void) /* FIXME: Should the UPS shutdown mean the driver shutdown? */ /* upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); */ } } diff --git a/drivers/clone.c b/drivers/clone.c index 4a1b2ac753..dbd4730fd4 100644 --- a/drivers/clone.c +++ b/drivers/clone.c @@ -670,7 +670,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index 0d66ef3137..349f330558 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -392,7 +392,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/generic_gpio_common.c b/drivers/generic_gpio_common.c index 891f7d8593..e74930ca7f 100644 --- a/drivers/generic_gpio_common.c +++ b/drivers/generic_gpio_common.c @@ -481,7 +481,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 779b7b4e54..18c65db2a8 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -642,12 +642,12 @@ int upscmd(const char *cmd, const char *arg) case STAT_INSTCMD_INVALID: upslogx(LOG_ERR, "shutdown failed"); if (handling_upsdrv_shutdown) - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); return rval; case STAT_INSTCMD_UNKNOWN: upslogx(LOG_ERR, "shutdown not supported"); if (handling_upsdrv_shutdown) - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); return rval; default: upslogx(LOG_INFO, "shutdown command executed"); diff --git a/drivers/genericups.c b/drivers/genericups.c index 870d4baef1..868f8f10e6 100644 --- a/drivers/genericups.c +++ b/drivers/genericups.c @@ -321,7 +321,7 @@ void upsdrv_shutdown(void) if (upstype == -1) { upslogx(LOG_ERR, "No upstype set - see help text / man page!"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); return; } @@ -330,7 +330,7 @@ void upsdrv_shutdown(void) if (flags == -1) { upslogx(LOG_ERR, "No shutdown command defined for this model!"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); return; } @@ -339,7 +339,7 @@ void upsdrv_shutdown(void) #ifndef WIN32 #ifndef HAVE_TCSENDBREAK upslogx(LOG_ERR, "Need to send a BREAK, but don't have tcsendbreak!"); - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); return; #endif #endif @@ -349,7 +349,7 @@ void upsdrv_shutdown(void) if (ret != 0) { upslog_with_errno(LOG_ERR, "tcsendbreak"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } return; @@ -364,7 +364,7 @@ void upsdrv_shutdown(void) if (ret != 0) { upslog_with_errno(LOG_ERR, "ioctl TIOCMSET"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); return; } diff --git a/drivers/huawei-ups2000.c b/drivers/huawei-ups2000.c index cf1d0d61b4..9f7bacc201 100644 --- a/drivers/huawei-ups2000.c +++ b/drivers/huawei-ups2000.c @@ -1807,7 +1807,7 @@ void upsdrv_shutdown(void) if (r != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "upsdrv_shutdown failed!"); if (!device_sdcommands) - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/hwmon_ina219.c b/drivers/hwmon_ina219.c index 3e24f2d6a8..9801eb3fc5 100644 --- a/drivers/hwmon_ina219.c +++ b/drivers/hwmon_ina219.c @@ -473,7 +473,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/isbmex.c b/drivers/isbmex.c index f46c86d781..ea8a276575 100644 --- a/drivers/isbmex.c +++ b/drivers/isbmex.c @@ -382,7 +382,7 @@ int instcmd(const char *cmdname, const char *extra) /* upslogx(LOG_ERR, "Shutdown only supported with the Generic Driver, type 6 and special cable"); //upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); */ int i; for(i=0;i<=5;i++) diff --git a/drivers/ivtscd.c b/drivers/ivtscd.c index e724fa2319..07b32d5ad0 100644 --- a/drivers/ivtscd.c +++ b/drivers/ivtscd.c @@ -200,7 +200,8 @@ void upsdrv_shutdown(void) /* Hmmm, why was this an exit-case before? fatalx(EXIT_SUCCESS...) */ upslogx(LOG_ERR, "Power is back!"); - set_exit_flag(-2); /* EXIT_SUCCESS */ + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(EF_EXIT_SUCCESS); return; } } diff --git a/drivers/liebert.c b/drivers/liebert.c index 372bb34fb4..9d5755c0f2 100644 --- a/drivers/liebert.c +++ b/drivers/liebert.c @@ -55,7 +55,7 @@ void upsdrv_shutdown(void) upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_initinfo(void) diff --git a/drivers/macosx-ups.c b/drivers/macosx-ups.c index 66e3181ea7..37c7a45bd6 100644 --- a/drivers/macosx-ups.c +++ b/drivers/macosx-ups.c @@ -281,7 +281,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } /* you may have to check the line status since the commands diff --git a/drivers/main.c b/drivers/main.c index fdaa17e4f4..3a3caddee8 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -180,7 +180,7 @@ static void forceshutdown(void) upsdrv_shutdown(); /* the driver always exits here, to not block probable ongoing shutdown */ - exit(exit_flag == -1 ? EXIT_FAILURE : EXIT_SUCCESS); + exit(exit_flag == EF_EXIT_FAILURE ? EXIT_FAILURE : EXIT_SUCCESS); } /* this function only prints the usage message; it does not call exit() */ @@ -1715,7 +1715,7 @@ static void set_reload_flag( reload_flag = 15; break; */ - set_exit_flag(-2); + set_exit_flag(EF_EXIT_SUCCESS); return; case SIGCMD_RELOAD: /* SIGHUP */ @@ -1732,7 +1732,7 @@ static void set_reload_flag( /* reload what we can, log what needs a restart so skipped */ reload_flag = 1; } else if (sig && !strcmp(sig, SIGCMD_EXIT)) { - set_exit_flag(-2); + set_exit_flag(EF_EXIT_SUCCESS); return; } else { /* non-fatal reload as a fallback */ diff --git a/drivers/netxml-ups.c b/drivers/netxml-ups.c index 40cd6806fe..94fa5679b3 100644 --- a/drivers/netxml-ups.c +++ b/drivers/netxml-ups.c @@ -476,7 +476,7 @@ void upsdrv_shutdown(void) { if (STAT_SET_HANDLED != status) { upslogx(LOG_ERR, "Shutdown failed: %d", status); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/nut-ipmipsu.c b/drivers/nut-ipmipsu.c index 5accb26444..57e8263ae2 100644 --- a/drivers/nut-ipmipsu.c +++ b/drivers/nut-ipmipsu.c @@ -168,7 +168,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 1530ecf057..ee59ddcf6e 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -2792,7 +2792,8 @@ void upsdrv_shutdown(void) /* Don't know what happened */ if (!item) { upslogx(LOG_ERR, "Unable to set start delay"); - set_exit_flag(-1); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(EF_EXIT_FAILURE); return; } @@ -2807,7 +2808,8 @@ void upsdrv_shutdown(void) if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) { upslogx(LOG_ERR, "Start delay '%s' out of range", val); - set_exit_flag(-1); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(EF_EXIT_FAILURE); return; } @@ -2817,7 +2819,8 @@ void upsdrv_shutdown(void) /* Don't know what happened */ if (!item) { upslogx(LOG_ERR, "Unable to set shutdown delay"); - set_exit_flag(-1); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(EF_EXIT_FAILURE); return; } @@ -2832,7 +2835,8 @@ void upsdrv_shutdown(void) if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) { upslogx(LOG_ERR, "Shutdown delay '%s' out of range", val); - set_exit_flag(-1); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(EF_EXIT_FAILURE); return; } @@ -2874,12 +2878,14 @@ void upsdrv_shutdown(void) upslogx(LOG_ERR, "Shutting down in %s seconds", dstate_getinfo("ups.delay.shutdown")); - set_exit_flag(-2); /* EXIT_SUCCESS */ + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); - set_exit_flag(-1); + /* FIXME: Should the UPS shutdown mean the driver shutdown? */ + set_exit_flag(EF_EXIT_FAILURE); } #ifdef QX_USB diff --git a/drivers/phoenixcontact_modbus.c b/drivers/phoenixcontact_modbus.c index 8b4a6c2404..101c50396d 100644 --- a/drivers/phoenixcontact_modbus.c +++ b/drivers/phoenixcontact_modbus.c @@ -156,7 +156,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/powercom.c b/drivers/powercom.c index 0b072844e4..97d636806d 100644 --- a/drivers/powercom.c +++ b/drivers/powercom.c @@ -298,7 +298,7 @@ static void shutdown_halt(void) upslogx(LOG_INFO, "Shutdown (stayoff) initiated."); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-2); /* EXIT_SUCCESS */ + set_exit_flag(EF_EXIT_SUCCESS); } static void shutdown_ret(void) @@ -311,7 +311,7 @@ static void shutdown_ret(void) upslogx(LOG_INFO, "Shutdown (return) initiated."); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-2); /* EXIT_SUCCESS */ + set_exit_flag(EF_EXIT_SUCCESS); } /* registered instant commands */ diff --git a/drivers/powerman-pdu.c b/drivers/powerman-pdu.c index 25624f3f4c..d37798ee84 100644 --- a/drivers/powerman-pdu.c +++ b/drivers/powerman-pdu.c @@ -161,7 +161,7 @@ void upsdrv_shutdown(void) /* OB: the load must remain off until the power returns */ upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/riello_ser.c b/drivers/riello_ser.c index b344cb5739..1ec4985de8 100644 --- a/drivers/riello_ser.c +++ b/drivers/riello_ser.c @@ -1199,13 +1199,13 @@ void upsdrv_shutdown(void) upslogx(LOG_ERR, "Shutting down"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-2); /* EXIT_SUCCESS */ + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c index d45f6785f8..576c2c062c 100644 --- a/drivers/riello_usb.c +++ b/drivers/riello_usb.c @@ -1169,13 +1169,13 @@ void upsdrv_shutdown(void) upslogx(LOG_ERR, "Shutting down"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-2); /* EXIT_SUCCESS */ + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_updateinfo(void) diff --git a/drivers/safenet.c b/drivers/safenet.c index 65baf4970a..3ac5ba6547 100644 --- a/drivers/safenet.c +++ b/drivers/safenet.c @@ -442,7 +442,7 @@ void upsdrv_shutdown(void) upslogx(LOG_ERR, "SafeNet protocol compatible UPS not found on %s", device_path); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); return; } diff --git a/drivers/skel.c b/drivers/skel.c index ab65098522..0ad09d8561 100644 --- a/drivers/skel.c +++ b/drivers/skel.c @@ -117,7 +117,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } /* you may have to check the line status since the commands diff --git a/drivers/sms_ser.c b/drivers/sms_ser.c index 4eb0e69bb1..a55d2d5d69 100644 --- a/drivers/sms_ser.c +++ b/drivers/sms_ser.c @@ -549,13 +549,13 @@ void upsdrv_shutdown(void) { upslogx(LOG_ERR, "Shutting down"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-2); /* EXIT_SUCCESS */ + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) { diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 67a9069c61..63d695d7aa 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -373,7 +373,7 @@ void upsdrv_shutdown(void) upslogx(LOG_ERR, "Shutdown failed!"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/socomec_jbus.c b/drivers/socomec_jbus.c index 0770ddde3c..3722692590 100644 --- a/drivers/socomec_jbus.c +++ b/drivers/socomec_jbus.c @@ -438,7 +438,7 @@ void upsdrv_shutdown(void) if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index f389fee0a8..822e88f0e2 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1004,7 +1004,7 @@ void upsdrv_shutdown(void) upslogx(LOG_ERR, "Shutdown failed!"); /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(-1); + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) From 2b7965136541cb95307d6186a2fc3d7a65fe9893 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 13:33:30 +0100 Subject: [PATCH 074/144] drivers/*.c, drivers/main.h: introduce and use a common handling_upsdrv_shutdown flag to exit or not after upsdrv_shutdown() and individual power-state related INSTCMDs [#2670] Signed-off-by: Jim Klimov --- NEWS.adoc | 7 +++++-- drivers/adelsystem_cbi.c | 17 ++++++++-------- drivers/al175.c | 4 ++-- drivers/apc_modbus.c | 4 +++- drivers/apcsmart-old.c | 4 +++- drivers/apcsmart.c | 6 ++++-- drivers/apcupsd-ups.c | 4 ++-- drivers/asem.c | 4 ++-- drivers/bcmxcp.c | 6 ++++-- drivers/belkin.c | 4 +++- drivers/belkinunv.c | 4 +++- drivers/bestfcom.c | 4 +++- drivers/bestfortress.c | 6 +++++- drivers/bestuferrups.c | 4 +++- drivers/bestups.c | 4 +++- drivers/bicker_ser.c | 14 +++++++------ drivers/blazer.c | 15 +++++++------- drivers/clone-outlet.c | 6 ++---- drivers/clone.c | 4 ++-- drivers/dummy-ups.c | 4 ++-- drivers/etapro.c | 4 +++- drivers/everups.c | 4 +++- drivers/gamatronic.c | 4 +++- drivers/generic_gpio_common.c | 4 ++-- drivers/generic_modbus.c | 13 ++++++------ drivers/genericups.c | 23 ++++++++++++---------- drivers/huawei-ups2000.c | 2 +- drivers/hwmon_ina219.c | 4 ++-- drivers/isbmex.c | 13 ++++++++---- drivers/ivtscd.c | 8 +++++--- drivers/liebert-esp2.c | 14 +++++++++---- drivers/liebert-gxe.c | 6 +++++- drivers/liebert.c | 8 +++++--- drivers/macosx-ups.c | 4 ++-- drivers/main.c | 24 +++++++++++++++++++++- drivers/main.h | 7 ++++--- drivers/masterguard.c | 4 +++- drivers/metasys.c | 6 ++++-- drivers/microdowell.c | 12 ++++++----- drivers/microsol-common.c | 4 +++- drivers/netxml-ups.c | 8 +++++--- drivers/nut-ipmipsu.c | 4 ++-- drivers/nutdrv_atcl_usb.c | 4 +++- drivers/nutdrv_qx.c | 35 ++++++++++++++------------------- drivers/nutdrv_siemens_sitop.c | 4 +++- drivers/oneac.c | 4 +++- drivers/optiups.c | 8 +++++--- drivers/phoenixcontact_modbus.c | 4 ++-- drivers/pijuice.c | 4 +++- drivers/powercom.c | 14 ++++++++----- drivers/powerman-pdu.c | 4 ++-- drivers/powerpanel.c | 5 +++-- drivers/rhino.c | 4 +++- drivers/richcomm_usb.c | 4 +++- drivers/riello_ser.c | 12 ++++++----- drivers/riello_usb.c | 12 ++++++----- drivers/safenet.c | 8 +++++--- drivers/skel.c | 4 ++-- drivers/sms_ser.c | 12 ++++++----- drivers/snmp-ups.c | 6 ++++-- drivers/socomec_jbus.c | 4 ++-- drivers/solis.c | 4 +++- drivers/tripplite.c | 4 +++- drivers/tripplite_usb.c | 4 +++- drivers/tripplitesu.c | 6 ++++-- drivers/upscode2.c | 4 +++- drivers/usbhid-ups.c | 6 ++++-- drivers/victronups.c | 4 +++- 68 files changed, 307 insertions(+), 182 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 0809fa3e3e..8a5aa05c32 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -322,6 +322,11 @@ during a NUT build. starting the beeper (where supported) to verify that the UPS communications happen as expected, without compromising the load connected to the UPS. + +Also defined `EF_EXIT_SUCCESS` and `EF_EXIT_FAILURE` in `include/common.h` +to avoid magic numbers in code like `set_exit_flag(-2)`, and revised whether +it is getting set at all in "killpower" vs. other cases, based on new +`handling_upsdrv_shutdown` internal flag. ++ NOTE: during this overhaul, many older drivers got their first ever supported INSTCMD such as `shutdown.return`, `shutdown.stayoff` or `load.off`. [#2670] @@ -363,8 +368,6 @@ INSTCMD such as `shutdown.return`, `shutdown.stayoff` or `load.off`. [#2670] `gcc-13`+ whose static analyzers on NUT CI farm complained about some imperfections after adding newer OS revisions to the population of build agents. [#2585, #2588] - * defined `EF_EXIT_SUCCESS` and `EF_EXIT_FAILURE` in `include/common.h` - to avoid magic numbers in code like `set_exit_flag(-2)`. [#2670] - updated `docs/nut-names.txt` with items defined by 42ITy NUT fork. [#2339] diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index b152105d6d..a7faa2efbb 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -49,8 +49,6 @@ static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ -static char handling_upsdrv_shutdown = 0; - /* initialize alarm structs */ void alrminit(void); @@ -482,8 +480,9 @@ void upsdrv_shutdown(void) * a limitation (on some platforms) of the interface/media * used for these devices. */ - handling_upsdrv_shutdown = 1; - loop_shutdown_commands("shutdown.stayoff", NULL); + int ret = loop_shutdown_commands("shutdown.stayoff", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } /* print driver usage info */ @@ -847,17 +846,19 @@ int upscmd(const char *cmd, const char *arg) switch (rval) { case STAT_INSTCMD_FAILED: case STAT_INSTCMD_INVALID: - if (handling_upsdrv_shutdown) - fatalx(EXIT_FAILURE, "shutdown failed"); upslog_with_errno(LOG_ERR, "instcmd: %s failed", cmd); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); break; case STAT_INSTCMD_UNKNOWN: - if (handling_upsdrv_shutdown) - fatalx(EXIT_FAILURE, "shutdown not supported"); upslog_with_errno(LOG_ERR, "instcmd: %s not supported", cmd); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); break; default: upslogx(LOG_INFO, "shutdown command executed"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); break; } } else { diff --git a/drivers/al175.c b/drivers/al175.c index f4dcbc6da1..7221786ca9 100644 --- a/drivers/al175.c +++ b/drivers/al175.c @@ -1283,8 +1283,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } /* you may have to check the line status since the commands diff --git a/drivers/apc_modbus.c b/drivers/apc_modbus.c index 8265b1739a..d3f5951d44 100644 --- a/drivers/apc_modbus.c +++ b/drivers/apc_modbus.c @@ -1584,7 +1584,9 @@ void upsdrv_shutdown(void) /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/apcsmart-old.c b/drivers/apcsmart-old.c index 3d78a0f2f7..16a5053392 100644 --- a/drivers/apcsmart-old.c +++ b/drivers/apcsmart-old.c @@ -1069,7 +1069,9 @@ void upsdrv_shutdown(void) /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int sdret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(sdret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index d12b85c402..28648d8d9a 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -1753,11 +1753,13 @@ static void upsdrv_shutdown_advanced(void) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - char temp[APC_LBUF]; + char temp[APC_LBUF]; /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/apcupsd-ups.c b/drivers/apcupsd-ups.c index 99971ccfb7..ddb02ef66c 100644 --- a/drivers/apcupsd-ups.c +++ b/drivers/apcupsd-ups.c @@ -372,8 +372,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/asem.c b/drivers/asem.c index e3bd3a30bc..bcb7557793 100644 --- a/drivers/asem.c +++ b/drivers/asem.c @@ -340,8 +340,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } /* you may have to check the line status since the commands diff --git a/drivers/bcmxcp.c b/drivers/bcmxcp.c index dd4038ab96..2697d4a672 100644 --- a/drivers/bcmxcp.c +++ b/drivers/bcmxcp.c @@ -1954,12 +1954,14 @@ void upsdrv_shutdown(void) * if the above doesn't work, try shutdown.stayoff */ if (loop_shutdown_commands("shutdown.return,shutdown.stayoff", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } diff --git a/drivers/belkin.c b/drivers/belkin.c index b4124211ef..2ff51c2b6e 100644 --- a/drivers/belkin.c +++ b/drivers/belkin.c @@ -352,7 +352,9 @@ void upsdrv_updateinfo(void) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - loop_shutdown_commands("shutdown.return", NULL); + int ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } /* handle "beeper.disable" */ diff --git a/drivers/belkinunv.c b/drivers/belkinunv.c index 11183d856c..03f85838c3 100644 --- a/drivers/belkinunv.c +++ b/drivers/belkinunv.c @@ -1185,7 +1185,9 @@ void upsdrv_shutdown(void) /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/bestfcom.c b/drivers/bestfcom.c index aea0820759..7b6e40b1af 100644 --- a/drivers/bestfcom.c +++ b/drivers/bestfcom.c @@ -491,7 +491,9 @@ int instcmd(const char *cmdname, const char *extra) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - loop_shutdown_commands("shutdown.return", NULL); + int ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } /* list flags and values that you want to receive via -x */ diff --git a/drivers/bestfortress.c b/drivers/bestfortress.c index d690d51d50..d453124219 100644 --- a/drivers/bestfortress.c +++ b/drivers/bestfortress.c @@ -514,9 +514,13 @@ static int upsdrv_setvar (const char *var, const char * data) { */ void upsdrv_shutdown(void) { + int ret = -1; + upsdebugx(2, "%s: begin", __func__); - loop_shutdown_commands("shutdown.return", NULL); + ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); upsdebugx(2, "%s: end", __func__); } diff --git a/drivers/bestuferrups.c b/drivers/bestuferrups.c index 41d45a6d8c..fb59f81c2d 100644 --- a/drivers/bestuferrups.c +++ b/drivers/bestuferrups.c @@ -390,7 +390,9 @@ int instcmd(const char *cmdname, const char *extra) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - loop_shutdown_commands("shutdown.return", NULL); + int ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } /* list flags and values that you want to receive via -x */ diff --git a/drivers/bestups.c b/drivers/bestups.c index 4689dfd4df..7486bab647 100644 --- a/drivers/bestups.c +++ b/drivers/bestups.c @@ -321,7 +321,9 @@ void upsdrv_shutdown(void) { /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/bicker_ser.c b/drivers/bicker_ser.c index 2107cceaf3..9029b21144 100644 --- a/drivers/bicker_ser.c +++ b/drivers/bicker_ser.c @@ -860,25 +860,27 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - int retry; + int retry; /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } for (retry = 1; retry <= BICKER_RETRIES; retry++) { if (bicker_shutdown() > 0) { - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_SUCCESS); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); return; } } upslogx(LOG_ERR, "Shutdown failed!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/blazer.c b/drivers/blazer.c index 7dd882c73b..dcb58370a5 100644 --- a/drivers/blazer.c +++ b/drivers/blazer.c @@ -855,19 +855,19 @@ void upsdrv_shutdown(void) /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } /* Stop pending shutdowns */ for (retry = 1; retry <= MAXTRIES; retry++) { - if (blazer_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { continue; } break; - } if (retry > MAXTRIES) { @@ -876,18 +876,17 @@ void upsdrv_shutdown(void) /* Shutdown */ for (retry = 1; retry <= MAXTRIES; retry++) { - if (blazer_instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) { continue; } upslogx(LOG_ERR, "Shutting down in %ld seconds", offdelay); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_SUCCESS); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } diff --git a/drivers/clone-outlet.c b/drivers/clone-outlet.c index b7a301d87e..9e5ae86e61 100644 --- a/drivers/clone-outlet.c +++ b/drivers/clone-outlet.c @@ -520,11 +520,9 @@ void upsdrv_shutdown(void) * impacting the load fed by the UPS. */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ -/* upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(EF_EXIT_FAILURE); - */ + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/clone.c b/drivers/clone.c index dbd4730fd4..53cf4eb175 100644 --- a/drivers/clone.c +++ b/drivers/clone.c @@ -669,8 +669,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index 349f330558..c973285006 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -391,8 +391,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/etapro.c b/drivers/etapro.c index 8393d59619..9967cec07b 100644 --- a/drivers/etapro.c +++ b/drivers/etapro.c @@ -343,7 +343,9 @@ upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - loop_shutdown_commands("shutdown.return", NULL); + int ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } void diff --git a/drivers/everups.c b/drivers/everups.c index 8e89c6944b..ebbf395dc5 100644 --- a/drivers/everups.c +++ b/drivers/everups.c @@ -207,7 +207,9 @@ int instcmd(const char *cmdname, const char *extra) void upsdrv_shutdown(void) { - loop_shutdown_commands("load.off", NULL); + int ret = loop_shutdown_commands("load.off", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/gamatronic.c b/drivers/gamatronic.c index e9ffe12902..6261590a9a 100644 --- a/drivers/gamatronic.c +++ b/drivers/gamatronic.c @@ -335,7 +335,9 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - loop_shutdown_commands("shutdown.return", NULL); + int ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } static diff --git a/drivers/generic_gpio_common.c b/drivers/generic_gpio_common.c index e74930ca7f..6097785bac 100644 --- a/drivers/generic_gpio_common.c +++ b/drivers/generic_gpio_common.c @@ -480,8 +480,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 18c65db2a8..4011d13ae0 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -47,8 +47,6 @@ static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus res static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ -static char handling_upsdrv_shutdown = 0; - /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); @@ -335,8 +333,9 @@ void upsdrv_shutdown(void) * used for these devices. */ - handling_upsdrv_shutdown = 1; - loop_shutdown_commands("shutdown.stayoff", NULL); + int ret = loop_shutdown_commands("shutdown.stayoff", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } /* print driver usage info */ @@ -641,16 +640,18 @@ int upscmd(const char *cmd, const char *arg) case STAT_INSTCMD_FAILED: case STAT_INSTCMD_INVALID: upslogx(LOG_ERR, "shutdown failed"); - if (handling_upsdrv_shutdown) + if (handling_upsdrv_shutdown > 0) set_exit_flag(EF_EXIT_FAILURE); return rval; case STAT_INSTCMD_UNKNOWN: upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown) + if (handling_upsdrv_shutdown > 0) set_exit_flag(EF_EXIT_FAILURE); return rval; default: upslogx(LOG_INFO, "shutdown command executed"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); break; } } else { diff --git a/drivers/genericups.c b/drivers/genericups.c index 868f8f10e6..3a5336be3d 100644 --- a/drivers/genericups.c +++ b/drivers/genericups.c @@ -314,14 +314,16 @@ void upsdrv_shutdown(void) /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } if (upstype == -1) { upslogx(LOG_ERR, "No upstype set - see help text / man page!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); return; } @@ -329,8 +331,8 @@ void upsdrv_shutdown(void) if (flags == -1) { upslogx(LOG_ERR, "No shutdown command defined for this model!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); return; } @@ -339,7 +341,8 @@ void upsdrv_shutdown(void) #ifndef WIN32 #ifndef HAVE_TCSENDBREAK upslogx(LOG_ERR, "Need to send a BREAK, but don't have tcsendbreak!"); - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); return; #endif #endif @@ -348,8 +351,8 @@ void upsdrv_shutdown(void) if (ret != 0) { upslog_with_errno(LOG_ERR, "tcsendbreak"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } return; @@ -363,8 +366,8 @@ void upsdrv_shutdown(void) if (ret != 0) { upslog_with_errno(LOG_ERR, "ioctl TIOCMSET"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); return; } diff --git a/drivers/huawei-ups2000.c b/drivers/huawei-ups2000.c index 9f7bacc201..bbb280bb44 100644 --- a/drivers/huawei-ups2000.c +++ b/drivers/huawei-ups2000.c @@ -1806,7 +1806,7 @@ void upsdrv_shutdown(void) r = loop_shutdown_commands("shutdown.reboot", NULL); if (r != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "upsdrv_shutdown failed!"); - if (!device_sdcommands) + if (handling_upsdrv_shutdown > 0) set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/hwmon_ina219.c b/drivers/hwmon_ina219.c index 9801eb3fc5..894e20b7d4 100644 --- a/drivers/hwmon_ina219.c +++ b/drivers/hwmon_ina219.c @@ -472,8 +472,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/isbmex.c b/drivers/isbmex.c index ea8a276575..c506826776 100644 --- a/drivers/isbmex.c +++ b/drivers/isbmex.c @@ -370,6 +370,8 @@ int instcmd(const char *cmdname, const char *extra) /* Shutdown UPS */ if (!strcasecmp(cmdname, "shutdown.stayoff")) { + int i; + /* shutdown is supported on models with * contact closure. Some ISB models with serial * support support contact closure, some don't. @@ -382,10 +384,11 @@ int instcmd(const char *cmdname, const char *extra) /* upslogx(LOG_ERR, "Shutdown only supported with the Generic Driver, type 6 and special cable"); //upslogx(LOG_ERR, "shutdown not supported"); - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); */ - int i; - for(i=0;i<=5;i++) + + for (i = 0; i <= 5; i++) { ser_send_char(upsfd, '#'); usleep(50000); @@ -403,7 +406,9 @@ void upsdrv_shutdown(void) /* FIXME: Check with the device what our instcmd * (nee upsdrv_shutdown() contents) actually does! */ - loop_shutdown_commands("shutdown.stayoff", NULL); + int ret = loop_shutdown_commands("shutdown.stayoff", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/ivtscd.c b/drivers/ivtscd.c index 07b32d5ad0..2bca607646 100644 --- a/drivers/ivtscd.c +++ b/drivers/ivtscd.c @@ -185,7 +185,9 @@ void upsdrv_shutdown(void) { /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } @@ -200,8 +202,8 @@ void upsdrv_shutdown(void) /* Hmmm, why was this an exit-case before? fatalx(EXIT_SUCCESS...) */ upslogx(LOG_ERR, "Power is back!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_SUCCESS); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); return; } } diff --git a/drivers/liebert-esp2.c b/drivers/liebert-esp2.c index ad441cf0bb..f76992e34c 100644 --- a/drivers/liebert-esp2.c +++ b/drivers/liebert-esp2.c @@ -553,19 +553,25 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - char reply[8]; + char reply[8]; /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } if(!(do_command(cmd_setOutOffMode, reply, 8) != -1) && (do_command(cmd_setOutOffDelay, reply, 8) != -1) && (do_command(cmd_sysLoadKey, reply, 6) != -1) && - (do_command(cmd_shutdown, reply, 8) != -1)) - upslogx(LOG_ERR, "Failed to shutdown UPS"); + (do_command(cmd_shutdown, reply, 8) != -1) + ) { + upslogx(LOG_ERR, "Failed to shutdown UPS"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); + } } static int instcmd(const char *cmdname, const char *extra) diff --git a/drivers/liebert-gxe.c b/drivers/liebert-gxe.c index 81d21eadf3..6e853a3ba2 100644 --- a/drivers/liebert-gxe.c +++ b/drivers/liebert-gxe.c @@ -513,12 +513,16 @@ void upsdrv_shutdown(void) { /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } /* FIXME: There seems to be instcmd(load.off), why not that? */ upslogx(LOG_INFO, "Liebert GXE UPS can't fully shutdown, NOOP"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_cleanup(void) diff --git a/drivers/liebert.c b/drivers/liebert.c index 9d5755c0f2..b96777199b 100644 --- a/drivers/liebert.c +++ b/drivers/liebert.c @@ -49,13 +49,15 @@ void upsdrv_shutdown(void) /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_initinfo(void) diff --git a/drivers/macosx-ups.c b/drivers/macosx-ups.c index 37c7a45bd6..5046f2cb6a 100644 --- a/drivers/macosx-ups.c +++ b/drivers/macosx-ups.c @@ -280,8 +280,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } /* you may have to check the line status since the commands diff --git a/drivers/main.c b/drivers/main.c index 3a3caddee8..781396acd8 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -51,6 +51,19 @@ TYPE_FD extrafd = ERROR_FD; static HANDLE mutex = INVALID_HANDLE_VALUE; #endif +/* Set by INSTCMD to killpower or by running `drivername -k` to + * help differentiate calls into upsdrv_shutdown() and further + * into instcmd() implementations that set UPS power state as + * needing the driver to set_exit_flag() or not. Values: + * -1 Do not exit even if killing power (e.g. when shutting + * down an UPS that we only monitor, but are not fed from + * and not shutting down THIS system). TODO: Ways to set. + * 0 Default do not exit (routinely handling an INSTCMD like + * "shutdown.return") + * 1 Exit (set when this driver is killing power) + */ +int handling_upsdrv_shutdown = 0; + /* for ser_open */ int do_lock_port = 1; @@ -176,10 +189,17 @@ static void forceshutdown(void) { upslogx(LOG_NOTICE, "Initiating UPS shutdown"); + /* NOTE: This is currently called exclusively as `drivername -k` + * CLI argument handling, so we exit afterwards and do not care + * about possible `handling_upsdrv_shutdown = -1` use-cases here. + */ + handling_upsdrv_shutdown = 1; + /* the driver must not block in this function */ upsdrv_shutdown(); - /* the driver always exits here, to not block probable ongoing shutdown */ + /* the driver always exits here, to + * not block probable ongoing shutdown */ exit(exit_flag == EF_EXIT_FAILURE ? EXIT_FAILURE : EXIT_SUCCESS); } @@ -836,6 +856,8 @@ int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { upslogx(LOG_WARNING, "Requesting UPS [%s] to power off, " "as/if handled by its driver by default (may exit), " "due to socket protocol request", NUT_STRARG(upsname)); + if (handling_upsdrv_shutdown == 0) + handling_upsdrv_shutdown = 1; upsdrv_shutdown(); return STAT_INSTCMD_HANDLED; } else { diff --git a/drivers/main.h b/drivers/main.h index 255ff93b40..fa6437cf61 100644 --- a/drivers/main.h +++ b/drivers/main.h @@ -10,12 +10,13 @@ #include "wincompat.h" #endif -/* public functions & variables from main.c */ +/* public functions & variables from main.c, documented in detail there */ extern const char *progname, *upsname, *device_name; extern char *device_path, *device_sdcommands; -extern int broken_driver, experimental_driver, do_lock_port, exit_flag; +extern int broken_driver, experimental_driver, + do_lock_port, exit_flag, handling_upsdrv_shutdown; extern TYPE_FD upsfd, extrafd; -extern time_t poll_interval; +extern time_t poll_interval; /* functions & variables required in each driver */ void upsdrv_initups(void); /* open connection to UPS, fail if not found */ diff --git a/drivers/masterguard.c b/drivers/masterguard.c index 5e15ffe49f..1fff1664e7 100644 --- a/drivers/masterguard.c +++ b/drivers/masterguard.c @@ -569,7 +569,9 @@ int instcmd(const char *cmdname, const char *extra) ********************************************************************/ void upsdrv_shutdown(void) { - loop_shutdown_commands("shutdown.return", NULL); + int ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } /******************************************************************** diff --git a/drivers/metasys.c b/drivers/metasys.c index cdb6e91d25..ec86b6e8d7 100644 --- a/drivers/metasys.c +++ b/drivers/metasys.c @@ -868,11 +868,13 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - unsigned char command[10], answer[10]; + unsigned char command[10], answer[10]; /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/microdowell.c b/drivers/microdowell.c index 2a2a94c6f3..ef4de017d1 100644 --- a/drivers/microdowell.c +++ b/drivers/microdowell.c @@ -944,14 +944,16 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { - unsigned char OutBuff[20] ; - unsigned char InpBuff[260] ; - unsigned char *p ; - unsigned char BatteryFlag=0 ; + unsigned char OutBuff[20]; + unsigned char InpBuff[260]; + unsigned char *p; + unsigned char BatteryFlag = 0; /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/microsol-common.c b/drivers/microsol-common.c index e92e87114a..6a1aa13d0a 100644 --- a/drivers/microsol-common.c +++ b/drivers/microsol-common.c @@ -756,7 +756,9 @@ void upsdrv_shutdown(void) { /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/netxml-ups.c b/drivers/netxml-ups.c index 94fa5679b3..84dd54741f 100644 --- a/drivers/netxml-ups.c +++ b/drivers/netxml-ups.c @@ -432,7 +432,9 @@ void upsdrv_shutdown(void) { * call protocol cleanup below, if needed. */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } @@ -475,8 +477,8 @@ void upsdrv_shutdown(void) { if (STAT_SET_HANDLED != status) { upslogx(LOG_ERR, "Shutdown failed: %d", status); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/nut-ipmipsu.c b/drivers/nut-ipmipsu.c index 57e8263ae2..ce84db1bc8 100644 --- a/drivers/nut-ipmipsu.c +++ b/drivers/nut-ipmipsu.c @@ -167,8 +167,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/nutdrv_atcl_usb.c b/drivers/nutdrv_atcl_usb.c index 5de62e568b..ba46a6ea64 100644 --- a/drivers/nutdrv_atcl_usb.c +++ b/drivers/nutdrv_atcl_usb.c @@ -716,7 +716,9 @@ void upsdrv_shutdown(void) /* FIXME: Check with the device what our instcmd * (nee upsdrv_shutdown() contents) actually does! */ - loop_shutdown_commands("shutdown.stayoff", NULL); + int ret = loop_shutdown_commands("shutdown.stayoff", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index ee59ddcf6e..41b5c21b1b 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -2780,7 +2780,9 @@ void upsdrv_shutdown(void) * replace tunables used below ("stayoff" etc). */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } @@ -2792,8 +2794,8 @@ void upsdrv_shutdown(void) /* Don't know what happened */ if (!item) { upslogx(LOG_ERR, "Unable to set start delay"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); return; } @@ -2808,8 +2810,8 @@ void upsdrv_shutdown(void) if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) { upslogx(LOG_ERR, "Start delay '%s' out of range", val); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); return; } @@ -2819,8 +2821,8 @@ void upsdrv_shutdown(void) /* Don't know what happened */ if (!item) { upslogx(LOG_ERR, "Unable to set shutdown delay"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); return; } @@ -2835,14 +2837,13 @@ void upsdrv_shutdown(void) if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) { upslogx(LOG_ERR, "Shutdown delay '%s' out of range", val); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); return; } /* Stop pending shutdowns */ if (find_nut_info("shutdown.stop", QX_FLAG_CMD, QX_FLAG_SKIP)) { - for (retry = 1; retry <= MAXTRIES; retry++) { if (instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { @@ -2856,36 +2857,30 @@ void upsdrv_shutdown(void) if (retry > MAXTRIES) { upslogx(LOG_NOTICE, "No shutdown pending"); } - } /* Shutdown */ for (retry = 1; retry <= MAXTRIES; retry++) { - if (testvar("stayoff")) { - if (instcmd("shutdown.stayoff", NULL) != STAT_INSTCMD_HANDLED) { continue; } - } else { - if (instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) { continue; } - } upslogx(LOG_ERR, "Shutting down in %s seconds", dstate_getinfo("ups.delay.shutdown")); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_SUCCESS); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } #ifdef QX_USB diff --git a/drivers/nutdrv_siemens_sitop.c b/drivers/nutdrv_siemens_sitop.c index daf88152f5..ff91f4727a 100644 --- a/drivers/nutdrv_siemens_sitop.c +++ b/drivers/nutdrv_siemens_sitop.c @@ -250,7 +250,9 @@ void upsdrv_updateinfo(void) { void upsdrv_shutdown(void) { /* by default, tell the UPS to shut down, * then return - DO NOT SLEEP HERE */ - loop_shutdown_commands("shutdown.return", NULL); + int ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/oneac.c b/drivers/oneac.c index 217e3418e8..cdf6f9dbaa 100644 --- a/drivers/oneac.c +++ b/drivers/oneac.c @@ -821,7 +821,9 @@ void upsdrv_shutdown(void) * implemented in instcmd() here nominally (not sure if named * correctly - better re-check on hardware). */ - loop_shutdown_commands("shutdown.reboot", NULL); + int ret = loop_shutdown_commands("shutdown.reboot", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/optiups.c b/drivers/optiups.c index d0cb4616ea..e5a711e400 100644 --- a/drivers/optiups.c +++ b/drivers/optiups.c @@ -541,12 +541,14 @@ void upsdrv_shutdown(void) /* OB: the load must remain off until the power returns */ /* If get no response, assume on battery & battery low */ - long s = OPTISBIT_ON_BATTERY_POWER | OPTISBIT_LOW_BATTERY; - ssize_t r; + long s = OPTISBIT_ON_BATTERY_POWER | OPTISBIT_LOW_BATTERY; + ssize_t r; /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/phoenixcontact_modbus.c b/drivers/phoenixcontact_modbus.c index 101c50396d..0f6937d777 100644 --- a/drivers/phoenixcontact_modbus.c +++ b/drivers/phoenixcontact_modbus.c @@ -155,8 +155,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/pijuice.c b/drivers/pijuice.c index 4fda7adf82..7e72672bcf 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -854,7 +854,9 @@ void upsdrv_shutdown(void) /* FIXME: Check with the device what our instcmd * (nee upsdrv_shutdown() contents) actually does! */ - loop_shutdown_commands("shutdown.stayoff", NULL); + int ret = loop_shutdown_commands("shutdown.stayoff", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/powercom.c b/drivers/powercom.c index 97d636806d..0019fa5b87 100644 --- a/drivers/powercom.c +++ b/drivers/powercom.c @@ -297,8 +297,8 @@ static void shutdown_halt(void) ser_send_char (upsfd, types[type].shutdown_arguments.delay[1]); upslogx(LOG_INFO, "Shutdown (stayoff) initiated."); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_SUCCESS); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); } static void shutdown_ret(void) @@ -310,8 +310,8 @@ static void shutdown_ret(void) ser_send_char (upsfd, types[type].shutdown_arguments.delay[1]); upslogx(LOG_INFO, "Shutdown (return) initiated."); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_SUCCESS); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); } /* registered instant commands */ @@ -844,12 +844,16 @@ void upsdrv_updateinfo(void) /* shutdown UPS */ void upsdrv_shutdown(void) { + int ret = -1; + if (!device_sdcommands) { /* default: power down the attached load immediately */ printf("Forced UPS shutdown (and wait for power)...\n"); } - loop_shutdown_commands("shutdown.return", NULL); + ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } /* initialize UPS */ diff --git a/drivers/powerman-pdu.c b/drivers/powerman-pdu.c index d37798ee84..bfaf5a958b 100644 --- a/drivers/powerman-pdu.c +++ b/drivers/powerman-pdu.c @@ -160,8 +160,8 @@ void upsdrv_shutdown(void) /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/powerpanel.c b/drivers/powerpanel.c index 86fd8be2b5..a555b179e9 100644 --- a/drivers/powerpanel.c +++ b/drivers/powerpanel.c @@ -106,7 +106,9 @@ void upsdrv_shutdown(void) /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } @@ -124,7 +126,6 @@ void upsdrv_shutdown(void) * we can't read status or it is telling us we're on battery. */ for (i = 0; i < MAXTRIES; i++) { - ret = subdriver[mode]->updateinfo(); if (ret >= 0) { break; diff --git a/drivers/rhino.c b/drivers/rhino.c index 142a1f8e51..e268a08093 100644 --- a/drivers/rhino.c +++ b/drivers/rhino.c @@ -754,7 +754,9 @@ void upsdrv_shutdown(void) * in both cases, just with different logged messages. */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/richcomm_usb.c b/drivers/richcomm_usb.c index 1328a8c1c4..7977ae8a55 100644 --- a/drivers/richcomm_usb.c +++ b/drivers/richcomm_usb.c @@ -778,7 +778,9 @@ void upsdrv_shutdown(void) /* FIXME: Check with the device what our instcmd * (nee upsdrv_shutdown() contents) actually does! */ - loop_shutdown_commands("shutdown.return", NULL); + int ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/riello_ser.c b/drivers/riello_ser.c index 1ec4985de8..8af39b1ddd 100644 --- a/drivers/riello_ser.c +++ b/drivers/riello_ser.c @@ -1168,7 +1168,9 @@ void upsdrv_shutdown(void) /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } @@ -1198,14 +1200,14 @@ void upsdrv_shutdown(void) } upslogx(LOG_ERR, "Shutting down"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_SUCCESS); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c index 576c2c062c..e16246355d 100644 --- a/drivers/riello_usb.c +++ b/drivers/riello_usb.c @@ -1139,7 +1139,9 @@ void upsdrv_shutdown(void) /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } @@ -1168,14 +1170,14 @@ void upsdrv_shutdown(void) } upslogx(LOG_ERR, "Shutting down"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_SUCCESS); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_updateinfo(void) diff --git a/drivers/safenet.c b/drivers/safenet.c index 3ac5ba6547..fbb28026d5 100644 --- a/drivers/safenet.c +++ b/drivers/safenet.c @@ -423,7 +423,9 @@ void upsdrv_shutdown(void) /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } @@ -441,8 +443,8 @@ void upsdrv_shutdown(void) } upslogx(LOG_ERR, "SafeNet protocol compatible UPS not found on %s", device_path); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); return; } diff --git a/drivers/skel.c b/drivers/skel.c index 0ad09d8561..d79c665491 100644 --- a/drivers/skel.c +++ b/drivers/skel.c @@ -116,8 +116,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } /* you may have to check the line status since the commands diff --git a/drivers/sms_ser.c b/drivers/sms_ser.c index a55d2d5d69..07a210ed43 100644 --- a/drivers/sms_ser.c +++ b/drivers/sms_ser.c @@ -519,7 +519,9 @@ void upsdrv_shutdown(void) { /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } @@ -548,14 +550,14 @@ void upsdrv_shutdown(void) { } upslogx(LOG_ERR, "Shutting down"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_SUCCESS); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) { diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 63d695d7aa..d159d13336 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -368,12 +368,14 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands("shutdown.return,shutdown.reboot,load.off.delay,shutdown.stayoff", &cmd_used) == STAT_INSTCMD_HANDLED) { upslogx(LOG_INFO, "Shutdown successful with '%s'", NUT_STRARG(cmd_used)); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/socomec_jbus.c b/drivers/socomec_jbus.c index 3722692590..7268e9b4b8 100644 --- a/drivers/socomec_jbus.c +++ b/drivers/socomec_jbus.c @@ -437,8 +437,8 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "shutdown not supported"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } } diff --git a/drivers/solis.c b/drivers/solis.c index 27ee4a1e75..d7f93d58c5 100644 --- a/drivers/solis.c +++ b/drivers/solis.c @@ -964,7 +964,9 @@ void upsdrv_updateinfo(void) { void upsdrv_shutdown(void) { /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/tripplite.c b/drivers/tripplite.c index d0a571a239..3d330b3b54 100644 --- a/drivers/tripplite.c +++ b/drivers/tripplite.c @@ -378,7 +378,9 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { - loop_shutdown_commands("shutdown.return", NULL); + int ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } void upsdrv_updateinfo(void) diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index a034bbfcd0..e71b04d9a6 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -1261,7 +1261,9 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { - loop_shutdown_commands("shutdown.return", NULL); + int ret = loop_shutdown_commands("shutdown.return", NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } void upsdrv_updateinfo(void) diff --git a/drivers/tripplitesu.c b/drivers/tripplitesu.c index da778221c3..0262203e1b 100644 --- a/drivers/tripplitesu.c +++ b/drivers/tripplitesu.c @@ -846,11 +846,13 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - char parm[20]; + char parm[20]; /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/upscode2.c b/drivers/upscode2.c index 1bd446c286..aa8d8e62e3 100644 --- a/drivers/upscode2.c +++ b/drivers/upscode2.c @@ -878,7 +878,9 @@ void upsdrv_shutdown(void) { /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 822e88f0e2..16c4bb8fee 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -999,12 +999,14 @@ void upsdrv_shutdown(void) */ if (loop_shutdown_commands("shutdown.return,shutdown.reboot,load.off.delay,shutdown.stayoff", &cmd_used) == STAT_INSTCMD_HANDLED) { upslogx(LOG_INFO, "Shutdown successful with '%s'", NUT_STRARG(cmd_used)); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_SUCCESS); return; } upslogx(LOG_ERR, "Shutdown failed!"); - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - set_exit_flag(EF_EXIT_FAILURE); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/victronups.c b/drivers/victronups.c index adfe93a978..12e7578654 100644 --- a/drivers/victronups.c +++ b/drivers/victronups.c @@ -500,7 +500,9 @@ void upsdrv_shutdown(void) { /* FIXME: Make a name for default original shutdown */ if (device_sdcommands) { - loop_shutdown_commands(NULL, NULL); + int ret = loop_shutdown_commands(NULL, NULL); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); return; } From a25e052620c82201acceef93046fbfeb2455b9b9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 15:10:59 +0100 Subject: [PATCH 075/144] drivers/ivtscd.c: comment about upsdrv_shutdown() oddness in this driver [#2670] Signed-off-by: Jim Klimov --- drivers/ivtscd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/ivtscd.c b/drivers/ivtscd.c index 2bca607646..a747bbd65a 100644 --- a/drivers/ivtscd.c +++ b/drivers/ivtscd.c @@ -191,6 +191,12 @@ void upsdrv_shutdown(void) return; } + /* FIXME: This driver (and solar device?) does not seem to + * really support a shutdown. It also blocks in this method + * until battery.voltage.act becomes(?) greater than nominal, + * meaning power is back, and then exits the driver. + * All in all, looks odd. + */ while (1) { if (ivt_status() < 7) { continue; From 2d9926d995133ff4d91a045babf992623bcf709b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 15:11:27 +0100 Subject: [PATCH 076/144] drivers/powercom.c: drop checks for HAVE___ATTRIBUTE__NORETURN in instcmd() [#2670] Signed-off-by: Jim Klimov --- drivers/powercom.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/powercom.c b/drivers/powercom.c index 0019fa5b87..89fe549667 100644 --- a/drivers/powercom.c +++ b/drivers/powercom.c @@ -326,15 +326,11 @@ static int instcmd (const char *cmdname, const char *extra) * wall-power gets restored. The routine exits the driver anyway. */ shutdown_ret(); -#ifndef HAVE___ATTRIBUTE__NORETURN return STAT_INSTCMD_HANDLED; -#endif } if (!strcasecmp(cmdname, "shutdown.stayoff")) { shutdown_halt(); -#ifndef HAVE___ATTRIBUTE__NORETURN return STAT_INSTCMD_HANDLED; -#endif } upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); From cc55e5a7139802900464661d9c5564f8687edf07 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Nov 2024 15:25:15 +0100 Subject: [PATCH 077/144] drivers/main.{c,h}, docs: introduce `shutdown.default` INSTCMD concept and upsdrv_shutdown_default() shared method [#2670] Signed-off-by: Jim Klimov --- NEWS.adoc | 4 +++- docs/man/ups.conf.txt | 3 ++- docs/nut-names.txt | 7 ++++++ drivers/main.c | 56 ++++++++++++++++++++++++++++++++++++++++--- drivers/main.h | 8 +++++++ 5 files changed, 73 insertions(+), 5 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 8a5aa05c32..6daf6b8450 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -328,7 +328,9 @@ it is getting set at all in "killpower" vs. other cases, based on new `handling_upsdrv_shutdown` internal flag. + NOTE: during this overhaul, many older drivers got their first ever supported -INSTCMD such as `shutdown.return`, `shutdown.stayoff` or `load.off`. [#2670] +INSTCMD such as `shutdown.return`, `shutdown.stayoff` or `load.off`. Default +logic that was previously the content of `upsdrv_shutdown()` methods was often +relocated into new `shutdown.default` INSTCMD definitions. [#2670] - common code: * introduced a `NUT_DEBUG_SYSLOG` environment variable to tweak activation diff --git a/docs/man/ups.conf.txt b/docs/man/ups.conf.txt index 09b32a4126..cf2020603a 100644 --- a/docs/man/ups.conf.txt +++ b/docs/man/ups.conf.txt @@ -220,7 +220,8 @@ The default value for this parameter is 0. Optional. Comma-separated list of instant command name(s) to send to the UPS when you request its shutdown. + -Default value is built into each driver (where supported). +Default logic is built into each driver (where supported) and can be +referenced here as the `shutdown.default` value. + The primary use-case is for devices whose drivers "natively" support trying several commands, but the built-in order of those calls a diff --git a/docs/nut-names.txt b/docs/nut-names.txt index 07a62bbcc0..ed47b387c5 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -852,6 +852,13 @@ Instant commands | 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.default | Run default driver-defined (device-specific) + routine, primarily intended for emergency + poweroff performed as part of FSD handling; + often an alias to other `shutdown.*` and/or + `load.off` operations or a chain to try + several of those. See also `sdcommands` in + common driver options. | 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 diff --git a/drivers/main.c b/drivers/main.c index 781396acd8..2f5ed9b6b8 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -771,7 +771,8 @@ void addvar(int vartype, const char *name, const char *desc) * by the device, or STAT_INSTCMD_INVALID if none succeeded. * If cmdused is not NULL, it is populated by the command * string which succeeded (or NULL if none), and the caller - * should free() it eventually. + * should free() it eventually. This method also frees any + * non-NULL *cmdused, so it should be pre-initialized to NULL. */ int do_loop_shutdown_commands(const char *sdcmds, char **cmdused) { int cmdret = STAT_INSTCMD_UNKNOWN; @@ -779,8 +780,11 @@ int do_loop_shutdown_commands(const char *sdcmds, char **cmdused) { upsdebugx(1, "%s(%s)...", __func__, NUT_STRARG(sdcmds)); - if (cmdused) + if (cmdused) { + if (*cmdused) + free(*cmdused); *cmdused = NULL; + } if (!sdcmds || !*sdcmds) { upsdebugx(1, "This driver or its configuration did not pass any instant commands to run"); @@ -798,7 +802,15 @@ int do_loop_shutdown_commands(const char *sdcmds, char **cmdused) { continue; if ((cmdret = upsh.instcmd(s, NULL)) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ - if (cmdused) + + /* Note: If we are handling "shutdown.default" here, + * it is anticipated that it calls some other INSTCMD + * as the implementation, and that could set a value + * which we actually want to keep and tell the caller. + * We had freed *cmdused above, so it if is not empty + * here - something during the handling populated it. + */ + if (cmdused && !(*cmdused)) *cmdused = xstrdup(s); upsdebugx(1, "%s(): command '%s' was handled successfully", __func__, NUT_STRARG(s)); goto done; @@ -836,6 +848,44 @@ int loop_shutdown_commands(const char *sdcmds_default, char **cmdused) { } } +/* Common and default implementation of upsdrv_shutdown() in most drivers, + * unless they do something that can not be made instcmd("shutdown.default") + */ +int upsdrv_shutdown_default(const char *sdcmds_default, char **cmdused) { + char *sdcmd_used = NULL; + int sdret = loop_shutdown_commands( + sdcmds_default ? sdcmds_default : "shutdown.default", + &sdcmd_used); + + if (cmdused) { + if (*cmdused) + free(*cmdused); + *cmdused = NULL; + } + + if (sdret == STAT_INSTCMD_HANDLED) { + upslogx(LOG_INFO, "UPS [%s]: shutdown request was successful with '%s'", + NUT_STRARG(upsname), NUT_STRARG(sdcmd_used)); + + /* Pass it up to caller? */ + if (cmdused) { + *cmdused = sdcmd_used; + } else { + if (sdcmd_used) + free(sdcmd_used); + } + } else if (!upsh.instcmd) { + upslogx(LOG_ERR, "UPS [%s]: shutdown not supported", NUT_STRARG(upsname)); + } else { + upslogx(LOG_ERR, "UPS [%s]: shutdown request(s) failed", NUT_STRARG(upsname)); + } + + if (handling_upsdrv_shutdown > 0) + set_exit_flag(sdret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); + + return sdret; +} + /* handle instant commands common for all drivers */ int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { char buf[SMALLBUF]; diff --git a/drivers/main.h b/drivers/main.h index fa6437cf61..88671b3175 100644 --- a/drivers/main.h +++ b/drivers/main.h @@ -49,6 +49,14 @@ int do_loop_shutdown_commands(const char *sdcmds, char **cmdused); */ int loop_shutdown_commands(const char *sdcmds_default, char **cmdused); +/* + * Effectively call loop_shutdown_commands("shutdown.default") (which in turn + * probably calls some other INSTCMD, but may be using a more custom logic), + * and report how that went. + * Depending on run-time circumstances, probably set_exit_flag() too. + */ +int upsdrv_shutdown_default(const char *sdcmds_default, char **cmdused); + /* handle instant commands common for all drivers * (returns STAT_INSTCMD_* state values per enum in upshandler.h) */ From 7d98b52f4e39e2055bd0339655a13e3b1c07944f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 11:16:55 +0100 Subject: [PATCH 078/144] drivers/main.c: update some comments Signed-off-by: Jim Klimov --- drivers/main.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index 2f5ed9b6b8..b457515671 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -902,6 +902,12 @@ int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { cmdname, extra, NUT_STRARG(upsname), buf); if (!strcmp(cmdname, "driver.killpower")) { + /* An implementation of `drivername -k` requested from + * the running and connected driver instance by protocol + * over local Unix socket or pipe, primarily intended + * for (emergency) FSD use-cases and so with a fail-safe + * flag involved. + */ if (!strcmp("1", dstate_getinfo("driver.flag.allow_killpower"))) { upslogx(LOG_WARNING, "Requesting UPS [%s] to power off, " "as/if handled by its driver by default (may exit), " @@ -2221,7 +2227,7 @@ int main(int argc, char **argv) /* Only switch to statepath if we're not powering off * or not just dumping data (for discovery) */ - /* This avoids case where ie /var is unmounted already */ + /* This avoids case where i.e. /var is unmounted already */ #ifndef WIN32 if ((!do_forceshutdown) && (!dump_data)) { if (chdir(dflt_statepath())) @@ -2869,7 +2875,8 @@ int main(int argc, char **argv) upslogx(LOG_WARNING, "Running as foreground process, not saving a PID file"); } - /* May already be set by parsed configuration flag, only set default if not: */ + /* May already be set by parsed configuration flag, + * only set default if not: */ if (dstate_getinfo("driver.flag.allow_killpower") == NULL) dstate_setinfo("driver.flag.allow_killpower", "0"); From 7b52ab909be1cc234880eee01b51252caad0e6e8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 11:17:53 +0100 Subject: [PATCH 079/144] drivers/main.c: implement INSTCMD "shutdown.default" as a way to call upsdrv_shutdown() in any driver [#2670] Signed-off-by: Jim Klimov --- drivers/main.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/main.c b/drivers/main.c index b457515671..c9063570b3 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -901,6 +901,24 @@ int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { upsdebugx(2, "entering main_instcmd(%s, %s) for [%s] on %s", cmdname, extra, NUT_STRARG(upsname), buf); + if (!strcmp(cmdname, "shutdown.default")) { + /* Call the default implementation of UPS shutdown as + * an instant command, should not halt nor exit the + * driver program, so a subsequent power-on command + * may be possible (depending on driver and device). + * May well be implemented as recursion into "load.off", + * "shutdown.return" or "shutdown.stayoff" - possibly + * with a choice which one to call made at run-time, + * but in some drivers may involve logic not equal to + * any one of those other command implementations. + * Primarily intended to be a choice among `sdcommands`, + * called by default if none were passed (or this one + * was passed explicitly). + */ + upsdrv_shutdown(); + return STAT_INSTCMD_HANDLED; + } + if (!strcmp(cmdname, "driver.killpower")) { /* An implementation of `drivername -k` requested from * the running and connected driver instance by protocol @@ -2691,6 +2709,10 @@ int main(int argc, char **argv) /* get the base data established before allowing connections */ dstate_setinfo("driver.state", "init.info"); upsdrv_initinfo(); + + /* Register a way to call upsdrv_shutdown() among `sdcommands` */ + dstate_addcmd("shutdown.default"); + /* Note: a few drivers also call their upsdrv_updateinfo() during * their upsdrv_initinfo(), possibly to impact the initialization */ dstate_setinfo("driver.state", "init.updateinfo"); From 59acc68ce8c67e641087168848cc8a33ea94ce10 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 14:36:49 +0100 Subject: [PATCH 080/144] drivers/main.c: do_loop_shutdown_commands(): shortcut for "shutdown.default" support [#2670] Signed-off-by: Jim Klimov --- drivers/main.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/main.c b/drivers/main.c index c9063570b3..63abece7cd 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -793,6 +793,28 @@ int do_loop_shutdown_commands(const char *sdcmds, char **cmdused) { if (upsh.instcmd == NULL) { upsdebugx(1, "This driver does not implement INSTCMD support"); + + /* ...but the default one we can short-circuit without + * registered INSTCMDs (FIXME: loop detection/protection): + */ + s = strstr(sdcmds, "shutdown.default"); + if (s) { + /* check this is really a sub-string */ + size_t cmdlen = strlen("shutdown.default"); + if ( + (s == sdcmds || *(s-1) == ',') && + (s[cmdlen] == '\0' || s[cmdlen] == ',') + ) { + upsdebugx(1, "Handle 'shutdown.default' directly, " + "ignore other `sdcommands` (if any): %s", + sdcmds); + upsdrv_shutdown(); + cmdret = STAT_INSTCMD_HANDLED; + /* commented below */ + if (cmdused && !(*cmdused)) + *cmdused = xstrdup("shutdown.default"); + } + } goto done; } From 40a0212f57c73e096ef6e528a20d0193efca9223 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 14:42:28 +0100 Subject: [PATCH 081/144] drivers/main.{c,h}: rename upsdrv_shutdown_default() => upsdrv_shutdown_sdcommands_or_default() less ambiguously [#2670] Signed-off-by: Jim Klimov --- drivers/main.c | 2 +- drivers/main.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index 63abece7cd..bf95575ce2 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -873,7 +873,7 @@ int loop_shutdown_commands(const char *sdcmds_default, char **cmdused) { /* Common and default implementation of upsdrv_shutdown() in most drivers, * unless they do something that can not be made instcmd("shutdown.default") */ -int upsdrv_shutdown_default(const char *sdcmds_default, char **cmdused) { +int upsdrv_shutdown_sdcommands_or_default(const char *sdcmds_default, char **cmdused) { char *sdcmd_used = NULL; int sdret = loop_shutdown_commands( sdcmds_default ? sdcmds_default : "shutdown.default", diff --git a/drivers/main.h b/drivers/main.h index 88671b3175..a7d24220b2 100644 --- a/drivers/main.h +++ b/drivers/main.h @@ -55,7 +55,7 @@ int loop_shutdown_commands(const char *sdcmds_default, char **cmdused); * and report how that went. * Depending on run-time circumstances, probably set_exit_flag() too. */ -int upsdrv_shutdown_default(const char *sdcmds_default, char **cmdused); +int upsdrv_shutdown_sdcommands_or_default(const char *sdcmds_default, char **cmdused); /* handle instant commands common for all drivers * (returns STAT_INSTCMD_* state values per enum in upshandler.h) From 00b291a57e76bb7489e8385cf22335c35c7f56b1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 14:44:55 +0100 Subject: [PATCH 082/144] drivers/main.{c,h}: call upsdrv_shutdown_sdcommands_or_default() not default upsdrv_shutdown() as the implementation of forceshutdown() and "driver.killpower" INSTCMD (e.g. via `drivername -k`) [#2670] Signed-off-by: Jim Klimov --- drivers/main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index bf95575ce2..4c6abf494f 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -187,7 +187,7 @@ static void forceshutdown(void) static void forceshutdown(void) { - upslogx(LOG_NOTICE, "Initiating UPS shutdown"); + upslogx(LOG_NOTICE, "Initiating UPS [%s] shutdown", upsname); /* NOTE: This is currently called exclusively as `drivername -k` * CLI argument handling, so we exit afterwards and do not care @@ -195,8 +195,9 @@ static void forceshutdown(void) */ handling_upsdrv_shutdown = 1; - /* the driver must not block in this function */ - upsdrv_shutdown(); + /* the driver must not block in this function (calling INSTCMD from + * `sdcommands` passed by user, or upsdrv_shutdown() by default */ + upsdrv_shutdown_sdcommands_or_default(NULL, NULL); /* the driver always exits here, to * not block probable ongoing shutdown */ @@ -954,7 +955,7 @@ int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { "due to socket protocol request", NUT_STRARG(upsname)); if (handling_upsdrv_shutdown == 0) handling_upsdrv_shutdown = 1; - upsdrv_shutdown(); + upsdrv_shutdown_sdcommands_or_default(NULL, NULL); return STAT_INSTCMD_HANDLED; } else { upslogx(LOG_WARNING, "Got socket protocol request for UPS [%s] " From 25250b02aea4e31ba20a17a33e6cf22cfaf27135 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 14:58:12 +0100 Subject: [PATCH 083/144] drivers/main.{c,h}, drivers/dstate.c: introduce main_instcmd_fallback() so drivers might implement their own "shutdown.default" at will [#2670] Signed-off-by: Jim Klimov --- drivers/dstate.c | 25 ++++++++++++++++++------- drivers/main.c | 28 ++++++++++++++++++++++++++-- drivers/main.h | 4 ++++ 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 2b36688291..46a3bfb80e 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -847,15 +847,26 @@ static int sock_arg(conn_t *conn, size_t numarg, char **arg) if (upsh.instcmd) { ret = upsh.instcmd(cmdname, cmdparam); - /* send back execution result if requested */ - if (cmdid) - send_tracking(conn, cmdid, ret); + if (ret != STAT_INSTCMD_UNKNOWN) { + /* send back execution result if requested */ + if (cmdid) + send_tracking(conn, cmdid, ret); - /* The command was handled, status is a separate consideration */ - return 1; - } + /* The command was handled, status is a separate consideration */ + return 1; + } /* else try other handler(s) */ + } /* else try other handler(s) */ + + /* Finally try the fallback handler shared by all drivers */ + ret = main_instcmd_fallback(cmdname, cmdparam, conn); + /* send back execution result if requested */ + if (cmdid) + send_tracking(conn, cmdid, ret); - upslogx(LOG_NOTICE, "Got INSTCMD, but driver lacks a handler"); + if (ret == STAT_INSTCMD_UNKNOWN) + upslogx(LOG_NOTICE, + "Got INSTCMD '%s', but driver lacks a handler", + NUT_STRARG(cmdname)); return 1; } diff --git a/drivers/main.c b/drivers/main.c index 4c6abf494f..b1c34fe14f 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -909,8 +909,9 @@ int upsdrv_shutdown_sdcommands_or_default(const char *sdcmds_default, char **cmd return sdret; } -/* handle instant commands common for all drivers */ -int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { +/* handle instant commands common for all drivers - fallback for common + * command names that could be implemented in a driver but were not */ +int main_instcmd_fallback(const char *cmdname, const char *extra, conn_t *conn) { char buf[SMALLBUF]; if (conn) #ifndef WIN32 @@ -942,6 +943,29 @@ int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { return STAT_INSTCMD_HANDLED; } + /* By default, the driver-specific values are + * unknown to shared standard handler */ + upsdebugx(2, "shared %s() does not handle command %s, " + "proceeding to driver-specific handler", + __func__, cmdname); + return STAT_INSTCMD_UNKNOWN; +} + +/* handle instant commands common for all drivers */ +int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { + char buf[SMALLBUF]; + if (conn) +#ifndef WIN32 + snprintf(buf, sizeof(buf), "socket %d", conn->fd); +#else + snprintf(buf, sizeof(buf), "handle %p", conn->fd); +#endif + else + snprintf(buf, sizeof(buf), "(null)"); + + upsdebugx(2, "entering main_instcmd(%s, %s) for [%s] on %s", + cmdname, extra, NUT_STRARG(upsname), buf); + if (!strcmp(cmdname, "driver.killpower")) { /* An implementation of `drivername -k` requested from * the running and connected driver instance by protocol diff --git a/drivers/main.h b/drivers/main.h index a7d24220b2..bb8c89e61b 100644 --- a/drivers/main.h +++ b/drivers/main.h @@ -62,6 +62,10 @@ int upsdrv_shutdown_sdcommands_or_default(const char *sdcmds_default, char **cmd */ int main_instcmd(const char *cmdname, const char *extra, conn_t *conn); +/* handle instant commands common for all drivers - fallback for common + * command names that could be implemented in a driver but were not */ +int main_instcmd_fallback(const char *cmdname, const char *extra, conn_t *conn); + /* handle setting variables common for all drivers * (returns STAT_SET_* state values per enum in upshandler.h) */ From 17cf2dab651dacfc4c7d9aa4f37685daf9ea7c8e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 15:18:20 +0100 Subject: [PATCH 084/144] drivers/main.{c,h}: introduce MAX_SDCOMMANDS_DEPTH for do_loop_shutdown_commands() [#2670] Signed-off-by: Jim Klimov --- drivers/main.c | 24 +++++++++++++++++++++--- drivers/main.h | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index b1c34fe14f..3df14337a3 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -778,8 +778,22 @@ void addvar(int vartype, const char *name, const char *desc) int do_loop_shutdown_commands(const char *sdcmds, char **cmdused) { int cmdret = STAT_INSTCMD_UNKNOWN; char *buf = NULL, *s = NULL; - - upsdebugx(1, "%s(%s)...", __func__, NUT_STRARG(sdcmds)); + static int call_depth = 0; + + call_depth++; + upsdebugx(1, "Starting %s(%s), call depth %d...", + __func__, NUT_STRARG(sdcmds), call_depth); + + if (call_depth > MAX_SDCOMMANDS_DEPTH) { + /* Might get here e.g. if someone were to call + * upsdrv_shutdown_sdcommands_or_default() along + * an implementation code path from upsdrv_shutdown() + * with sdcmds=="shutdown.default"?.. */ + fatalx(EXIT_FAILURE, "Shutdown handlers for UPS [%s] are " + "too deeply nested, this seems to be either " + "a NUT programming error or a mis-configuration " + "of your 'sdcommands' setting", NUT_STRARG(upsname)); + } if (cmdused) { if (*cmdused) @@ -845,7 +859,11 @@ int do_loop_shutdown_commands(const char *sdcmds, char **cmdused) { free(buf); if (cmdret != STAT_INSTCMD_HANDLED) cmdret = STAT_INSTCMD_INVALID; - upsdebugx(1, "%s(%s): %d", __func__, NUT_STRARG(sdcmds), cmdret); + + upsdebugx(1, "Ending %s(%s), call depth %d: return-code %d", + __func__, NUT_STRARG(sdcmds), call_depth, cmdret); + call_depth--; + return cmdret; } diff --git a/drivers/main.h b/drivers/main.h index bb8c89e61b..249a2b1202 100644 --- a/drivers/main.h +++ b/drivers/main.h @@ -40,6 +40,7 @@ void set_exit_flag(int sig); * should free() it eventually. */ int do_loop_shutdown_commands(const char *sdcmds, char **cmdused); +#define MAX_SDCOMMANDS_DEPTH 15 /* Use driver-provided sdcmds_default, unless a custom driver parameter value * "sdcommands" is set - then use it instead. Call do_loop_shutdown_commands() From a94bc837daca74924885a050c8a24a4b49d6ed2a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 17:10:15 +0100 Subject: [PATCH 085/144] drivers/main.c: update some comments [#2670] Signed-off-by: Jim Klimov --- drivers/main.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index 3df14337a3..d4d5be067f 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -881,6 +881,11 @@ int loop_shutdown_commands(const char *sdcmds_default, char **cmdused) { sdcmds_custom = dstate_getinfo("driver.parameter.sdcommands"); if (sdcmds_custom) { + /* NOTE: User-provided commands may be something other + * than actual shutdown, e.g. a beeper to test that the + * INSTCMD happened such and when expected without + * impacting the load fed by the UPS. + */ upsdebugx(1, "%s: call do_loop_shutdown_commands() with custom sdcommands", __func__); return do_loop_shutdown_commands(sdcmds_custom, cmdused); } else { @@ -889,8 +894,23 @@ int loop_shutdown_commands(const char *sdcmds_default, char **cmdused) { } } -/* Common and default implementation of upsdrv_shutdown() in most drivers, - * unless they do something that can not be made instcmd("shutdown.default") +/* Since NUT v2.8.3 updated driver model, here we typically call the + * user-provided list of `sdcommands` (if any), or the caller-provided + * list (hard-coded in a driver), or the default implementation as + * "shutdown.default" (usually ends up as upsdrv_shutdown()) if nothing + * else was requested by the configuration and call chain. + * Any of these call chains, especially the default one, in turn may + * call instcmd() for whatever practical action is needed (unless the + * default logic is all-custom implemented in upsdrv_shutdown()), and + * reports the result (including the case of lacking a registered + * upsh.instcmd handler). + * + * A practical shutdown implementation logic would be coded + * in instcmd("shutdown.default") and may be structured like + * detailed by comments in skel.c::upsdrv_shutdown(), or can + * just call a fitting other instcmd (typically "shutdown.stayoff", + * "shutdown.return" or "load.off") -- possibly chosen by current + * on-line/on-battery state. */ int upsdrv_shutdown_sdcommands_or_default(const char *sdcmds_default, char **cmdused) { char *sdcmd_used = NULL; From be0a35a2f9cbd72b07afe5c52c3b9a44696bab62 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 17:10:57 +0100 Subject: [PATCH 086/144] drivers/*.c: simplify upsdrv_shutdown() back, as only "shutdown.default" implementations again [#2670] Signed-off-by: Jim Klimov --- drivers/adelsystem_cbi.c | 5 +++- drivers/al175.c | 17 +++++------- drivers/apc_modbus.c | 23 ++++++++-------- drivers/apcsmart-old.c | 11 +++----- drivers/apcsmart.c | 11 +++----- drivers/apcupsd-ups.c | 17 +++++------- drivers/asem.c | 17 +++++------- drivers/bcmxcp.c | 5 +++- drivers/belkin.c | 5 +++- drivers/belkinunv.c | 11 +++----- drivers/bestfcom.c | 5 +++- drivers/bestfortress.c | 5 +++- drivers/bestuferrups.c | 5 +++- drivers/bestups.c | 9 ++----- drivers/bicker_ser.c | 11 +++----- drivers/blazer.c | 11 +++----- drivers/clone-outlet.c | 17 +++++------- drivers/clone.c | 17 +++++------- drivers/dummy-ups.c | 17 +++++------- drivers/etapro.c | 5 +++- drivers/everups.c | 5 +++- drivers/gamatronic.c | 5 +++- drivers/generic_gpio_common.c | 17 +++++------- drivers/generic_modbus.c | 5 +++- drivers/genericups.c | 11 +++----- drivers/huawei-ups2000.c | 7 ++--- drivers/hwmon_ina219.c | 17 +++++------- drivers/isbmex.c | 5 +++- drivers/ivtscd.c | 9 ++----- drivers/liebert-esp2.c | 11 +++----- drivers/liebert-gxe.c | 9 ++----- drivers/liebert.c | 11 +++----- drivers/macosx-ups.c | 17 +++++------- drivers/masterguard.c | 5 +++- drivers/metasys.c | 11 +++----- drivers/mge-utalk.c | 47 +++++++++------------------------ drivers/microdowell.c | 11 +++----- drivers/microsol-common.c | 9 ++----- drivers/netxml-ups.c | 19 ++++++------- drivers/nut-ipmipsu.c | 17 +++++------- drivers/nutdrv_atcl_usb.c | 5 +++- drivers/nutdrv_qx.c | 12 +++------ drivers/nutdrv_siemens_sitop.c | 5 +++- drivers/oneac.c | 5 +++- drivers/optiups.c | 14 +++------- drivers/phoenixcontact_modbus.c | 17 +++++------- drivers/pijuice.c | 5 +++- drivers/powercom.c | 5 +++- drivers/powerman-pdu.c | 23 +++++++--------- drivers/powerpanel.c | 11 +++----- drivers/rhino.c | 12 ++------- drivers/richcomm_usb.c | 5 +++- drivers/riello_ser.c | 11 +++----- drivers/riello_usb.c | 11 +++----- drivers/safenet.c | 11 +++----- drivers/skel.c | 21 ++++++++------- drivers/sms_ser.c | 11 +++----- drivers/snmp-ups.c | 5 +++- drivers/socomec_jbus.c | 17 +++++------- drivers/solis.c | 9 ++----- drivers/tripplite.c | 5 +++- drivers/tripplite_usb.c | 5 +++- drivers/tripplitesu.c | 11 +++----- drivers/upscode2.c | 9 ++----- drivers/usbhid-ups.c | 5 +++- drivers/victronups.c | 9 ++----- 66 files changed, 288 insertions(+), 443 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index a7faa2efbb..0539b796ec 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -468,6 +468,9 @@ void upsdrv_updateinfo(void) /* shutdown UPS */ void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* * WARNING: When using RTU TCP, this driver will probably * never support shutdowns properly, except on some systems: @@ -480,7 +483,7 @@ void upsdrv_shutdown(void) * a limitation (on some platforms) of the interface/media * used for these devices. */ - int ret = loop_shutdown_commands("shutdown.stayoff", NULL); + int ret = do_loop_shutdown_commands("shutdown.stayoff", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/al175.c b/drivers/al175.c index 7221786ca9..251c054339 100644 --- a/drivers/al175.c +++ b/drivers/al175.c @@ -1267,6 +1267,9 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* TODO use TOGGLE_PRS_ONOFF for shutdown */ /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ @@ -1275,17 +1278,9 @@ void upsdrv_shutdown(void) it doesn't respond at first if possible */ /* replace with a proper shutdown function */ - - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ diff --git a/drivers/apc_modbus.c b/drivers/apc_modbus.c index d3f5951d44..978948b3ea 100644 --- a/drivers/apc_modbus.c +++ b/drivers/apc_modbus.c @@ -1569,6 +1569,9 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* * WARNING: When using RTU TCP, this driver will probably * never support shutdowns properly, except on some systems: @@ -1582,19 +1585,15 @@ void upsdrv_shutdown(void) * used for these devices. */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } - - /* FIXME: Make a name for default original shutdown: - * got no direct equivalent in apc_modbus_command_map[] - * used for instcmd above + /* FIXME: got no direct equivalent in apc_modbus_command_map[] + * used for instcmd above. Investigate if we can add this + * combo into that map and name it as an INSTCMD to call by + * this driver's standard approach. */ - modbus_write_register(modbus_ctx, APC_MODBUS_OUTLETCOMMAND_BF_REG, APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_SHUTDOWN | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP); + modbus_write_register(modbus_ctx, + APC_MODBUS_OUTLETCOMMAND_BF_REG, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_SHUTDOWN | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP + ); } void upsdrv_help(void) diff --git a/drivers/apcsmart-old.c b/drivers/apcsmart-old.c index 16a5053392..1e28b09b2e 100644 --- a/drivers/apcsmart-old.c +++ b/drivers/apcsmart-old.c @@ -1063,18 +1063,13 @@ static void upsdrv_shutdown_advanced(long status) /* power down the attached load immediately */ void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + char temp[32]; ssize_t ret; long status; - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int sdret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(sdret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } - if (!smartmode()) upsdebugx(1, "SM detection failed. Trying a shutdown command anyway"); diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 28648d8d9a..80f4cc3e05 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -1753,15 +1753,10 @@ static void upsdrv_shutdown_advanced(void) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - char temp[APC_LBUF]; + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + char temp[APC_LBUF]; if (!smartmode(1)) upslogx(LOG_WARNING, "%s: %s", __func__, "setting SmartMode failed !"); diff --git a/drivers/apcupsd-ups.c b/drivers/apcupsd-ups.c index ddb02ef66c..66f2dcf42e 100644 --- a/drivers/apcupsd-ups.c +++ b/drivers/apcupsd-ups.c @@ -363,18 +363,13 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - /* replace with a proper shutdown function */ + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/asem.c b/drivers/asem.c index bcb7557793..77a826848f 100644 --- a/drivers/asem.c +++ b/drivers/asem.c @@ -326,23 +326,18 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ /* replace with a proper shutdown function */ - - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ diff --git a/drivers/bcmxcp.c b/drivers/bcmxcp.c index 2697d4a672..91218b1b8f 100644 --- a/drivers/bcmxcp.c +++ b/drivers/bcmxcp.c @@ -1948,11 +1948,14 @@ float calculate_ups_load(const unsigned char *answer) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + upsdebugx(1, "upsdrv_shutdown..."); /* First try to shutdown with delay; * if the above doesn't work, try shutdown.stayoff */ - if (loop_shutdown_commands("shutdown.return,shutdown.stayoff", NULL) == STAT_INSTCMD_HANDLED) { + if (do_loop_shutdown_commands("shutdown.return,shutdown.stayoff", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ if (handling_upsdrv_shutdown > 0) set_exit_flag(EF_EXIT_SUCCESS); diff --git a/drivers/belkin.c b/drivers/belkin.c index 2ff51c2b6e..7044fae268 100644 --- a/drivers/belkin.c +++ b/drivers/belkin.c @@ -352,7 +352,10 @@ void upsdrv_updateinfo(void) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - int ret = loop_shutdown_commands("shutdown.return", NULL); + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + + int ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/belkinunv.c b/drivers/belkinunv.c index 03f85838c3..09f7e40a99 100644 --- a/drivers/belkinunv.c +++ b/drivers/belkinunv.c @@ -1166,6 +1166,9 @@ void upsdrv_updateinfo(void) /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* Note: this UPS cannot (apparently) be put into "soft shutdown" mode; thus the -k option should not normally be used; instead, a workaround using the "-x wait" option @@ -1183,14 +1186,6 @@ void upsdrv_shutdown(void) page. */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } - upslogx(LOG_WARNING, "You are using the -k option, which is broken for this driver.\n" "Check belkinunv(8) man page about '-x wait' option instead.\n" diff --git a/drivers/bestfcom.c b/drivers/bestfcom.c index 7b6e40b1af..c2a437a593 100644 --- a/drivers/bestfcom.c +++ b/drivers/bestfcom.c @@ -491,7 +491,10 @@ int instcmd(const char *cmdname, const char *extra) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - int ret = loop_shutdown_commands("shutdown.return", NULL); + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + + int ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/bestfortress.c b/drivers/bestfortress.c index d453124219..8869840c6f 100644 --- a/drivers/bestfortress.c +++ b/drivers/bestfortress.c @@ -514,11 +514,14 @@ static int upsdrv_setvar (const char *var, const char * data) { */ void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + int ret = -1; upsdebugx(2, "%s: begin", __func__); - ret = loop_shutdown_commands("shutdown.return", NULL); + ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); diff --git a/drivers/bestuferrups.c b/drivers/bestuferrups.c index fb59f81c2d..0a1bbda1d6 100644 --- a/drivers/bestuferrups.c +++ b/drivers/bestuferrups.c @@ -390,7 +390,10 @@ int instcmd(const char *cmdname, const char *extra) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - int ret = loop_shutdown_commands("shutdown.return", NULL); + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + + int ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/bestups.c b/drivers/bestups.c index 7486bab647..d3746d02ef 100644 --- a/drivers/bestups.c +++ b/drivers/bestups.c @@ -319,13 +319,8 @@ static int ups_on_line(void) void upsdrv_shutdown(void) { - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ printf("The UPS will shut down in approximately one minute.\n"); diff --git a/drivers/bicker_ser.c b/drivers/bicker_ser.c index 9029b21144..0d2a121d58 100644 --- a/drivers/bicker_ser.c +++ b/drivers/bicker_ser.c @@ -860,15 +860,10 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - int retry; + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + int retry; for (retry = 1; retry <= BICKER_RETRIES; retry++) { if (bicker_shutdown() > 0) { diff --git a/drivers/blazer.c b/drivers/blazer.c index dcb58370a5..69add382bd 100644 --- a/drivers/blazer.c +++ b/drivers/blazer.c @@ -851,15 +851,10 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - int retry; + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + int retry; /* Stop pending shutdowns */ for (retry = 1; retry <= MAXTRIES; retry++) { diff --git a/drivers/clone-outlet.c b/drivers/clone-outlet.c index 9e5ae86e61..0cc8e7fa98 100644 --- a/drivers/clone-outlet.c +++ b/drivers/clone-outlet.c @@ -512,18 +512,13 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - /* replace with a proper shutdown function */ + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } diff --git a/drivers/clone.c b/drivers/clone.c index 53cf4eb175..ec45672dee 100644 --- a/drivers/clone.c +++ b/drivers/clone.c @@ -660,18 +660,13 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - /* replace with a proper shutdown function */ + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index c973285006..c20aabe376 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -382,18 +382,13 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - /* replace with a proper shutdown function */ + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } static int instcmd(const char *cmdname, const char *extra) diff --git a/drivers/etapro.c b/drivers/etapro.c index 9967cec07b..1e09d518cc 100644 --- a/drivers/etapro.c +++ b/drivers/etapro.c @@ -343,7 +343,10 @@ upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - int ret = loop_shutdown_commands("shutdown.return", NULL); + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + + int ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/everups.c b/drivers/everups.c index ebbf395dc5..b9a8f909d2 100644 --- a/drivers/everups.c +++ b/drivers/everups.c @@ -207,7 +207,10 @@ int instcmd(const char *cmdname, const char *extra) void upsdrv_shutdown(void) { - int ret = loop_shutdown_commands("load.off", NULL); + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + + int ret = do_loop_shutdown_commands("load.off", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/gamatronic.c b/drivers/gamatronic.c index 6261590a9a..3f37046ab9 100644 --- a/drivers/gamatronic.c +++ b/drivers/gamatronic.c @@ -335,7 +335,10 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - int ret = loop_shutdown_commands("shutdown.return", NULL); + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + + int ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/generic_gpio_common.c b/drivers/generic_gpio_common.c index 6097785bac..fd7229c811 100644 --- a/drivers/generic_gpio_common.c +++ b/drivers/generic_gpio_common.c @@ -471,18 +471,13 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - /* replace with a proper shutdown function */ + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 4011d13ae0..8dd986a769 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -320,6 +320,9 @@ void upsdrv_updateinfo(void) /* shutdown UPS */ void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* * WARNING: When using RTU TCP, this driver will probably * never support shutdowns properly, except on some systems: @@ -333,7 +336,7 @@ void upsdrv_shutdown(void) * used for these devices. */ - int ret = loop_shutdown_commands("shutdown.stayoff", NULL); + int ret = do_loop_shutdown_commands("shutdown.stayoff", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/genericups.c b/drivers/genericups.c index 3a5336be3d..2fde2f8555 100644 --- a/drivers/genericups.c +++ b/drivers/genericups.c @@ -310,15 +310,10 @@ static void set_ups_type(void) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - int flags, ret; + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + int flags, ret; if (upstype == -1) { upslogx(LOG_ERR, "No upstype set - see help text / man page!"); diff --git a/drivers/huawei-ups2000.c b/drivers/huawei-ups2000.c index bbb280bb44..31afe6bcb4 100644 --- a/drivers/huawei-ups2000.c +++ b/drivers/huawei-ups2000.c @@ -1801,10 +1801,11 @@ static int ups2000_update_timers(void) void upsdrv_shutdown(void) { - int r; + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - r = loop_shutdown_commands("shutdown.reboot", NULL); - if (r != STAT_INSTCMD_HANDLED) { + int ret = do_loop_shutdown_commands("shutdown.reboot", NULL); + if (ret != STAT_INSTCMD_HANDLED) { upslogx(LOG_ERR, "upsdrv_shutdown failed!"); if (handling_upsdrv_shutdown > 0) set_exit_flag(EF_EXIT_FAILURE); diff --git a/drivers/hwmon_ina219.c b/drivers/hwmon_ina219.c index 894e20b7d4..dc2b3336e6 100644 --- a/drivers/hwmon_ina219.c +++ b/drivers/hwmon_ina219.c @@ -463,18 +463,13 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - /* replace with a proper shutdown function */ + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/isbmex.c b/drivers/isbmex.c index c506826776..e9c32ece6d 100644 --- a/drivers/isbmex.c +++ b/drivers/isbmex.c @@ -403,10 +403,13 @@ int instcmd(const char *cmdname, const char *extra) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* FIXME: Check with the device what our instcmd * (nee upsdrv_shutdown() contents) actually does! */ - int ret = loop_shutdown_commands("shutdown.stayoff", NULL); + int ret = do_loop_shutdown_commands("shutdown.stayoff", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/ivtscd.c b/drivers/ivtscd.c index a747bbd65a..01575fe6ca 100644 --- a/drivers/ivtscd.c +++ b/drivers/ivtscd.c @@ -183,13 +183,8 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ /* FIXME: This driver (and solar device?) does not seem to * really support a shutdown. It also blocks in this method diff --git a/drivers/liebert-esp2.c b/drivers/liebert-esp2.c index f76992e34c..08e3e1b0cd 100644 --- a/drivers/liebert-esp2.c +++ b/drivers/liebert-esp2.c @@ -553,15 +553,10 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - char reply[8]; + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + char reply[8]; if(!(do_command(cmd_setOutOffMode, reply, 8) != -1) && (do_command(cmd_setOutOffDelay, reply, 8) != -1) && diff --git a/drivers/liebert-gxe.c b/drivers/liebert-gxe.c index 6e853a3ba2..0412070625 100644 --- a/drivers/liebert-gxe.c +++ b/drivers/liebert-gxe.c @@ -511,13 +511,8 @@ void upsdrv_initups(void) void upsdrv_shutdown(void) { - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ /* FIXME: There seems to be instcmd(load.off), why not that? */ upslogx(LOG_INFO, "Liebert GXE UPS can't fully shutdown, NOOP"); diff --git a/drivers/liebert.c b/drivers/liebert.c index b96777199b..67710c5abc 100644 --- a/drivers/liebert.c +++ b/drivers/liebert.c @@ -43,18 +43,13 @@ upsdrv_info_t upsdrv_info = { void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* XXX: replace with a proper shutdown function (raise DTR) */ /* worse yet: stock cables don't support shutdown at all */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } - upslogx(LOG_ERR, "shutdown not supported"); if (handling_upsdrv_shutdown > 0) set_exit_flag(EF_EXIT_FAILURE); diff --git a/drivers/macosx-ups.c b/drivers/macosx-ups.c index 5046f2cb6a..de2e4f73a2 100644 --- a/drivers/macosx-ups.c +++ b/drivers/macosx-ups.c @@ -264,6 +264,9 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ /* maybe try to detect the UPS here, but try a shutdown even if @@ -273,16 +276,10 @@ void upsdrv_shutdown(void) for monitoring and notification purposes. Still, there is a key that might be useful to set in SystemConfiguration land. */ - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ diff --git a/drivers/masterguard.c b/drivers/masterguard.c index 1fff1664e7..49c1c4dc31 100644 --- a/drivers/masterguard.c +++ b/drivers/masterguard.c @@ -569,7 +569,10 @@ int instcmd(const char *cmdname, const char *extra) ********************************************************************/ void upsdrv_shutdown(void) { - int ret = loop_shutdown_commands("shutdown.return", NULL); + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + + int ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/metasys.c b/drivers/metasys.c index ec86b6e8d7..dc3ead8969 100644 --- a/drivers/metasys.c +++ b/drivers/metasys.c @@ -868,15 +868,10 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - unsigned char command[10], answer[10]; + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + unsigned char command[10], answer[10]; /* Ensure that the ups is configured for automatically restart after a complete battery discharge diff --git a/drivers/mge-utalk.c b/drivers/mge-utalk.c index 1bf2edf19f..5ac45e5d3e 100644 --- a/drivers/mge-utalk.c +++ b/drivers/mge-utalk.c @@ -475,10 +475,11 @@ void upsdrv_updateinfo(void) /* --------------------------------------------------------------- */ -/* See comment in method below */ -static char handling_instcmd_shutdown = 0; void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + char buf[BUFFLEN]; /* static time_t lastcmd = 0; */ @@ -489,33 +490,9 @@ void upsdrv_shutdown(void) */ static char already_shutting_down = 0; - if (!handling_instcmd_shutdown - && !already_shutting_down - && device_sdcommands - ) { - char *cmdstr = NULL; - int cmdret = -1; - - already_shutting_down = 1; - handling_instcmd_shutdown = -1; - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - cmdret = loop_shutdown_commands(NULL, &cmdstr); - if (cmdret != STAT_INSTCMD_HANDLED) { - upslogx(LOG_WARNING, "Failed to command the UPS to '%s'", NUT_STRARG(cmdstr)); - if (cmdstr) - free(cmdstr); - } - - /* Reset the flags if not going down */ - already_shutting_down = 0; - handling_instcmd_shutdown = 0; + if (already_shutting_down) return; - } - /* just in case */ + already_shutting_down = 1; /* Here we are if handling explicit INSTCMD to shut down, @@ -542,9 +519,13 @@ void upsdrv_shutdown(void) } /* if(strcmp(buf, "OK")) */ - /* FIXME: Should the UPS shutdown mean the driver shutdown? */ - /* call the cleanup to disable/close the comm link */ - upsdrv_cleanup(); + if (handling_upsdrv_shutdown > 0) { + /* call the cleanup to disable/close the comm link */ + upsdrv_cleanup(); + } else { + /* Reset the flags if driver is not going down */ + already_shutting_down = 0; + } } /* --------------------------------------------------------------- */ @@ -590,16 +571,12 @@ int instcmd(const char *cmdname, const char *extra) if (!strcasecmp(cmdname, "shutdown.stayoff")) { sdtype = SD_STAYOFF; - if (!handling_instcmd_shutdown) - handling_instcmd_shutdown = 1; upsdrv_shutdown(); } if (!strcasecmp(cmdname, "shutdown.return")) { sdtype = SD_RETURN; - if (!handling_instcmd_shutdown) - handling_instcmd_shutdown = 1; upsdrv_shutdown(); } diff --git a/drivers/microdowell.c b/drivers/microdowell.c index ef4de017d1..7ce650fc99 100644 --- a/drivers/microdowell.c +++ b/drivers/microdowell.c @@ -944,19 +944,14 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + unsigned char OutBuff[20]; unsigned char InpBuff[260]; unsigned char *p; unsigned char BatteryFlag = 0; - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } - OutBuff[0] = CMD_GET_STATUS ; /* get UPS status */ if ((p = CmdSerial(OutBuff, LEN_GET_STATUS, InpBuff)) != NULL) { diff --git a/drivers/microsol-common.c b/drivers/microsol-common.c index 6a1aa13d0a..4eab5d4f71 100644 --- a/drivers/microsol-common.c +++ b/drivers/microsol-common.c @@ -754,13 +754,8 @@ void upsdrv_updateinfo(void) */ void upsdrv_shutdown(void) { - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ if (!line_unpowered) { /* on line */ upslogx(LOG_NOTICE, "On line, sending power cycle command..."); diff --git a/drivers/netxml-ups.c b/drivers/netxml-ups.c index 84dd54741f..ee4e4f6897 100644 --- a/drivers/netxml-ups.c +++ b/drivers/netxml-ups.c @@ -394,6 +394,9 @@ void upsdrv_updateinfo(void) } void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* * WARNING: * This driver will probably never support this properly: @@ -407,6 +410,11 @@ void upsdrv_shutdown(void) { * used for these devices. */ + /* FIXME: Make a name for default original shutdown + * in particular to make it one of the options and + * call protocol cleanup below, if needed. + */ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ /* maybe try to detect the UPS here, but try a shutdown even if @@ -427,17 +435,6 @@ void upsdrv_shutdown(void) { object_query_t *resp = NULL; object_query_t *req = NULL; - /* FIXME: Make a name for default original shutdown - * in particular to make it one of the options and - * call protocol cleanup below, if needed. - */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } - /* Pragmatic do { ... } while (0) loop allowing break to cleanup */ do { /* Create SET_OBJECT request */ diff --git a/drivers/nut-ipmipsu.c b/drivers/nut-ipmipsu.c index ce84db1bc8..9e978ee845 100644 --- a/drivers/nut-ipmipsu.c +++ b/drivers/nut-ipmipsu.c @@ -145,6 +145,9 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* * WARNING: * This driver will probably never support this properly: @@ -159,17 +162,9 @@ void upsdrv_shutdown(void) */ /* replace with a proper shutdown function */ - - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } /* diff --git a/drivers/nutdrv_atcl_usb.c b/drivers/nutdrv_atcl_usb.c index ba46a6ea64..2bb69a60fb 100644 --- a/drivers/nutdrv_atcl_usb.c +++ b/drivers/nutdrv_atcl_usb.c @@ -713,10 +713,13 @@ int instcmd(const char *cmdname, const char *extra) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* FIXME: Check with the device what our instcmd * (nee upsdrv_shutdown() contents) actually does! */ - int ret = loop_shutdown_commands("shutdown.stayoff", NULL); + int ret = do_loop_shutdown_commands("shutdown.stayoff", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 41b5c21b1b..46ee43b725 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -2769,22 +2769,18 @@ int setvar(const char *varname, const char *val) /* Try to shutdown the UPS */ void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + int retry; item_t *item; const char *val; upsdebugx(1, "%s...", __func__); - /* FIXME: Make a name for default original shutdown - * and note this common "sdcommands" feature can + /* FIXME: Use common "sdcommands" feature to * replace tunables used below ("stayoff" etc). */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } /* Get user-defined delays */ diff --git a/drivers/nutdrv_siemens_sitop.c b/drivers/nutdrv_siemens_sitop.c index ff91f4727a..0029ed0eb0 100644 --- a/drivers/nutdrv_siemens_sitop.c +++ b/drivers/nutdrv_siemens_sitop.c @@ -248,9 +248,12 @@ void upsdrv_updateinfo(void) { } void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* by default, tell the UPS to shut down, * then return - DO NOT SLEEP HERE */ - int ret = loop_shutdown_commands("shutdown.return", NULL); + int ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/oneac.c b/drivers/oneac.c index cdf6f9dbaa..a534ddbff9 100644 --- a/drivers/oneac.c +++ b/drivers/oneac.c @@ -814,6 +814,9 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* FIXME: before loop_shutdown_commands(), code directly called * here was identical to "shutdown.reboot", while the driver * shutdown should more reasonably be "shutdown.return" (when @@ -821,7 +824,7 @@ void upsdrv_shutdown(void) * implemented in instcmd() here nominally (not sure if named * correctly - better re-check on hardware). */ - int ret = loop_shutdown_commands("shutdown.reboot", NULL); + int ret = do_loop_shutdown_commands("shutdown.reboot", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/optiups.c b/drivers/optiups.c index e5a711e400..4fe2cbb1ec 100644 --- a/drivers/optiups.c +++ b/drivers/optiups.c @@ -537,22 +537,16 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ /* If get no response, assume on battery & battery low */ long s = OPTISBIT_ON_BATTERY_POWER | OPTISBIT_LOW_BATTERY; - ssize_t r; - - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } - r = optiquery( "AG" ); + ssize_t r = optiquery( "AG" ); if ( r < 1 ) { upslogx(LOG_ERR, "can't retrieve ups status during shutdown" ); diff --git a/drivers/phoenixcontact_modbus.c b/drivers/phoenixcontact_modbus.c index 0f6937d777..f7effa0a0b 100644 --- a/drivers/phoenixcontact_modbus.c +++ b/drivers/phoenixcontact_modbus.c @@ -133,6 +133,9 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* * WARNING: When using RTU TCP, this driver will probably * never support shutdowns properly, except on some systems: @@ -147,17 +150,9 @@ void upsdrv_shutdown(void) */ /* replace with a proper shutdown function */ - - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index 7e72672bcf..0ac56c8fb6 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -851,10 +851,13 @@ int instcmd(const char *cmdname, const char *extra) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* FIXME: Check with the device what our instcmd * (nee upsdrv_shutdown() contents) actually does! */ - int ret = loop_shutdown_commands("shutdown.stayoff", NULL); + int ret = do_loop_shutdown_commands("shutdown.stayoff", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/powercom.c b/drivers/powercom.c index 89fe549667..752a2a2198 100644 --- a/drivers/powercom.c +++ b/drivers/powercom.c @@ -840,6 +840,9 @@ void upsdrv_updateinfo(void) /* shutdown UPS */ void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + int ret = -1; if (!device_sdcommands) { @@ -847,7 +850,7 @@ void upsdrv_shutdown(void) printf("Forced UPS shutdown (and wait for power)...\n"); } - ret = loop_shutdown_commands("shutdown.return", NULL); + ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/powerman-pdu.c b/drivers/powerman-pdu.c index bfaf5a958b..348c67a20c 100644 --- a/drivers/powerman-pdu.c +++ b/drivers/powerman-pdu.c @@ -135,6 +135,9 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* * WARNING: * This driver will probably never support this properly: @@ -149,20 +152,12 @@ void upsdrv_shutdown(void) */ /* replace with a proper shutdown function */ - - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - /* FIXME: shutdown all outlets? */ - /* OL: this must power cycle the load if possible */ - /* OB: the load must remain off until the power returns */ - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + /* FIXME: shutdown all outlets? */ + /* OL: this must power cycle the load if possible */ + /* OB: the load must remain off until the power returns */ + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } /* diff --git a/drivers/powerpanel.c b/drivers/powerpanel.c index a555b179e9..2d8c212781 100644 --- a/drivers/powerpanel.c +++ b/drivers/powerpanel.c @@ -102,15 +102,10 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - int i, ret = -1; + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + int i, ret = -1; /* * Try to shutdown with delay and automatic reboot if the power diff --git a/drivers/rhino.c b/drivers/rhino.c index e268a08093..60346ed806 100644 --- a/drivers/rhino.c +++ b/drivers/rhino.c @@ -749,16 +749,8 @@ void upsdrv_updateinfo(void) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - /* FIXME: Make a name for default original shutdown; - * note that currently it just does "shutdown.stayoff" - * in both cases, just with different logged messages. - */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ /* basic idea: find out line status and send appropriate command */ /* on line: send normal shutdown, ups will return by itself on utility */ diff --git a/drivers/richcomm_usb.c b/drivers/richcomm_usb.c index 7977ae8a55..77b3ceeab8 100644 --- a/drivers/richcomm_usb.c +++ b/drivers/richcomm_usb.c @@ -775,10 +775,13 @@ int instcmd(const char *cmdname, const char *extra) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* FIXME: Check with the device what our instcmd * (nee upsdrv_shutdown() contents) actually does! */ - int ret = loop_shutdown_commands("shutdown.return", NULL); + int ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/riello_ser.c b/drivers/riello_ser.c index 8af39b1ddd..59bae583cd 100644 --- a/drivers/riello_ser.c +++ b/drivers/riello_ser.c @@ -1163,17 +1163,12 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ int retry; - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } - /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c index e16246355d..a56f27757b 100644 --- a/drivers/riello_usb.c +++ b/drivers/riello_usb.c @@ -1134,17 +1134,12 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ int retry; - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } - /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ diff --git a/drivers/safenet.c b/drivers/safenet.c index fbb28026d5..ac5a6b0e14 100644 --- a/drivers/safenet.c +++ b/drivers/safenet.c @@ -419,15 +419,10 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - int retry = 3; + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + int retry = 3; /* * Since we may have arrived here before the hardware is initialized, diff --git a/drivers/skel.c b/drivers/skel.c index d79c665491..595e4dc0d1 100644 --- a/drivers/skel.c +++ b/drivers/skel.c @@ -102,6 +102,9 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ /* maybe try to detect the UPS here, but try a shutdown even if @@ -109,16 +112,9 @@ void upsdrv_shutdown(void) /* replace with a proper shutdown function */ - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ @@ -131,6 +127,11 @@ void upsdrv_shutdown(void) /* static int instcmd(const char *cmdname, const char *extra) { + if (!strcasecmp(cmdname, "shutdown.default")) { + // optional custom implementation - but we + // prefer to code upsdrv_shutdown() directly + } + if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; diff --git a/drivers/sms_ser.c b/drivers/sms_ser.c index 07a210ed43..9a96b6f294 100644 --- a/drivers/sms_ser.c +++ b/drivers/sms_ser.c @@ -514,17 +514,12 @@ void upsdrv_updateinfo(void) { } void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ int retry; - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } - /* maybe try to detect the UPS here, but try a shutdown even if * it doesn't respond at first if possible */ diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index d159d13336..a4a4090ff3 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -341,6 +341,9 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + /* * WARNING: * This driver will probably never support this properly: @@ -366,7 +369,7 @@ void upsdrv_shutdown(void) * - If the above doesn't work, try load.off.delay * - Finally, try shutdown.stayoff */ - if (loop_shutdown_commands("shutdown.return,shutdown.reboot,load.off.delay,shutdown.stayoff", &cmd_used) == STAT_INSTCMD_HANDLED) { + if (do_loop_shutdown_commands("shutdown.return,shutdown.reboot,load.off.delay,shutdown.stayoff", &cmd_used) == STAT_INSTCMD_HANDLED) { upslogx(LOG_INFO, "Shutdown successful with '%s'", NUT_STRARG(cmd_used)); if (handling_upsdrv_shutdown > 0) set_exit_flag(EF_EXIT_SUCCESS); diff --git a/drivers/socomec_jbus.c b/drivers/socomec_jbus.c index 7268e9b4b8..075283449e 100644 --- a/drivers/socomec_jbus.c +++ b/drivers/socomec_jbus.c @@ -428,18 +428,13 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - /* replace with a proper shutdown function */ + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* NOTE: User-provided commands may be something other - * than actual shutdown, e.g. a beeper to test that the - * INSTCMD happened such and when expected without - * impacting the load fed by the UPS. - */ - if (loop_shutdown_commands(NULL, NULL) != STAT_INSTCMD_HANDLED) { - upslogx(LOG_ERR, "shutdown not supported"); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(EF_EXIT_FAILURE); - } + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + if (handling_upsdrv_shutdown > 0) + set_exit_flag(EF_EXIT_FAILURE); } void upsdrv_help(void) diff --git a/drivers/solis.c b/drivers/solis.c index d7f93d58c5..e4f92acbf4 100644 --- a/drivers/solis.c +++ b/drivers/solis.c @@ -962,13 +962,8 @@ void upsdrv_updateinfo(void) { * - on line: send shutdown+return, UPS will cycle and return soon. */ void upsdrv_shutdown(void) { - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ if (!SourceFail) { /* on line */ upslogx(LOG_NOTICE, "On line, sending shutdown+return command...\n"); diff --git a/drivers/tripplite.c b/drivers/tripplite.c index 3d330b3b54..299fb5fd7d 100644 --- a/drivers/tripplite.c +++ b/drivers/tripplite.c @@ -378,7 +378,10 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { - int ret = loop_shutdown_commands("shutdown.return", NULL); + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + + int ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index e71b04d9a6..d886c91e20 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -1261,7 +1261,10 @@ void upsdrv_initinfo(void) void upsdrv_shutdown(void) { - int ret = loop_shutdown_commands("shutdown.return", NULL); + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + + int ret = do_loop_shutdown_commands("shutdown.return", NULL); if (handling_upsdrv_shutdown > 0) set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); } diff --git a/drivers/tripplitesu.c b/drivers/tripplitesu.c index 0262203e1b..d849df2b99 100644 --- a/drivers/tripplitesu.c +++ b/drivers/tripplitesu.c @@ -846,15 +846,10 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - char parm[20]; + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + char parm[20]; if (!init_comm()) printf("Status failed. Assuming it's on battery and trying a shutdown anyway.\n"); diff --git a/drivers/upscode2.c b/drivers/upscode2.c index aa8d8e62e3..f1d4ae16e3 100644 --- a/drivers/upscode2.c +++ b/drivers/upscode2.c @@ -876,13 +876,8 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ upslogx(LOG_EMERG, "Shutting down..."); diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 16c4bb8fee..b9d563e1c3 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -987,6 +987,9 @@ int setvar(const char *varname, const char *val) void upsdrv_shutdown(void) { + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ + char *cmd_used = NULL; upsdebugx(1, "%s...", __func__); @@ -997,7 +1000,7 @@ void upsdrv_shutdown(void) * - If the above doesn't work, try load.off.delay * - Finally, try shutdown.stayoff */ - if (loop_shutdown_commands("shutdown.return,shutdown.reboot,load.off.delay,shutdown.stayoff", &cmd_used) == STAT_INSTCMD_HANDLED) { + if (do_loop_shutdown_commands("shutdown.return,shutdown.reboot,load.off.delay,shutdown.stayoff", &cmd_used) == STAT_INSTCMD_HANDLED) { upslogx(LOG_INFO, "Shutdown successful with '%s'", NUT_STRARG(cmd_used)); if (handling_upsdrv_shutdown > 0) set_exit_flag(EF_EXIT_SUCCESS); diff --git a/drivers/victronups.c b/drivers/victronups.c index 12e7578654..10425d3fb8 100644 --- a/drivers/victronups.c +++ b/drivers/victronups.c @@ -498,13 +498,8 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - /* FIXME: Make a name for default original shutdown */ - if (device_sdcommands) { - int ret = loop_shutdown_commands(NULL, NULL); - if (handling_upsdrv_shutdown > 0) - set_exit_flag(ret == STAT_INSTCMD_HANDLED ? EF_EXIT_SUCCESS : EF_EXIT_FAILURE); - return; - } + /* Only implement "shutdown.default"; do not invoke + * general handling of other `sdcommands` here */ ser_send(upsfd, "vCc0!%c", ENDCHAR); usleep(UPS_DELAY); From d0b42c4ae405f5d8ad6f12bb863fc596eab52177 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 21:18:53 +0100 Subject: [PATCH 087/144] drivers/main.c: move do_forceshutdown handling to be after upsdrv_initinfo() where we register INSTCMD handlers [#2670] Signed-off-by: Jim Klimov --- drivers/main.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index d4d5be067f..0d854d810f 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -1017,7 +1017,9 @@ int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { "due to socket protocol request", NUT_STRARG(upsname)); if (handling_upsdrv_shutdown == 0) handling_upsdrv_shutdown = 1; + dstate_setinfo("driver.state", "fsd.killpower"); upsdrv_shutdown_sdcommands_or_default(NULL, NULL); + dstate_setinfo("driver.state", "quiet"); return STAT_INSTCMD_HANDLED; } else { upslogx(LOG_WARNING, "Got socket protocol request for UPS [%s] " @@ -2775,9 +2777,6 @@ int main(int argc, char **argv) fatalx(EXIT_FAILURE, "Fatal error: broken driver. It probably needs to be converted.\n"); } - if (do_forceshutdown) - forceshutdown(); - /* publish the top-level data: version numbers, driver name */ dstate_setinfo("driver.version", "%s", UPS_VERSION); dstate_setinfo("driver.version.internal", "%s", upsdrv_info.version); @@ -2798,6 +2797,11 @@ int main(int argc, char **argv) /* Register a way to call upsdrv_shutdown() among `sdcommands` */ dstate_addcmd("shutdown.default"); + if (do_forceshutdown) { + dstate_setinfo("driver.state", "fsd.killpower"); + forceshutdown(); + } + /* Note: a few drivers also call their upsdrv_updateinfo() during * their upsdrv_initinfo(), possibly to impact the initialization */ dstate_setinfo("driver.state", "init.updateinfo"); From fbef5c5fcd585225d20c89bf79dbe82062cd4faa Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 21:20:33 +0100 Subject: [PATCH 088/144] Revert "drivers/main.{c,h}, drivers/dstate.c: introduce main_instcmd_fallback() so drivers might implement their own "shutdown.default" at will [#2670]" This reverts commit 25250b02aea4e31ba20a17a33e6cf22cfaf27135: too messy in logs where driver.instcmd() think they are last in stack. Signed-off-by: Jim Klimov --- drivers/dstate.c | 25 +++++++------------------ drivers/main.c | 28 ++-------------------------- drivers/main.h | 4 ---- drivers/skel.c | 5 ----- 4 files changed, 9 insertions(+), 53 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 46a3bfb80e..2b36688291 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -847,26 +847,15 @@ static int sock_arg(conn_t *conn, size_t numarg, char **arg) if (upsh.instcmd) { ret = upsh.instcmd(cmdname, cmdparam); - if (ret != STAT_INSTCMD_UNKNOWN) { - /* send back execution result if requested */ - if (cmdid) - send_tracking(conn, cmdid, ret); - - /* The command was handled, status is a separate consideration */ - return 1; - } /* else try other handler(s) */ - } /* else try other handler(s) */ + /* send back execution result if requested */ + if (cmdid) + send_tracking(conn, cmdid, ret); - /* Finally try the fallback handler shared by all drivers */ - ret = main_instcmd_fallback(cmdname, cmdparam, conn); - /* send back execution result if requested */ - if (cmdid) - send_tracking(conn, cmdid, ret); + /* The command was handled, status is a separate consideration */ + return 1; + } - if (ret == STAT_INSTCMD_UNKNOWN) - upslogx(LOG_NOTICE, - "Got INSTCMD '%s', but driver lacks a handler", - NUT_STRARG(cmdname)); + upslogx(LOG_NOTICE, "Got INSTCMD, but driver lacks a handler"); return 1; } diff --git a/drivers/main.c b/drivers/main.c index 0d854d810f..bc2eda3691 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -947,9 +947,8 @@ int upsdrv_shutdown_sdcommands_or_default(const char *sdcmds_default, char **cmd return sdret; } -/* handle instant commands common for all drivers - fallback for common - * command names that could be implemented in a driver but were not */ -int main_instcmd_fallback(const char *cmdname, const char *extra, conn_t *conn) { +/* handle instant commands common for all drivers */ +int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { char buf[SMALLBUF]; if (conn) #ifndef WIN32 @@ -981,29 +980,6 @@ int main_instcmd_fallback(const char *cmdname, const char *extra, conn_t *conn) return STAT_INSTCMD_HANDLED; } - /* By default, the driver-specific values are - * unknown to shared standard handler */ - upsdebugx(2, "shared %s() does not handle command %s, " - "proceeding to driver-specific handler", - __func__, cmdname); - return STAT_INSTCMD_UNKNOWN; -} - -/* handle instant commands common for all drivers */ -int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { - char buf[SMALLBUF]; - if (conn) -#ifndef WIN32 - snprintf(buf, sizeof(buf), "socket %d", conn->fd); -#else - snprintf(buf, sizeof(buf), "handle %p", conn->fd); -#endif - else - snprintf(buf, sizeof(buf), "(null)"); - - upsdebugx(2, "entering main_instcmd(%s, %s) for [%s] on %s", - cmdname, extra, NUT_STRARG(upsname), buf); - if (!strcmp(cmdname, "driver.killpower")) { /* An implementation of `drivername -k` requested from * the running and connected driver instance by protocol diff --git a/drivers/main.h b/drivers/main.h index 249a2b1202..864eb11b7a 100644 --- a/drivers/main.h +++ b/drivers/main.h @@ -63,10 +63,6 @@ int upsdrv_shutdown_sdcommands_or_default(const char *sdcmds_default, char **cmd */ int main_instcmd(const char *cmdname, const char *extra, conn_t *conn); -/* handle instant commands common for all drivers - fallback for common - * command names that could be implemented in a driver but were not */ -int main_instcmd_fallback(const char *cmdname, const char *extra, conn_t *conn); - /* handle setting variables common for all drivers * (returns STAT_SET_* state values per enum in upshandler.h) */ diff --git a/drivers/skel.c b/drivers/skel.c index 595e4dc0d1..3cb620ab1c 100644 --- a/drivers/skel.c +++ b/drivers/skel.c @@ -127,11 +127,6 @@ void upsdrv_shutdown(void) /* static int instcmd(const char *cmdname, const char *extra) { - if (!strcasecmp(cmdname, "shutdown.default")) { - // optional custom implementation - but we - // prefer to code upsdrv_shutdown() directly - } - if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; From 1d92ded152b676992bb623fff50fb95019b58f24 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Nov 2024 22:28:31 +0100 Subject: [PATCH 089/144] drivers/main.c: do_loop_shutdown_commands(): handle "shutdown.default" as upsdrv_shutdown() in main loop too [#2670] Signed-off-by: Jim Klimov --- drivers/main.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/main.c b/drivers/main.c index bc2eda3691..f89142372c 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -807,6 +807,7 @@ int do_loop_shutdown_commands(const char *sdcmds, char **cmdused) { } if (upsh.instcmd == NULL) { + /* FIXME: support main_instcmd() too? */ upsdebugx(1, "This driver does not implement INSTCMD support"); /* ...but the default one we can short-circuit without @@ -837,7 +838,15 @@ int do_loop_shutdown_commands(const char *sdcmds, char **cmdused) { while ((s = strtok(s == NULL ? buf : NULL, ",")) != NULL) { if (!*s) continue; - if ((cmdret = upsh.instcmd(s, NULL)) == STAT_INSTCMD_HANDLED) { + + if (!strcmp(s, "shutdown.default")) { + upsdrv_shutdown(); + cmdret = STAT_INSTCMD_HANDLED; + } else { + cmdret = upsh.instcmd(s, NULL); + } + + if (cmdret == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ /* Note: If we are handling "shutdown.default" here, From 2c7d98bc90a10683759fe97170f838c7d25f8f41 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 2 Dec 2024 16:13:32 +0100 Subject: [PATCH 090/144] drivers/dstate.c: revise logging of "Got INSTCMD, but driver lacks a handler" [#2686] Signed-off-by: Jim Klimov --- drivers/dstate.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index f4132a829b..933231d979 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -855,7 +855,16 @@ static int sock_arg(conn_t *conn, size_t numarg, char **arg) return 1; } - upslogx(LOG_NOTICE, "Got INSTCMD, but driver lacks a handler"); + if (cmdparam) { + upslogx(LOG_NOTICE, + "Got INSTCMD '%s' '%s', but driver lacks a handler", + NUT_STRARG(cmdname), NUT_STRARG(cmdparam)); + } else { + upslogx(LOG_NOTICE, + "Got INSTCMD '%s', but driver lacks a handler", + NUT_STRARG(cmdname)); + } + return 1; } From 2072d834f5c581f42340f45657f71e1bebb56e38 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 2 Dec 2024 16:16:17 +0100 Subject: [PATCH 091/144] data/cmdvartab: document CMDDESC shutdown.default [#2686] Signed-off-by: Jim Klimov --- data/cmdvartab | 1 + 1 file changed, 1 insertion(+) diff --git a/data/cmdvartab b/data/cmdvartab index ba915cd114..e820d3e2c6 100644 --- a/data/cmdvartab +++ b/data/cmdvartab @@ -218,6 +218,7 @@ CMDDESC driver.reload-or-exit "Reload running driver configuration from the file CMDDESC load.off "Turn off the load immediately" CMDDESC load.on "Turn on the load immediately" +CMDDESC shutdown.default "Run the driver-defined UPS shutdown sequence (as opposed to user-configured 'sdcommands')" CMDDESC shutdown.return "Turn off the load and return when power is back" CMDDESC shutdown.stayoff "Turn off the load and remain off" CMDDESC shutdown.stop "Stop a shutdown in progress" From aaf254d48455422f7b8dad2364985b8cea7661f8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 2 Dec 2024 17:21:06 +0100 Subject: [PATCH 092/144] clients/ups{rw,cmd}.c: when reporting "OK" without "TRACKING", issue a debug message about its limited usefullness Signed-off-by: Jim Klimov --- clients/upscmd.c | 3 +++ clients/upsrw.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/clients/upscmd.c b/clients/upscmd.c index 868009bf53..8796677c8b 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -195,6 +195,9 @@ static void do_cmd(char **argv, const int argc) ) { /* reply as usual */ fprintf(stderr, "%s\n", buf); + upsdebugx(1, "%s: 'OK' only means the NUT data server accepted the request as valid, " + "but as we did not wait for result, we do not know if it was handled in fact.", + __func__); return; } diff --git a/clients/upsrw.c b/clients/upsrw.c index 710d39d51e..4bdc04f773 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -121,6 +121,9 @@ static void do_set(const char *varname, const char *newval) ) { /* reply as usual */ fprintf(stderr, "%s\n", buf); + upsdebugx(1, "%s: 'OK' only means the NUT data server accepted the request as valid, " + "but as we did not wait for result, we do not know if it was handled in fact.", + __func__); return; } From 35edb4f5d8b824cef72801587384788469aa70fa Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 2 Dec 2024 17:30:26 +0100 Subject: [PATCH 093/144] clients/ups{rw,cmd}.c: enable use of NUT_DEBUG_LEVEL envvar Signed-off-by: Jim Klimov --- clients/upscmd.c | 15 +++++++++++++-- clients/upsrw.c | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/clients/upscmd.c b/clients/upscmd.c index 8796677c8b..3745014906 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -285,9 +285,20 @@ int main(int argc, char **argv) uint16_t port; ssize_t ret; int have_un = 0, have_pw = 0, cmdlist = 0; - char buf[SMALLBUF * 2], username[SMALLBUF], password[SMALLBUF]; + char buf[SMALLBUF * 2], username[SMALLBUF], password[SMALLBUF], *s = NULL; const char *prog = xbasename(argv[0]); + /* NOTE: Caller must `export NUT_DEBUG_LEVEL` to see debugs for upsc + * and NUT methods called from it. This line aims to just initialize + * the subsystem, and set initial timestamp. Debugging the client is + * primarily of use to developers, so is not exposed via `-D` args. + */ + s = getenv("NUT_DEBUG_LEVEL"); + if (s && str_to_int(s, &i, 10) && i > 0) { + nut_debug_level = i; + } + upsdebugx(1, "Starting NUT client: %s", prog); + while ((i = getopt(argc, argv, "+lhu:p:t:wV")) != -1) { switch (i) @@ -462,6 +473,6 @@ int main(int argc, char **argv) /* Formal do_upsconf_args implementation to satisfy linker on AIX */ #if (defined NUT_PLATFORM_AIX) void do_upsconf_args(char *upsname, char *var, char *val) { - fatalx(EXIT_FAILURE, "INTERNAL ERROR: formal do_upsconf_args called"); + fatalx(EXIT_FAILURE, "INTERNAL ERROR: formal do_upsconf_args called"); } #endif /* end of #if (defined NUT_PLATFORM_AIX) */ diff --git a/clients/upsrw.c b/clients/upsrw.c index 4bdc04f773..892e6825e3 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -644,7 +644,18 @@ int main(int argc, char **argv) int i; uint16_t port; const char *prog = xbasename(argv[0]); - char *password = NULL, *username = NULL, *setvar = NULL; + char *password = NULL, *username = NULL, *setvar = NULL, *s = NULL; + + /* NOTE: Caller must `export NUT_DEBUG_LEVEL` to see debugs for upsc + * and NUT methods called from it. This line aims to just initialize + * the subsystem, and set initial timestamp. Debugging the client is + * primarily of use to developers, so is not exposed via `-D` args. + */ + s = getenv("NUT_DEBUG_LEVEL"); + if (s && str_to_int(s, &i, 10) && i > 0) { + nut_debug_level = i; + } + upsdebugx(1, "Starting NUT client: %s", prog); while ((i = getopt(argc, argv, "+hls:p:t:u:wV")) != -1) { switch (i) @@ -720,6 +731,6 @@ int main(int argc, char **argv) /* Formal do_upsconf_args implementation to satisfy linker on AIX */ #if (defined NUT_PLATFORM_AIX) void do_upsconf_args(char *upsname, char *var, char *val) { - fatalx(EXIT_FAILURE, "INTERNAL ERROR: formal do_upsconf_args called"); + fatalx(EXIT_FAILURE, "INTERNAL ERROR: formal do_upsconf_args called"); } #endif /* end of #if (defined NUT_PLATFORM_AIX) */ From a9b75b1be66610d83e38508ec1597ab87d7da787 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 2 Dec 2024 18:04:42 +0100 Subject: [PATCH 094/144] drivers/upsdrvquery.c, NEWS.adoc: upsdrvquery_read_timeout(): fix private use of timeval={-1,-1} as select(..., NULL) for indefinite wait [#1922, #2392, #2686, #2670] Signed-off-by: Jim Klimov --- NEWS.adoc | 6 ++++++ docs/nut.dict | 4 +++- drivers/upsdrvquery.c | 7 ++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 37db27e3b0..796ee1e294 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -71,6 +71,12 @@ https://github.com/networkupstools/nut/milestone/11 a `*_s()` variant was available was not handled correctly. [PR #2583] * A recently introduced `allow_killpower` did not actually work as an `ups.conf` flag (only as a protocol command). [issue #2605, PR #2606] + * The ability of two copies of the driver program to talk to each other + with `upsdrvquery.c` code was not complete for the case of indefinite + `select()` wait timeout. Now `upsdrvquery_read_timeout()` fixed private + use of `struct timeval={-1,-1}` as a trigger to `select(..., NULL)`, + as logged in one part of code and not handled in the other, for the + indefinite wait [#1922, #2392, #2686, #2670] - development iterations of NUT should now identify with not only the semantic version of a preceding release, but with git-derived information about the diff --git a/docs/nut.dict b/docs/nut.dict index 1617c0b1ef..8d37d7eb73 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 3247 utf-8 +personal_ws-1.1 en 3249 utf-8 AAC AAS ABI @@ -2977,6 +2977,7 @@ timehead timername timestamp timeticks +timeval tios tmp tmpfiles @@ -3075,6 +3076,7 @@ upsdebugx upsdev upsdrv upsdrvctl +upsdrvquery upsdrvsvcctl upserror upsfetch diff --git a/drivers/upsdrvquery.c b/drivers/upsdrvquery.c index 31bb8e94e1..2c4a767bbb 100644 --- a/drivers/upsdrvquery.c +++ b/drivers/upsdrvquery.c @@ -238,6 +238,11 @@ ssize_t upsdrvquery_read_timeout(udq_pipe_conn_t *conn, struct timeval tv) { struct timeval start, now, presleep; #endif + upsdebugx(5, "%s: tv={sec=%" PRIiMAX ", usec=%06" PRIiMAX "}%s", + __func__, (intmax_t)tv.tv_sec, (intmax_t)tv.tv_usec, + tv.tv_sec < 0 || tv.tv_usec < 0 ? " (unlimited timeout)" : "" + ); + if (!conn || INVALID_FD(conn->sockfd)) { if (nut_debug_level > 0 || nut_upsdrvquery_debug_level >= NUT_UPSDRVQUERY_DEBUG_LEVEL_CONNECT) upslog_with_errno(LOG_ERR, "socket not initialized"); @@ -248,7 +253,7 @@ ssize_t upsdrvquery_read_timeout(udq_pipe_conn_t *conn, struct timeval tv) { FD_ZERO(&rfds); FD_SET(conn->sockfd, &rfds); - if (select(conn->sockfd + 1, &rfds, NULL, NULL, &tv) < 0) { + if (select(conn->sockfd + 1, &rfds, NULL, NULL, tv.tv_sec < 0 || tv.tv_usec < 0 ? NULL : &tv) < 0) { if (nut_debug_level > 0 || nut_upsdrvquery_debug_level >= NUT_UPSDRVQUERY_DEBUG_LEVEL_DIALOG) upslog_with_errno(LOG_ERR, "select with socket"); /* upsdrvquery_close(conn); */ From 7f314af34bfa4f59d373885eee6f60fbcd57b8ed Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 3 Dec 2024 10:41:50 +0100 Subject: [PATCH 095/144] clients/upsmon.{c,h}, docs, conf: apply suggestions from issue #415 discussion adding to PR #2658 changes Previously merged-in the couple of commits from issue https://github.com/networkupstools/nut/issues/415 proposal at https://github.com/networkupstools/nut/tree/upsmon_alarm for posterity, now cherry-picking some changes and further discussion ideas from mailing list thread at http://lists.alioth.debian.org/pipermail/nut-upsuser/2017-April/010591.html Closes: #415 Signed-off-by: Jim Klimov --- clients/upsmon.c | 4 +++- clients/upsmon.h | 3 ++- conf/upsmon.conf.sample.in | 3 ++- docs/man/upsmon.conf.txt | 2 +- docs/man/upsmon.txt | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 647418c4ba..dce985a0a4 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -3,6 +3,7 @@ Copyright (C) 1998 Russell Kroll 2012 Arnaud Quette + 2017 Eaton (author: Arnaud Quette ) 2020-2024 Jim Klimov This program is free software; you can redistribute it and/or modify @@ -756,7 +757,8 @@ static void ups_is_noteco(utype_t *ups) static void ups_is_alarm(utype_t *ups) { - if (flag_isset(ups->status, ST_ALARM)) { /* no change */ + if (flag_isset(ups->status, ST_ALARM)) { /* potentially no change */ + /* TODO: add device.alarm.count */ upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); return; } diff --git a/clients/upsmon.h b/clients/upsmon.h index ec68bb1a07..47b1249b4f 100644 --- a/clients/upsmon.h +++ b/clients/upsmon.h @@ -3,6 +3,7 @@ Copyright (C) 2000 Russell Kroll 2012 Arnaud Quette + 2017 Eaton (author: Arnaud Quette ) 2020-2024 Jim Klimov This program is free software; you can redistribute it and/or modify @@ -165,7 +166,7 @@ static struct { { NOTIFY_NOTBYPASS,"NOTBYPASS",NULL, "UPS %s: no longer on bypass", NOTIFY_DEFAULT }, { NOTIFY_ECO, "ECO", NULL, "UPS %s: in ECO mode (as defined by vendor)", NOTIFY_DEFAULT }, { NOTIFY_NOTECO, "NOTECO", NULL, "UPS %s: no longer in ECO mode", NOTIFY_DEFAULT }, - { NOTIFY_ALARM, "ALARM", NULL, "UPS %s is in an alarm state (has active alarms)", NOTIFY_DEFAULT }, + { NOTIFY_ALARM, "ALARM", NULL, "UPS %s: one or more active alarms (check ups.alarm)", NOTIFY_DEFAULT }, { NOTIFY_NOTALARM, "NOTALARM", NULL, "UPS %s is no longer in an alarm state (no active alarms)", NOTIFY_DEFAULT }, { NOTIFY_SUSPEND_STARTING, "SUSPEND_STARTING", NULL, "OS is entering sleep/suspend/hibernate mode", NOTIFY_DEFAULT }, diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index e5d7235859..f75a5104cc 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -334,7 +334,7 @@ POWERDOWNFLAG "@POWERDOWNFLAG@" # NOTIFYMSG NOTBYPASS "UPS %s: no longer on bypass" # NOTIFYMSG ECO "UPS %s: in ECO mode (as defined by vendor)" # NOTIFYMSG NOTECO "UPS %s: no longer in ECO mode (as defined by vendor)" -# NOTIFYMSG ALARM "UPS %s is in an alarm state (has active alarms)" +# NOTIFYMSG ALARM "UPS %s: one or more active alarms (check ups.alarm)" # NOTIFYMSG NOTALARM "UPS %s is no longer in an alarm state (no active alarms)" # # A few messages not directly related to UPS events are also available: @@ -356,6 +356,7 @@ POWERDOWNFLAG "@POWERDOWNFLAG@" # REPLBATT : The UPS battery is bad and needs to be replaced # NOCOMM : A UPS is unavailable (can't be contacted for monitoring) # NOPARENT : The process that shuts down the system has died (shutdown impossible) +# ALARM : UPS has one or more alarms (look at "ups.alarm" for details) # -------------------------------------------------------------------------- # NOTIFYFLAG - change behavior of upsmon when NOTIFY events occur diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 144c115ee9..36247b6448 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -280,7 +280,7 @@ for more details see linkman:upsmon[8]. NOTECO;; UPS no longer in ECO mode (see above) -ALARM;; UPS is in an alarm state (has active alarms) +ALARM;; UPS has one or more active alarms (check ups.alarm) NOTALARM;; UPS is no longer in an alarm state (no active alarms) diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index 7529bf2e72..b41e7bc62b 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -193,7 +193,7 @@ on battery life). Older devices only implemented one or the other. UPS no longer in ECO mode (see above). *ALARM*:: -UPS is in an alarm state (has active alarms). +UPS has one or more active alarms (look at `ups.alarm` for details). *NOTALARM*:: UPS is no longer in an alarm state (no active alarms). From c85e741f6d3f83dde20f9297fd18fd314ebdf044 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 3 Dec 2024 11:06:49 +0100 Subject: [PATCH 096/144] drivers/usbhid-ups.c, drivers/netxml-ups.c, NEWS.adoc, UPGRADING.adoc: handle STATUS_BIT(NOBATTERY) to also status_set("RB") [#415] Signed-off-by: Jim Klimov --- NEWS.adoc | 8 +++++++- UPGRADING.adoc | 8 +++++++- drivers/netxml-ups.c | 4 ++-- drivers/usbhid-ups.c | 4 ++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 6748168384..c7ded6d0ac 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -149,6 +149,12 @@ https://github.com/networkupstools/nut/milestone/11 - liebert-gxe: added new driver with support for Liebert GXE Series UPS (serial or USB posing as a serial port). [#2629] + - `usbhid-ups` and `netxml-ups` updated to handle "No battery installed!" + alarm also to set the `RB` (Replace Battery) value in `ups.status`. + This may cause dual triggering of notifications (as an `ALARM` generally + and as an important `REPLBATT` status in particular) in `upsmon`, but + better safe than sorry. [#415] + - usbhid-ups updates: * Support of the `onlinedischarge_log_throttle_hovercharge` in the NUT v2.8.2 release was found to be incomplete. [#2423, follow-up to #2215] @@ -242,7 +248,7 @@ https://github.com/networkupstools/nut/milestone/11 that there is no common standard for what constitutes an alarm and such alarm states were also previously observed for less severe reasons. This depends on the manufacturer/device-specific implementation in the driver. - [issue #2657, PR #2658] + [issues #415, #2657, PR #2658] * Updated documentation, end-user clients (CGI, NUT-Monitor UI); * Updated `upsmon` client with ability to report entering and exiting the ALARM status if reported by the driver; diff --git a/UPGRADING.adoc b/UPGRADING.adoc index 2a2bfe79ff..3912b1149a 100644 --- a/UPGRADING.adoc +++ b/UPGRADING.adoc @@ -59,7 +59,13 @@ Changes from 2.8.2 to 2.8.3 some alarms can contribute to unwanted/early shutdowns. For this reason a `0|1` setting `ALARMCRITICAL` was introduced into `upsmon.conf` (default is `1`), for such users to be able to prevent their `upsmon` from treating - the `ALARM` status as overly severe when it is not in fact. [#2658] + the `ALARM` status as overly severe when it is not in fact. [#2658, #415] + +- `usbhid-ups` and `netxml-ups` updated to handle "No battery installed!" + alarm also to set the `RB` (Replace Battery) value in `ups.status`. + This may cause dual triggering of notifications (as an `ALARM` generally + and as an important `REPLBATT` status in particular) in `upsmon`, but + better safe than sorry. [#415] - `usbhid-ups` subdriver `PowerCOM HID` subdriver sent UPS `shutdown` and `stayoff` commands in wrong byte order, at least for devices currently diff --git a/drivers/netxml-ups.c b/drivers/netxml-ups.c index cfdc31632c..9c8e01d2f5 100644 --- a/drivers/netxml-ups.c +++ b/drivers/netxml-ups.c @@ -42,7 +42,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "network XML UPS" -#define DRIVER_VERSION "0.46" +#define DRIVER_VERSION "0.47" /** *_OBJECT query multi-part body boundary */ #define FORM_POST_BOUNDARY "NUT-NETXML-UPS-OBJECTS" @@ -1020,7 +1020,7 @@ static void netxml_status_set(void) if (STATUS_BIT(OVERLOAD)) { status_set("OVER"); /* overload */ } - if (STATUS_BIT(REPLACEBATT)) { + if (STATUS_BIT(REPLACEBATT) || STATUS_BIT(NOBATTERY)) { status_set("RB"); /* replace batt */ } if (STATUS_BIT(TRIM)) { diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index d8c13a5a89..e5b4d0a359 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -29,7 +29,7 @@ */ #define DRIVER_NAME "Generic HID driver" -#define DRIVER_VERSION "0.58" +#define DRIVER_VERSION "0.59" #define HU_VAR_WAITBEFORERECONNECT "waitbeforereconnect" @@ -2408,7 +2408,7 @@ static void ups_status_set(void) if (ups_status & STATUS(OVERLOAD)) { status_set("OVER"); /* overload */ } - if (ups_status & STATUS(REPLACEBATT)) { + if ((ups_status & STATUS(REPLACEBATT)) || (ups_status & STATUS(NOBATTERY))) { if (lbrb_log_delay_sec < 1 || (!isCalibrating && !lbrb_log_delay_without_calibrating) || !last_lb_start /* Calibration ended (not LB anymore) */ From 97c683873743d8b7b4708dcd70fb247d6be4583f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 3 Dec 2024 11:26:49 +0100 Subject: [PATCH 097/144] clients/upsmon.c, NEWS.adoc: optimize parse_status() by not checking further strings if we had a match; report unexpected tokens [#415, #2708] Signed-off-by: Jim Klimov --- NEWS.adoc | 2 ++ clients/upsmon.c | 65 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index c7ded6d0ac..16df87edba 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -263,6 +263,8 @@ https://github.com/networkupstools/nut/milestone/11 from templates). [issue #321, PR #2383] * added an `OBLBDURATION` (seconds) setting to optionally delay raising the alarm for immediate shutdown in critical situation. [#321] + * optimized `parse_status()` by not checking further strings if we had + a match; report unexpected tokens in debug log. [#415] - More systemd integration: * Introduced a `nut-sleep.service` unit which stops `nut.target` when a diff --git a/clients/upsmon.c b/clients/upsmon.c index dce985a0a4..f78c11f387 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -2432,6 +2432,7 @@ static int try_connect(utype_t *ups) static void parse_status(utype_t *ups, char *status) { char *statword, *ptr; + int handled_stat_words = 0; clear_alarm(); @@ -2467,38 +2468,82 @@ static void parse_status(utype_t *ups, char *status) /* split up the status words and parse each one separately */ while (statword != NULL) { + int handled = 0; + ptr = strchr(statword, ' '); if (ptr) *ptr++ = '\0'; upsdebugx(3, "parsing: [%s]", statword); + handled_stat_words++; - if (!strcasecmp(statword, "OL")) + /* Keep in sync with "Status data" chapter of docs/new-drivers.txt */ + if (!strcasecmp(statword, "OL")) { ups_on_line(ups); - if (!strcasecmp(statword, "OB")) + handled++; + } + else if (!strcasecmp(statword, "OB")) { ups_on_batt(ups); - if (!strcasecmp(statword, "LB")) + handled++; + } + else if (!strcasecmp(statword, "LB")) { ups_low_batt(ups); - if (!strcasecmp(statword, "RB")) + handled++; + } + else if (!strcasecmp(statword, "RB")) { upsreplbatt(ups); - if (!strcasecmp(statword, "CAL")) + handled++; + } + else if (!strcasecmp(statword, "CAL")) { ups_is_cal(ups); - if (!strcasecmp(statword, "OFF")) + handled++; + } + else if (!strcasecmp(statword, "OFF")) { ups_is_off(ups); - if (!strcasecmp(statword, "BYPASS")) + handled++; + } + else if (!strcasecmp(statword, "BYPASS")) { ups_is_bypass(ups); - if (!strcasecmp(statword, "ECO")) + handled++; + } + else if (!strcasecmp(statword, "ECO")) { ups_is_eco(ups); - if (!strcasecmp(statword, "ALARM")) + handled++; + } + else if (!strcasecmp(statword, "ALARM")) { ups_is_alarm(ups); + handled++; + } /* do it last to override any possible OL */ - if (!strcasecmp(statword, "FSD")) + else if (!strcasecmp(statword, "FSD")) { ups_fsd(ups); + handled++; + } + /* Known standard status tokens, some being obsoleted, no upsmon reaction assigned */ + else if ( + !strcasecmp(statword, "HB") + || !strcasecmp(statword, "CHRG") + || !strcasecmp(statword, "DISCHRG") + || !strcasecmp(statword, "OVER") + || !strcasecmp(statword, "TRIM") + || !strcasecmp(statword, "BOOST") + ) { + upsdebugx(4, "Known and ignored status token: [%s]", statword); + handled++; + } + + if (!handled) { + /* Driver reported something non-standard? */ + upsdebugx(1, "WARNING: Unexpected status token: [%s]", statword); + /* FIXME: Define a notification type like "OTHER" to report the un-handled status */ + } update_crittimer(ups); statword = ptr; } + + upsdebugx(3, "Handled %d status tokens", handled_stat_words); } /* see what the status of the UPS is and handle any changes */ From c7c0a83f4cce8f5cd2fbf5a2b3e2f5fa3454a141 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 3 Dec 2024 15:51:06 +0100 Subject: [PATCH 098/144] clients/upsmon.{c,h}, NEWS.adoc: revise do_notify() to allow passing an extra arg into formatting strings with two "%s" placeholders [#415] Signed-off-by: Jim Klimov --- NEWS.adoc | 3 ++ clients/upsmon.c | 86 +++++++++++++++++++++++++++++++++--------------- clients/upsmon.h | 6 ++++ 3 files changed, 69 insertions(+), 26 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 16df87edba..4730393ce5 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -265,6 +265,9 @@ https://github.com/networkupstools/nut/milestone/11 the alarm for immediate shutdown in critical situation. [#321] * optimized `parse_status()` by not checking further strings if we had a match; report unexpected tokens in debug log. [#415] + * revised internal `do_notify()` method to support formatting strings + with two `%s` placeholders, to use if certain use-cases pass any extra + information (e.g. not just "we have alarms" but their values too). [#415] - More systemd integration: * Introduced a `nut-sleep.service` unit which stops `nut.target` when a diff --git a/clients/upsmon.c b/clients/upsmon.c index f78c11f387..e6a64b4b0e 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -377,7 +377,7 @@ static void notify(const char *notice, int flags, const char *ntype, #endif } -static void do_notify(const utype_t *ups, int ntype) +static void do_notify(const utype_t *ups, int ntype, const char *extra) { int i; char msg[SMALLBUF], *upsname = NULL; @@ -388,6 +388,9 @@ static void do_notify(const utype_t *ups, int ntype) for (i = 0; notifylist[i].name != NULL; i++) { if (notifylist[i].type == ntype) { + const char *msgfmt = (notifylist[i].msg ? notifylist[i].msg : notifylist[i].stockmsg); + char *s = strstr(msgfmt, "%s"); + upsdebugx(2, "%s: ntype 0x%04x (%s)", __func__, ntype, notifylist[i].name); #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL @@ -399,9 +402,33 @@ static void do_notify(const utype_t *ups, int ntype) #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY #pragma GCC diagnostic ignored "-Wformat-security" #endif - snprintf(msg, sizeof(msg), - notifylist[i].msg ? notifylist[i].msg : notifylist[i].stockmsg, - ups ? ups->sys : ""); + if (s) { + /* FIXME: Check user inputs to rule out + * any *OTHER* formatting characters. + * NOTE: Not addressing now, PR pending + * and queued for NUT v2.8.4 */ + + /* Got at least one "%s" */ + if (strstr(s + 1, "%s")) { + /* Got a second "%s" */ + /* FIXME: if (extra != NULL) and we + * only have one "%s", plaster another + * to "msgfmt" and follow this path? + */ + snprintf(msg, sizeof(msg), + msgfmt, + upsname ? upsname : "", + NUT_STRARG(extra)); + } else { + snprintf(msg, sizeof(msg), + msgfmt, + upsname ? upsname : ""); + } + } else { + /* No "%s" in this msgfmt! e.g. NOTIFY_NOPARENT, + * SUSPEND_STARTING, SUSPEND_FINISHED ... */ + snprintf(msg, sizeof(msg), "%s", msgfmt); + } #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL #pragma GCC diagnostic pop #endif @@ -598,7 +625,7 @@ static void ups_is_alive(utype_t *ups) /* only notify for 0->1 transitions (to ignore the first connect) */ if (ups->commstate == 0) - do_notify(ups, NOTIFY_COMMOK); + do_notify(ups, NOTIFY_COMMOK, NULL); ups->commstate = 1; } @@ -613,7 +640,7 @@ static void ups_is_gone(utype_t *ups) ups->commstate = 0; /* COMMBAD is the initial loss of communications */ - do_notify(ups, NOTIFY_COMMBAD); + do_notify(ups, NOTIFY_COMMBAD, NULL); return; } @@ -626,7 +653,7 @@ static void ups_is_gone(utype_t *ups) /* now only complain if we haven't lately */ if ((now - ups->lastncwarn) > nocommwarntime) { /* NOCOMM indicates a persistent condition */ - do_notify(ups, NOTIFY_NOCOMM); + do_notify(ups, NOTIFY_NOCOMM, NULL); ups->lastncwarn = now; } } @@ -673,7 +700,7 @@ static void ups_is_off(utype_t *ups) upsdebugx(3, "%s: %s (first time)", __func__, ups->sys); /* must have changed from !OFF to OFF, so notify */ - do_notify(ups, NOTIFY_OFF); + do_notify(ups, NOTIFY_OFF, NULL); setflag(&ups->status, ST_OFF); } @@ -683,7 +710,7 @@ static void ups_is_notoff(utype_t *ups) ups->offsince = 0; ups->offstate = 0; if (flag_isset(ups->status, ST_OFF)) { /* actual change */ - do_notify(ups, NOTIFY_NOTOFF); + do_notify(ups, NOTIFY_NOTOFF, NULL); clearflag(&ups->status, ST_OFF); try_restore_pollfreq(ups); } @@ -704,7 +731,7 @@ static void ups_is_bypass(utype_t *ups) /* must have changed from !BYPASS to BYPASS, so notify */ - do_notify(ups, NOTIFY_BYPASS); + do_notify(ups, NOTIFY_BYPASS, NULL); setflag(&ups->status, ST_BYPASS); } @@ -713,7 +740,7 @@ static void ups_is_notbypass(utype_t *ups) /* Called when BYPASS is NOT among known states */ ups->bypassstate = 0; if (flag_isset(ups->status, ST_BYPASS)) { /* actual change */ - do_notify(ups, NOTIFY_NOTBYPASS); + do_notify(ups, NOTIFY_NOTBYPASS, NULL); clearflag(&ups->status, ST_BYPASS); try_restore_pollfreq(ups); } @@ -740,7 +767,11 @@ static void ups_is_eco(utype_t *ups) /* must have changed from !ECO to ECO, so notify */ - do_notify(ups, NOTIFY_ECO); + /* FIXME: Dig in driver/vendor specific info points to discern + * ECO, HE, ESS, ABM and other fancy words, with optional extra + * string argument? + */ + do_notify(ups, NOTIFY_ECO, NULL); setflag(&ups->status, ST_ECO); } @@ -749,7 +780,7 @@ static void ups_is_noteco(utype_t *ups) /* Called when ECO is NOT among known states */ ups->ecostate = 0; if (flag_isset(ups->status, ST_ECO)) { /* actual change */ - do_notify(ups, NOTIFY_NOTECO); + do_notify(ups, NOTIFY_NOTECO, NULL); clearflag(&ups->status, ST_ECO); try_restore_pollfreq(ups); } @@ -771,7 +802,10 @@ static void ups_is_alarm(utype_t *ups) /* must have changed from !ALARM to ALARM, so notify */ - do_notify(ups, NOTIFY_ALARM); + /* FIXME: Pass `ups.status` string as the extra argument, + * so if the formatting string allows - it is detailed + * in the notification. */ + do_notify(ups, NOTIFY_ALARM, NULL); setflag(&ups->status, ST_ALARM); } @@ -780,7 +814,7 @@ static void ups_is_notalarm(utype_t *ups) /* Called when ALARM is NOT among known states */ ups->alarmstate = 0; if (flag_isset(ups->status, ST_ALARM)) { /* actual change */ - do_notify(ups, NOTIFY_NOTALARM); + do_notify(ups, NOTIFY_NOTALARM, NULL); clearflag(&ups->status, ST_ALARM); try_restore_pollfreq(ups); } @@ -801,7 +835,7 @@ static void ups_on_batt(utype_t *ups) /* must have changed from OL to OB, so notify */ - do_notify(ups, NOTIFY_ONBATT); + do_notify(ups, NOTIFY_ONBATT, NULL); setflag(&ups->status, ST_ONBATT); clearflag(&ups->status, ST_ONLINE); } @@ -819,7 +853,7 @@ static void ups_on_line(utype_t *ups) /* ignore the first OL at startup, otherwise send the notifier */ if (ups->linestate != -1) - do_notify(ups, NOTIFY_ONLINE); + do_notify(ups, NOTIFY_ONLINE, NULL); ups->linestate = 1; @@ -857,7 +891,7 @@ static void doshutdown(void) upslogx(LOG_CRIT, "Executing automatic power-fail shutdown"); wall("Executing automatic power-fail shutdown\n"); - do_notify(NULL, NOTIFY_SHUTDOWN); + do_notify(NULL, NOTIFY_SHUTDOWN, NULL); sleep(finaldelay); @@ -1418,7 +1452,7 @@ static void ups_low_batt(utype_t *ups) /* must have changed from !LB to LB, so notify */ - do_notify(ups, NOTIFY_LOWBATT); + do_notify(ups, NOTIFY_LOWBATT, NULL); setflag(&ups->status, ST_LOWBATT); } @@ -1429,7 +1463,7 @@ static void upsreplbatt(utype_t *ups) time(&now); if ((now - ups->lastrbwarn) > rbwarntime) { - do_notify(ups, NOTIFY_REPLBATT); + do_notify(ups, NOTIFY_REPLBATT, NULL); ups->lastrbwarn = now; } } @@ -1445,7 +1479,7 @@ static void ups_is_cal(utype_t *ups) /* must have changed from !CAL to CAL, so notify */ - do_notify(ups, NOTIFY_CAL); + do_notify(ups, NOTIFY_CAL, NULL); setflag(&ups->status, ST_CAL); } @@ -1453,7 +1487,7 @@ static void ups_is_notcal(utype_t *ups) { /* Called when CAL is NOT among known states */ if (flag_isset(ups->status, ST_CAL)) { /* actual change */ - do_notify(ups, NOTIFY_NOTCAL); + do_notify(ups, NOTIFY_NOTCAL, NULL); clearflag(&ups->status, ST_CAL); try_restore_pollfreq(ups); } @@ -1470,7 +1504,7 @@ static void ups_fsd(utype_t *ups) /* must have changed from !FSD to FSD, so notify */ - do_notify(ups, NOTIFY_FSD); + do_notify(ups, NOTIFY_FSD, NULL); setflag(&ups->status, ST_FSD); } @@ -3041,7 +3075,7 @@ static void check_parent(void) return; lastwarn = now; - do_notify(NULL, NOTIFY_NOPARENT); + do_notify(NULL, NOTIFY_NOPARENT, NULL); /* also do this in case the notifier isn't being effective */ upslogx(LOG_ALERT, "Parent died - shutdown impossible"); @@ -3472,7 +3506,7 @@ int main(int argc, char *argv[]) } if (sleep_inhibitor_status == 1) { /* Preparing for sleep */ - do_notify(NULL, NOTIFY_SUSPEND_STARTING); + do_notify(NULL, NOTIFY_SUSPEND_STARTING, NULL); upslogx(LOG_INFO, "%s: Processing OS going to sleep", prog); Uninhibit(&sleep_inhibitor_fd); @@ -3510,7 +3544,7 @@ int main(int argc, char *argv[]) switch (sleep_inhibitor_status) { case 0: /* Waking up */ - do_notify(NULL, NOTIFY_SUSPEND_FINISHED); + do_notify(NULL, NOTIFY_SUSPEND_FINISHED, NULL); upslogx(LOG_INFO, "%s: Processing OS wake-up after sleep", prog); upsnotify(NOTIFY_STATE_WATCHDOG, NULL); diff --git a/clients/upsmon.h b/clients/upsmon.h index 47b1249b4f..caf947bfae 100644 --- a/clients/upsmon.h +++ b/clients/upsmon.h @@ -166,6 +166,12 @@ static struct { { NOTIFY_NOTBYPASS,"NOTBYPASS",NULL, "UPS %s: no longer on bypass", NOTIFY_DEFAULT }, { NOTIFY_ECO, "ECO", NULL, "UPS %s: in ECO mode (as defined by vendor)", NOTIFY_DEFAULT }, { NOTIFY_NOTECO, "NOTECO", NULL, "UPS %s: no longer in ECO mode", NOTIFY_DEFAULT }, + + /* FIXME: Remember the ups.alarm value and report it here, + * maybe optionally - e.g. check if the "ALARM" formatting + * string has actually one or two "%s" placeholders inside. + * Do issue a new notification if ups.alarm value changes. + */ { NOTIFY_ALARM, "ALARM", NULL, "UPS %s: one or more active alarms (check ups.alarm)", NOTIFY_DEFAULT }, { NOTIFY_NOTALARM, "NOTALARM", NULL, "UPS %s is no longer in an alarm state (no active alarms)", NOTIFY_DEFAULT }, From 6bf775cae56b2017c10aacfef9365bf7561307c4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 3 Dec 2024 16:49:59 +0100 Subject: [PATCH 099/144] upsmon code, docs, conf: introduce "OTHER"/"NOTOTHER" notifications for unknown tokens in `ups.status` [#415] Signed-off-by: Jim Klimov --- NEWS.adoc | 3 + clients/upsmon.c | 94 +++++++++++++++++++++++++++-- clients/upsmon.h | 14 +++++ common/nutconf.cpp | 4 ++ common/nutwriter.cpp | 2 + conf/upsmon.conf.sample.in | 8 +++ docs/man/nutconf.txt | 2 + docs/man/upsmon.conf.txt | 6 ++ docs/man/upsmon.txt | 6 ++ docs/nut.dict | 3 +- include/nutconf.hpp | 3 + scripts/augeas/nutupsmonconf.aug.in | 2 + tools/nutconf/nutconf-cli.cpp | 2 +- 13 files changed, 143 insertions(+), 6 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 4730393ce5..7e5ced3334 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -268,6 +268,9 @@ https://github.com/networkupstools/nut/milestone/11 * revised internal `do_notify()` method to support formatting strings with two `%s` placeholders, to use if certain use-cases pass any extra information (e.g. not just "we have alarms" but their values too). [#415] + * introduced handling for "unknown" `ups.status` tokens, reporting them + as "OTHER" notification type (whenever the set of such tokens appears + or changes) or "NOTOTHER" when they disappear. [#415] - More systemd integration: * Introduced a `nut-sleep.service` unit which stops `nut.target` when a diff --git a/clients/upsmon.c b/clients/upsmon.c index e6a64b4b0e..1a45a820ea 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -1728,6 +1728,7 @@ static void addups(int reloading, const char *sys, const char *pvs, tmp->pw = xstrdup(pw); tmp->status = 0; + tmp->status_tokens = NULL; tmp->retain = 1; /* ignore initial COMMOK and ONLINE by default */ @@ -2465,8 +2466,9 @@ static int try_connect(utype_t *ups) /* deal with the contents of STATUS or ups.status for this ups */ static void parse_status(utype_t *ups, char *status) { - char *statword, *ptr; - int handled_stat_words = 0; + char *statword, *ptr, other_stat_words[SMALLBUF]; + int handled_stat_words = 0, changed_other_stat_words = 0; + st_tree_timespec_t st_start; clear_alarm(); @@ -2499,6 +2501,9 @@ static void parse_status(utype_t *ups, char *status) ups_is_notalarm(ups); statword = status; + /* Track what status tokens we no longer see */ + state_get_timestamp(&st_start); + other_stat_words[0] = '\0'; /* split up the status words and parse each one separately */ while (statword != NULL) { @@ -2562,14 +2567,50 @@ static void parse_status(utype_t *ups, char *status) || !strcasecmp(statword, "TRIM") || !strcasecmp(statword, "BOOST") ) { + /* FIXME: Do we want these logged similar to OTHERs? */ upsdebugx(4, "Known and ignored status token: [%s]", statword); handled++; } if (!handled) { + /* NOTE: Could just state_getinfo() but then + * to refresh the timestamp we'd need to walk + * it again. So replicating a bit of that code. */ + st_tree_t *sttmp = (ups->status_tokens ? state_tree_find(ups->status_tokens, statword) : NULL); + /* Driver reported something non-standard? */ - upsdebugx(1, "WARNING: Unexpected status token: [%s]", statword); - /* FIXME: Define a notification type like "OTHER" to report the un-handled status */ + upsdebugx(4, "Unexpected status token: [%s]", statword); + + snprintfcat(other_stat_words, + sizeof(other_stat_words), "%s%s", + *other_stat_words ? " " : "", + statword); + + /* Part of our job is to update the timestamp, + * so we can report which tokens disappeared + * and eject them from the list. + * + * The recorded value currently does not matter. + * + * FIXME: Use the value as e.g. "0"/"1" and + * maybe the state->aux as counter, to track + * and report that the non-standard token + * was seen more than once? + */ + if (sttmp) { + /* Start from the discovered node to shortcut to + * (static) st_tree_node_refresh_timestamp(sttmp) + * and complete the info-setting quickly. + */ + state_setinfo(&sttmp, statword, "1"); + } else { + /* This token was not seen last time => new state + * Handle with a notification type "OTHER" + * to report the new un-handled status. */ + changed_other_stat_words++; + upsdebugx(5, "Unexpected status token: [%s]: appeared", statword); + state_setinfo(&(ups->status_tokens), statword, "1"); + } } update_crittimer(ups); @@ -2577,6 +2618,51 @@ static void parse_status(utype_t *ups, char *status) statword = ptr; } + if (ups->status_tokens) { + st_tree_t *node = ups->status_tokens, *sttmp = node; + + /* Scroll to the leftmost entry for alphanumeric sorted processing */ + while (sttmp) { + node = sttmp; + sttmp = sttmp->left; + } + + /* Go from left to right, on a freeing spree if need be */ + while (node) { + sttmp = node->right; + if (st_tree_node_compare_timestamp(node, &st_start) < 0) { + upsdebugx(5, "Unexpected status token: [%s]: disappeared", + NUT_STRARG(node->var)); + changed_other_stat_words++; + + /* whatever is on the left, hang it off current right */ + if (node->right) { + node->right->left = node->left; /* May be NULL*/ + } + /* whatever is on the right, hang it off current left */ + if (node->left) { + node->left->right = node->right; /* May be NULL*/ + } + /* forget the neighbors before dropping the "tree" */ + node->right = NULL; + node->left = NULL; + + state_infofree(node); + } + node = sttmp; + } + } + + if (changed_other_stat_words) { + if (*other_stat_words) { + do_notify(ups, NOTIFY_OTHER, other_stat_words); + } else { + do_notify(ups, NOTIFY_NOTOTHER, NULL); + state_infofree(ups->status_tokens); + ups->status_tokens = NULL; + } + } + upsdebugx(3, "Handled %d status tokens", handled_stat_words); } diff --git a/clients/upsmon.h b/clients/upsmon.h index caf947bfae..939d7a52b5 100644 --- a/clients/upsmon.h +++ b/clients/upsmon.h @@ -24,6 +24,8 @@ #ifndef NUT_UPSMON_H_SEEN #define NUT_UPSMON_H_SEEN 1 +#include "state.h" + /* flags for ups->status */ #define ST_ONLINE (1 << 0) /* UPS is on line (OL) */ @@ -39,6 +41,7 @@ #define ST_BYPASS (1 << 9) /* UPS is on bypass so not protecting */ #define ST_ECO (1 << 10) /* UPS is in ECO (High Efficiency) mode or similar tweak, e.g. Energy Saver System mode */ #define ST_ALARM (1 << 11) /* UPS has at least one active alarm */ +#define ST_OTHER (1 << 12) /* UPS has at least one unclassified status token */ /* required contents of flag file */ #define SDMAGIC "upsmon-shutdown-file" @@ -63,6 +66,7 @@ typedef struct { char *un; /* username (optional for now) */ char *pw; /* password from conf */ int status; /* status (see flags above) */ + st_tree_t *status_tokens; /* parsed ups.status, mapping each token to whatever value if it is currently set (evicted when not) */ int retain; /* tracks deletions at reload */ /* handle suppression of COMMOK and ONLINE at startup */ @@ -117,6 +121,10 @@ typedef struct { #define NOTIFY_ALARM 18 /* UPS has at least one active alarm */ #define NOTIFY_NOTALARM 19 /* UPS has no active alarms */ +/* Special handling below */ +#define NOTIFY_OTHER 28 /* UPS has at least one unclassified status token */ +#define NOTIFY_NOTOTHER 29 /* UPS has no unclassified status tokens anymore */ + #define NOTIFY_SUSPEND_STARTING 30 /* OS is entering sleep/suspend/hibernate slumber mode, and we know it */ #define NOTIFY_SUSPEND_FINISHED 31 /* OS just finished sleep/suspend/hibernate slumber mode, and we know it */ @@ -175,6 +183,12 @@ static struct { { NOTIFY_ALARM, "ALARM", NULL, "UPS %s: one or more active alarms (check ups.alarm)", NOTIFY_DEFAULT }, { NOTIFY_NOTALARM, "NOTALARM", NULL, "UPS %s is no longer in an alarm state (no active alarms)", NOTIFY_DEFAULT }, + /* Special handling, two string placeholders! + * Reported when status_tokens tree changes (and is not empty in the end) */ + { NOTIFY_OTHER, "OTHER", NULL, "UPS %s: has at least one unclassified status token: [%s]", NOTIFY_DEFAULT }, + /* Reported when status_tokens tree becomes empty */ + { NOTIFY_NOTOTHER, "NOTOTHER", NULL, "UPS %s has no unclassified status tokens anymore", NOTIFY_DEFAULT }, + { NOTIFY_SUSPEND_STARTING, "SUSPEND_STARTING", NULL, "OS is entering sleep/suspend/hibernate mode", NOTIFY_DEFAULT }, { NOTIFY_SUSPEND_FINISHED, "SUSPEND_FINISHED", NULL, "OS just finished sleep/suspend/hibernate mode, de-activating obsolete UPS readings to avoid an unfortunate shutdown", NOTIFY_DEFAULT }, diff --git a/common/nutconf.cpp b/common/nutconf.cpp index cd8a76d5e9..3f1eef9aef 100644 --- a/common/nutconf.cpp +++ b/common/nutconf.cpp @@ -1324,6 +1324,10 @@ UpsmonConfiguration::NotifyType UpsmonConfiguration::NotifyTypeFromString(const return NOTIFY_ALARM; else if(str=="NOTALARM") return NOTIFY_NOTALARM; + else if(str=="OTHER") + return NOTIFY_OTHER; + else if(str=="NOTOTHER") + return NOTIFY_NOTOTHER; else if(str=="SUSPEND_STARTING") return NOTIFY_SUSPEND_STARTING; else if(str=="SUSPEND_FINISHED") diff --git a/common/nutwriter.cpp b/common/nutwriter.cpp index cafac79e48..bc26783d78 100644 --- a/common/nutwriter.cpp +++ b/common/nutwriter.cpp @@ -408,6 +408,8 @@ const NotifyFlagsStrings::TypeStrings NotifyFlagsStrings::type_str = { "NOTECO", // NOTIFY_NOTECO "ALARM", // NOTIFY_ALARM "NOTALARM", // NOTIFY_NOTALARM + "OTHER", // NOTIFY_OTHER + "NOTOTHER", // NOTIFY_NOTOTHER "SUSPEND_STARTING", // NOTIFY_SUSPEND_STARTING "SUSPEND_FINISHED", // NOTIFY_SUSPEND_FINISHED }; diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index f75a5104cc..cb000b11ca 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -337,6 +337,14 @@ POWERDOWNFLAG "@POWERDOWNFLAG@" # NOTIFYMSG ALARM "UPS %s: one or more active alarms (check ups.alarm)" # NOTIFYMSG NOTALARM "UPS %s is no longer in an alarm state (no active alarms)" # +# Special handling is provided for surprise tokens seen in ups.status, which +# are not in the standard NUT dictionary (but some drivers are known to use); +# note that unlike other formatting strings, the "OTHER" one has two string +# placeholders "%s" (it is safe to use one, leaving just the UPS name, or none): +# +# NOTIFYMSG OTHER "UPS %s: has at least one unclassified status token: [%s]" +# NOTIFYMSG NOTOTHER "UPS %s has no unclassified status tokens anymore" +# # A few messages not directly related to UPS events are also available: # # NOTIFYMSG SUSPEND_STARTING "OS is entering sleep/suspend/hibernate mode" diff --git a/docs/man/nutconf.txt b/docs/man/nutconf.txt index ebacfb86bd..74835f3a50 100644 --- a/docs/man/nutconf.txt +++ b/docs/man/nutconf.txt @@ -115,6 +115,8 @@ Notification types are: - 'NOTBYPASS' (no longer on bypass) - 'ALARM' (UPS is in an alarm state (has active alarms)) - 'NOTALARM' (UPS is no longer in an alarm state (no active alarms)) +- 'OTHER' (UPS has at least one unclassified status token) +- 'NOTOTHER' (UPS has no unclassified status tokens anymore) - 'SUSPEND_STARTING' (OS is entering sleep/suspend/hibernate mode) - 'SUSPEND_FINISHED' (OS just finished sleep/suspend/hibernate mode) diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 36247b6448..77bc887624 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -284,6 +284,12 @@ ALARM;; UPS has one or more active alarms (check ups.alarm) NOTALARM;; UPS is no longer in an alarm state (no active alarms) +OTHER;; UPS has at least one unclassified `ups.status` token; +for this notification, the `message` can contain a second `%s` placeholder +to substitute the current collection of such tokens. + +NOTOTHER;; UPS has no unclassified status tokens anymore + SUSPEND_STARTING;; OS is entering sleep/suspend/hibernate mode SUSPEND_FINISHED;; OS just finished sleep/suspend/hibernate mode, diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index b41e7bc62b..55110802bc 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -198,6 +198,12 @@ UPS has one or more active alarms (look at `ups.alarm` for details). *NOTALARM*:: UPS is no longer in an alarm state (no active alarms). +*OTHER*:: +UPS has at least one unclassified status token. + +*NOTOTHER*:: +UPS has no unclassified status tokens anymore. + *SUSPEND_STARTING*:: OS is entering sleep/suspend/hibernate mode. diff --git a/docs/nut.dict b/docs/nut.dict index df73c729b6..57f33ada7b 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 3246 utf-8 +personal_ws-1.1 en 3247 utf-8 AAC AAS ABI @@ -752,6 +752,7 @@ NOTIFYFLAG NOTIFYFLAGS NOTIFYMSG NOTOFF +NOTOTHER NQA NTP NUT's diff --git a/include/nutconf.hpp b/include/nutconf.hpp index a97298ce82..a3bdf7fdaf 100644 --- a/include/nutconf.hpp +++ b/include/nutconf.hpp @@ -1475,6 +1475,9 @@ class UpsmonConfiguration : public Serialisable NOTIFY_ALARM, NOTIFY_NOTALARM, + NOTIFY_OTHER = 28, + NOTIFY_NOTOTHER, + NOTIFY_SUSPEND_STARTING = 30, NOTIFY_SUSPEND_FINISHED, diff --git a/scripts/augeas/nutupsmonconf.aug.in b/scripts/augeas/nutupsmonconf.aug.in index e972a406d7..7e7f219433 100644 --- a/scripts/augeas/nutupsmonconf.aug.in +++ b/scripts/augeas/nutupsmonconf.aug.in @@ -127,6 +127,8 @@ let upsmon_notify_type = "ONLINE" | "NOTECO" | "ALARM" | "NOTALARM" + | "OTHER" + | "NOTOTHER" | "SUSPEND_STARTING" | "SUSPEND_FINISHED" diff --git a/tools/nutconf/nutconf-cli.cpp b/tools/nutconf/nutconf-cli.cpp index a12b071eb5..f72aa7a7df 100644 --- a/tools/nutconf/nutconf-cli.cpp +++ b/tools/nutconf/nutconf-cli.cpp @@ -145,7 +145,7 @@ const char * Usage::s_text[] = { "Notification types:", " ONLINE, ONBATT, LOWBATT, FSD, COMMOK, COMMBAD, SHUTDOWN, REPLBATT, NOCOMM, NOPARENT,", " CAL, NOTCAL, OFF, NOTOFF, BYPASS, NOTBYPASS, ECO, NOTECO, ALARM, NOTALARM,", - " SUSPEND_STARTING, SUSPEND_FINISHED", + " OTHER, NOTOTHER, SUSPEND_STARTING, SUSPEND_FINISHED", "Notification flags:", " SYSLOG, WALL, EXEC, IGNORE", "User specification:", From 32fcb3f570e4fc6194b480b92729ef4f5a625be7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 3 Dec 2024 17:33:14 +0100 Subject: [PATCH 100/144] docs/new-drivers.txt: in "Status data", stress the life-cycle of `ups.status` flag tokens [#2708] Signed-off-by: Jim Klimov --- docs/new-drivers.txt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/new-drivers.txt b/docs/new-drivers.txt index 6c85afcc06..f7ad204627 100644 --- a/docs/new-drivers.txt +++ b/docs/new-drivers.txt @@ -244,9 +244,20 @@ Possible values for status_set: BOOST -- UPS is boosting incoming voltage FSD -- Forced Shutdown (restricted use, see the note below) -Anything else will not be recognized by the usual clients. Coordinate -with the nut-upsdev list before creating something new, since there will be -duplication and ugliness otherwise. +Anything else will not be recognized by the usual clients expecting a +particular NUT standard release. New tokens may appear over time, but +driver developers should coordinate with the nut-upsdev list before creating +something new, since there will be duplication and ugliness otherwise. +It is possible that eventually, due to hardware and software design evolution, +some concepts would be superseded by others. Fundamental meanings of the +flags listed above should not change (but these flags may become no longer +issued by the current NUT drivers; then may still be used e.g. in forks or +older packaged builds). + +Clients however MUST accept any space-separated tokens in `ups.status` +without error or crash, and MUST treat those defined above with the +ascribed meanings, but MAY ignore unidentified tokens (quietly by default, +or acknowledge the skip with a debug log message). [NOTE] ============================================================================== From 04087cc104544daba331338f0c65e4ae56811735 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 3 Dec 2024 17:42:53 +0100 Subject: [PATCH 101/144] clients/upsmon.c: allow to get_var(ups, "alarm"...) and use it in ups_is_alarm() notification [#415] Signed-off-by: Jim Klimov --- clients/upsmon.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 1a45a820ea..94460c6277 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -788,6 +788,8 @@ static void ups_is_noteco(utype_t *ups) static void ups_is_alarm(utype_t *ups) { + char alarms[SMALLBUF]; + if (flag_isset(ups->status, ST_ALARM)) { /* potentially no change */ /* TODO: add device.alarm.count */ upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); @@ -802,10 +804,15 @@ static void ups_is_alarm(utype_t *ups) /* must have changed from !ALARM to ALARM, so notify */ - /* FIXME: Pass `ups.status` string as the extra argument, - * so if the formatting string allows - it is detailed - * in the notification. */ - do_notify(ups, NOTIFY_ALARM, NULL); + /* FIXME: Track the earlier reported string, re-notify */ + /* Pass `ups.status` string as the extra argument, + * so if the formatting string allows - it is detailed + * in the notification. */ + if (get_var(ups, "alarm", alarms, sizeof(alarms)) == 0) { + do_notify(ups, NOTIFY_ALARM, alarms); + } else { + do_notify(ups, NOTIFY_ALARM, NULL); + } setflag(&ups->status, ST_ALARM); } @@ -1065,6 +1072,14 @@ static int get_var(utype_t *ups, const char *var, char *buf, size_t bufsize) numq = 3; } + if (!strcmp(var, "alarm")) { + /* Opaque string */ + query[0] = "VAR"; + query[1] = ups->upsname; + query[2] = "ups.alarm"; + numq = 3; + } + if (numq == 0) { upslogx(LOG_ERR, "get_var: programming error: var=%s", var); return -1; From 380b3e4009608d4a77d42bf29bbe6c1dc559f6e8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 3 Dec 2024 17:54:10 +0100 Subject: [PATCH 102/144] clients/upsmon.c: ups_is_alarm(): track if ups.alarm value changed, re-notify [#415] Signed-off-by: Jim Klimov --- clients/upsmon.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 94460c6277..a6ed3d27f5 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -176,6 +176,9 @@ static int nut_debug_level_global = -1; */ static int nut_debug_level_args = 0; +/* pre-declare internal methods */ +static int get_var(utype_t *ups, const char *var, char *buf, size_t bufsize); + static void setflag(int *val, int flag) { *val |= flag; @@ -788,11 +791,26 @@ static void ups_is_noteco(utype_t *ups) static void ups_is_alarm(utype_t *ups) { + /* Track the earlier reported string, re-notify if needed */ + static char alarms_prev[SMALLBUF]; char alarms[SMALLBUF]; if (flag_isset(ups->status, ST_ALARM)) { /* potentially no change */ - /* TODO: add device.alarm.count */ - upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); + /* TODO: add device.alarm.count? */ + if (get_var(ups, "alarm", alarms, sizeof(alarms)) == 0) { + if (!strcmp(alarms_prev, alarms)) { + upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); + } else { + upsdebugx(4, "%s: %s: updated ups.alarm value is %s", + __func__, ups->sys, alarms); + strncpy(alarms_prev, alarms, sizeof(alarms_prev)); + /* FIXME: Check for two "%s" placeholders? */ + do_notify(ups, NOTIFY_ALARM, alarms); + } + } else { + upsdebugx(4, "%s: %s: still on alarm, failed to get current ups.alarm value", + __func__, ups->sys); + } return; } @@ -804,15 +822,20 @@ static void ups_is_alarm(utype_t *ups) /* must have changed from !ALARM to ALARM, so notify */ - /* FIXME: Track the earlier reported string, re-notify */ /* Pass `ups.status` string as the extra argument, * so if the formatting string allows - it is detailed * in the notification. */ if (get_var(ups, "alarm", alarms, sizeof(alarms)) == 0) { + upsdebugx(4, "%s: %s: current ups.alarm value is %s", + __func__, ups->sys, alarms); do_notify(ups, NOTIFY_ALARM, alarms); } else { + upsdebugx(4, "%s: %s: failed to get current ups.alarm value", + __func__, ups->sys); do_notify(ups, NOTIFY_ALARM, NULL); + alarms[0] = '\0'; } + strncpy(alarms_prev, alarms, sizeof(alarms_prev)); setflag(&ups->status, ST_ALARM); } From 1ac8b9a5177a699dca21b9242bd76db4514657f3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 3 Dec 2024 18:00:56 +0100 Subject: [PATCH 103/144] clients/upsmon.h, docs: default "ALARM" message format to two "%s" placeholders [#415] Signed-off-by: Jim Klimov --- clients/upsmon.h | 10 +++++----- conf/upsmon.conf.sample.in | 3 ++- docs/man/upsmon.conf.txt | 4 +++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/clients/upsmon.h b/clients/upsmon.h index 939d7a52b5..ca0dba5c99 100644 --- a/clients/upsmon.h +++ b/clients/upsmon.h @@ -175,12 +175,12 @@ static struct { { NOTIFY_ECO, "ECO", NULL, "UPS %s: in ECO mode (as defined by vendor)", NOTIFY_DEFAULT }, { NOTIFY_NOTECO, "NOTECO", NULL, "UPS %s: no longer in ECO mode", NOTIFY_DEFAULT }, - /* FIXME: Remember the ups.alarm value and report it here, - * maybe optionally - e.g. check if the "ALARM" formatting - * string has actually one or two "%s" placeholders inside. - * Do issue a new notification if ups.alarm value changes. + /* NOTE: We remember the ups.alarm value and report it here, + * maybe optionally - e.g. check if the "ALARM" formatting + * string has actually one or two "%s" placeholders inside. + * Do issue a new notification if ups.alarm value changes. */ - { NOTIFY_ALARM, "ALARM", NULL, "UPS %s: one or more active alarms (check ups.alarm)", NOTIFY_DEFAULT }, + { NOTIFY_ALARM, "ALARM", NULL, "UPS %s: one or more active alarms: [%s]", NOTIFY_DEFAULT }, { NOTIFY_NOTALARM, "NOTALARM", NULL, "UPS %s is no longer in an alarm state (no active alarms)", NOTIFY_DEFAULT }, /* Special handling, two string placeholders! diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index cb000b11ca..c6a25a9bbf 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -334,7 +334,8 @@ POWERDOWNFLAG "@POWERDOWNFLAG@" # NOTIFYMSG NOTBYPASS "UPS %s: no longer on bypass" # NOTIFYMSG ECO "UPS %s: in ECO mode (as defined by vendor)" # NOTIFYMSG NOTECO "UPS %s: no longer in ECO mode (as defined by vendor)" -# NOTIFYMSG ALARM "UPS %s: one or more active alarms (check ups.alarm)" +# NOTIFYMSG ALARM "UPS %s: one or more active alarms: [%s]" +# or NOTIFYMSG ALARM "UPS %s: one or more active alarms (check ups.alarm)" # NOTIFYMSG NOTALARM "UPS %s is no longer in an alarm state (no active alarms)" # # Special handling is provided for surprise tokens seen in ups.status, which diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 77bc887624..15620e1df5 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -280,7 +280,9 @@ for more details see linkman:upsmon[8]. NOTECO;; UPS no longer in ECO mode (see above) -ALARM;; UPS has one or more active alarms (check ups.alarm) +ALARM;; UPS has one or more active alarms (check ups.alarm); +for this notification, the `message` can contain a second `%s` placeholder +to substitute the current value of `ups.alarm`. NOTALARM;; UPS is no longer in an alarm state (no active alarms) From a2b2427c29057ef1bcf2f34c56a8f45ec147dcfb Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 4 Dec 2024 16:59:39 +0100 Subject: [PATCH 104/144] drivers/cps-hid.c, NEWS.adoc: add ups.temperature [#2711] Kudos to Cyber Energy (affiliate of CPS) per https://alioth-lists.debian.net/pipermail/nut-upsdev/2024-December/008069.html Signed-off-by: Jim Klimov --- NEWS.adoc | 2 +- drivers/cps-hid.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 91b22aa97f..b7347d21b3 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -180,7 +180,7 @@ https://github.com/networkupstools/nut/milestone/11 the old behavior (if some devices do need it), while a fix is applied by default: `powercom_sdcmd_byte_order_fallback`. [PR #2480] * `cps-hid` subdriver now supports more variables, as available on e.g. - CP1350EPFCLCD model. [PR #2540] + CP1350EPFCLCD model, including temperature. [PRs #2540, #2711] * USB parameters (per `usb_communication_subdriver_t`) are now set back to their default values during enumeration after probing each subdriver. Having an unrelated device connected with a VID:PID matching the diff --git a/drivers/cps-hid.c b/drivers/cps-hid.c index 651807a454..f29d974046 100644 --- a/drivers/cps-hid.c +++ b/drivers/cps-hid.c @@ -32,7 +32,7 @@ #include "cps-hid.h" #include "usb-common.h" -#define CPS_HID_VERSION "CyberPower HID 0.81" +#define CPS_HID_VERSION "CyberPower HID 0.82" /* Cyber Power Systems */ #define CPS_VENDORID 0x0764 @@ -234,6 +234,7 @@ static hid_info_t cps_hid2nut[] = { { "ups.timer.shutdown", 0, 0, "UPS.Output.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.reboot", 0, 0, "UPS.Output.DelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.firmware", 0, 0, "UPS.PowerSummary.CPSFirmwareVersion", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, + { "ups.temperature", 0, 0, "UPS.PowerSummary.Temperature", NULL, "%s", 0, kelvin_celsius_conversion }, /* Special case: ups.status & ups.alarm */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, From d4c361d8b9ec3b82282f17e736594e0cb064871e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 21:24:20 +0100 Subject: [PATCH 105/144] conf/upsmon.conf.sample.in: suggest NOTIFYFLAG for OTHER/NOTOTHER too [#415] Signed-off-by: Jim Klimov --- conf/upsmon.conf.sample.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index c6a25a9bbf..60bb61f1b5 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -398,6 +398,9 @@ POWERDOWNFLAG "@POWERDOWNFLAG@" # NOTIFYFLAG ALARM SYSLOG+WALL # NOTIFYFLAG NOTALARM SYSLOG+WALL # +# NOTIFYFLAG OTHER SYSLOG+WALL +# NOTIFYFLAG NOTOTHER SYSLOG+WALL +# # NOTIFYFLAG SUSPEND_STARTING SYSLOG+WALL # NOTIFYFLAG SUSPEND_FINISHED SYSLOG+WALL # From 25dcfab686be1872fa942f2b32286f1d82c456ea Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 14:06:15 +0100 Subject: [PATCH 106/144] scripts/misc/notifyme-debug: honour TEMPDIR or TMPDIR set by caller; fall back to /dev/shm or /tmp otherwise [#2709] Signed-off-by: Jim Klimov --- scripts/misc/notifyme-debug | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/scripts/misc/notifyme-debug b/scripts/misc/notifyme-debug index fb1620aa5c..22e2f79e76 100755 --- a/scripts/misc/notifyme-debug +++ b/scripts/misc/notifyme-debug @@ -1,12 +1,17 @@ #!/bin/sh -# This is a sample NUT notification script aimed to aid debugging of upsmon -# behavior. It creates or appends to a log file named per user ID in the -# temporary location with an entry per line like this: +# This is a sample NUT notification script aimed to aid debugging of +# `upsmon` behavior, as its NOTIFYCMD (as specified in upsmon.conf). +# This script creates or appends to a log file named per user ID in +# the specified or default temporary location with an entry per line +# like this: # Sat Apr 6 17:23:06 UTC 2024 [uid=77(nut) gid=77(nut) groups=77(nut)] COMMBAD [eco650]: Communications with UPS eco650 lost (1 tokens) # Sat Apr 6 17:28:01 UTC 2024 [uid=77(nut) gid=77(nut) groups=77(nut)] NOCOMM [eco650]: UPS eco650 is unavailable (1 tokens) # # Copyright (C) 2023-2024 by Jim Klimov # Licensed under the terms of the Network UPS Tools source license (GPLv2+) -printf '%s\t[%s]\t%s\t[%s]:\t%s\t(%s tokens)\n' "`date -u`" "`id`" "${NOTIFYTYPE-}" "${UPSNAME-}" "$*" "$#" >> "/tmp/notifyme-`id -u`.log" +[ -n "${TEMPDIR-}" ] || TEMPDIR="${TMPDIR-}" +[ -n "${TEMPDIR-}" ] || { [ -d "/dev/shm" && TEMPDIR="/dev/shm" || TEMPDIR="/tmp" ; } + +printf '%s\t[%s]\t%s\t[%s]:\t%s\t(%s tokens)\n' "`date -u`" "`id`" "${NOTIFYTYPE-}" "${UPSNAME-}" "$*" "$#" >> "${TEMPDIR}/notifyme-`id -u`.log" From 5af374d4e57ba9dece716b884a5231b06bb644f7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 15:07:12 +0100 Subject: [PATCH 107/144] tests/NIT/Makefile.am: allow to pass custom NUT_PORT and NIT_CASE [#2709] ...default the latter to `testgroup_sandbox_upsmon_master` now (includes the earlier `testcase_sandbox_start_drivers_after_upsd`) Signed-off-by: Jim Klimov --- tests/NIT/Makefile.am | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/NIT/Makefile.am b/tests/NIT/Makefile.am index 069825355f..825df6bd92 100644 --- a/tests/NIT/Makefile.am +++ b/tests/NIT/Makefile.am @@ -31,16 +31,20 @@ check-NIT-devel: $(abs_srcdir)/nit.sh +@cd "$(top_builddir)/drivers" && $(MAKE) $(AM_MAKEFLAGS) -s dummy-ups$(EXEEXT) upsdrvctl$(EXEEXT) +@$(MAKE) $(AM_MAKEFLAGS) check-NIT +# Allow to override with make/env vars; provide sensible defaults (see nit.sh): +#NIT_CASE = testcase_sandbox_start_drivers_after_upsd +NIT_CASE = testgroup_sandbox_upsmon_master +NUT_PORT = 12345 check-NIT-sandbox: $(abs_srcdir)/nit.sh [ -n "$${DEBUG_SLEEP-}" ] && [ "$${DEBUG_SLEEP-}" -gt 0 ] || DEBUG_SLEEP=600 ; export DEBUG_SLEEP ; \ LANG=C LC_ALL=C TZ=UTC \ - NUT_PORT=12345 NIT_CASE=testcase_sandbox_start_drivers_after_upsd NUT_FOREGROUND_WITH_PID=true \ + NUT_PORT=$(NUT_PORT) NIT_CASE="$(NIT_CASE)" NUT_FOREGROUND_WITH_PID=true \ "$(abs_srcdir)/nit.sh" check-NIT-sandbox-devel: $(abs_srcdir)/nit.sh +[ -n "$${DEBUG_SLEEP-}" ] && [ "$${DEBUG_SLEEP-}" -gt 0 ] || DEBUG_SLEEP=600 ; export DEBUG_SLEEP ; \ LANG=C LC_ALL=C TZ=UTC \ - NUT_PORT=12345 NIT_CASE=testcase_sandbox_start_drivers_after_upsd NUT_FOREGROUND_WITH_PID=true \ + NUT_PORT=$(NUT_PORT) NIT_CASE="$(NIT_CASE)" NUT_FOREGROUND_WITH_PID=true \ $(MAKE) $(AM_MAKEFLAGS) check-NIT-devel SPELLCHECK_SRC = README.adoc From 0f81a75152052bc2f671c71a974eb9a60302a57d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 21:32:05 +0100 Subject: [PATCH 108/144] tests/NIT/nit.sh: testcase_sandbox_upsc_query_timer(): add a retry with longer delay [#2709] Signed-off-by: Jim Klimov --- tests/NIT/nit.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 0d04dd54e3..9ead5562f5 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -1172,15 +1172,23 @@ testcase_sandbox_upsc_query_timer() { OUT2="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "[testcase_sandbox_upsc_query_timer] upsd does not respond on port ${NUT_PORT} ($?): $OUT2" OUT3="" OUT4="" + OUT5="" + + # The SUT may be busy, or dummy-ups can have a loop delay + # (pollfreq) after reading the file before wrapping around if [ x"$OUT1" = x"$OUT2" ]; then sleep 3 OUT3="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "[testcase_sandbox_upsc_query_timer] upsd does not respond on port ${NUT_PORT} ($?): $OUT3" if [ x"$OUT2" = x"$OUT3" ]; then sleep 3 OUT4="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "[testcase_sandbox_upsc_query_timer] upsd does not respond on port ${NUT_PORT} ($?): $OUT4" + if [ x"$OUT3" = x"$OUT4" ]; then + sleep 8 + OUT5="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "[testcase_sandbox_upsc_query_timer] upsd does not respond on port ${NUT_PORT} ($?): $OUT4" + fi fi fi - if echo "$OUT1$OUT2$OUT3$OUT4" | grep "OB" && echo "$OUT1$OUT2$OUT3$OUT4" | grep "OL" ; then + if echo "$OUT1$OUT2$OUT3$OUT4$OUT5" | grep "OB" && echo "$OUT1$OUT2$OUT3$OUT4$OUT5" | grep "OL" ; then log_info "[testcase_sandbox_upsc_query_timer] PASSED: ups.status flips over time" PASSED="`expr $PASSED + 1`" else From 3d63a05193aaab67749fdf6ea1e7054e7441c91d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 15:07:48 +0100 Subject: [PATCH 109/144] tests/NIT/nit.sh: generatecfg_upsmon_trivial(): refer to NOTIFYCMD=...notifyme-debug where possible [#2709] Signed-off-by: Jim Klimov --- tests/NIT/nit.sh | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 9ead5562f5..5b99256a7e 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -571,12 +571,25 @@ updatecfg_upsmon_supplies() { generatecfg_upsmon_trivial() { # Populate the configs for the run rm -f "$NUT_STATEPATH/upsmon.sd.log" - ( echo 'MINSUPPLIES 0' > "$NUT_CONFPATH/upsmon.conf" || exit - echo 'SHUTDOWNCMD "echo \"`date`: TESTING_DUMMY_SHUTDOWN_NOW\" | tee \"'"$NUT_STATEPATH"'/upsmon.sd.log\" "' >> "$NUT_CONFPATH/upsmon.conf" || exit + ( echo 'MINSUPPLIES 0' > "$NUT_CONFPATH/upsmon.conf" || exit + echo 'SHUTDOWNCMD "echo \"`date`: TESTING_DUMMY_SHUTDOWN_NOW\" | tee \"'"$NUT_STATEPATH"'/upsmon.sd.log\" "' >> "$NUT_CONFPATH/upsmon.conf" || exit + + if [ -x "${TOP_SRCDIR-}/scripts/misc/notifyme-debug" ] ; then + echo "NOTIFYCMD \"TEMPDIR='${NUT_STATEPATH}' ${TOP_SRCDIR-}/scripts/misc/notifyme-debug\"" >> "$NUT_CONFPATH/upsmon.conf" || exit + + # NOTE: "SYSLOG" typically ends up in console log of the NIT run and + # "EXEC" goes to a log file like tests/NIT/tmp/run/notifyme-399.log + if [ -s "${TOP_SRCDIR-}/conf/upsmon.conf.sample.in" ] ; then + grep -E '# NOTIFYFLAG .*SYSLOG\+WALL$' \ + < "${TOP_SRCDIR-}/conf/upsmon.conf.sample.in" \ + | sed 's,^# \(NOTIFYFLAG[^A-Z_]*[A-Z_]*\)[^A-Z_]*SYSLOG.*$,\1\tEXEC+SYSLOG,' \ + >> "$NUT_CONFPATH/upsmon.conf" || exit + fi + fi - if [ -n "${NUT_DEBUG_MIN-}" ] ; then - echo "DEBUG_MIN ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/upsmon.conf" || exit - fi + if [ -n "${NUT_DEBUG_MIN-}" ] ; then + echo "DEBUG_MIN ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/upsmon.conf" || exit + fi ) || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" if [ "`id -u`" = 0 ]; then From 3d6ccd867d320505353dcac9ec6bda5484831007 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 16:05:10 +0100 Subject: [PATCH 110/144] common/common.c: refactor upsnotify_suggest_NUT_QUIET_INIT_UPSNOTIFY_once() [#1590, #2136] Signed-off-by: Jim Klimov --- common/common.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/common/common.c b/common/common.c index 3e0aa4336f..278d2c1694 100644 --- a/common/common.c +++ b/common/common.c @@ -2208,6 +2208,21 @@ const char *str_upsnotify_state(upsnotify_state_t state) { } } +static void upsnotify_suggest_NUT_QUIET_INIT_UPSNOTIFY_once(void) { + static int reported = 0; + + if (reported) + return; + + reported = 1; + + if (getenv("NUT_QUIET_INIT_UPSNOTIFY")) + return; + + upsdebugx(1, "On systems without service units, " + "consider `export NUT_QUIET_INIT_UPSNOTIFY=true`"); +} + /* Send (daemon) state-change notifications to an * external service management framework such as systemd */ @@ -2307,9 +2322,9 @@ int upsnotify(upsnotify_state_t state, const char *fmt, ...) "skipped for libcommonclient build, " "will not spam more about it", __func__, str_upsnotify_state(state)); - upsdebugx(1, "On systems without service units, " - "consider `export NUT_QUIET_INIT_UPSNOTIFY=true`"); + upsnotify_suggest_NUT_QUIET_INIT_UPSNOTIFY_once(); } + upsnotify_reported_disabled_systemd = 1; # else /* not WITHOUT_LIBSYSTEMD */ if (!getenv("NOTIFY_SOCKET")) { @@ -2319,8 +2334,7 @@ int upsnotify(upsnotify_state_t state, const char *fmt, ...) "was requested, but not running as a service " "unit now, will not spam more about it", __func__, str_upsnotify_state(state)); - upsdebugx(1, "On systems without service units, " - "consider `export NUT_QUIET_INIT_UPSNOTIFY=true`"); + upsnotify_suggest_NUT_QUIET_INIT_UPSNOTIFY_once(); } upsnotify_reported_disabled_systemd = 1; } else { @@ -2616,9 +2630,8 @@ int upsnotify(upsnotify_state_t state, const char *fmt, ...) "no notification tech defined, " "will not spam more about it", __func__, str_upsnotify_state(state)); - upsdebugx(1, "On systems without service units, " - "consider `export NUT_QUIET_INIT_UPSNOTIFY=true`"); upsnotify_reported_disabled_notech = 1; + upsnotify_suggest_NUT_QUIET_INIT_UPSNOTIFY_once(); } else { upsdebugx(6, "%s: failed to notify about state %s", @@ -2632,9 +2645,8 @@ int upsnotify(upsnotify_state_t state, const char *fmt, ...) upsdebugx(upsnotify_report_verbosity, "%s: logged the systemd watchdog situation once, " "will not spam more about it", __func__); - upsdebugx(1, "On systems without service units, " - "consider `export NUT_QUIET_INIT_UPSNOTIFY=true`"); upsnotify_reported_watchdog_systemd = 1; + upsnotify_suggest_NUT_QUIET_INIT_UPSNOTIFY_once(); } # endif #endif From 66a4c31e27c2d2366447f1e2b1b34dbac41f7701 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 20:30:02 +0100 Subject: [PATCH 111/144] common/common.c: debug-log isPreparingForSleep() outcomes (when extremely high verbosity level is on) [#1070] Signed-off-by: Jim Klimov --- common/common.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/common.c b/common/common.c index 278d2c1694..b4f1b5456f 100644 --- a/common/common.c +++ b/common/common.c @@ -308,6 +308,8 @@ int isPreparingForSleep(void) if (isSupported_PreparingForSleep == 0) { /* Already determined that we can not use it, e.g. due to perms */ errno = isSupported_PreparingForSleep_errno; + upsdebug_with_errno(8, "%s: isSupported_PreparingForSleep=%d", + __func__, isSupported_PreparingForSleep); return -errno; } @@ -357,16 +359,19 @@ int isPreparingForSleep(void) if (val == prev) { /* Unchanged */ + upsdebugx(8, "%s: state unchanged", __func__); return -1; } /* First run and not immediately going to sleep, assume unchanged (no-op for upsmon et al) */ if (prev < 0 && !val) { prev = val; + upsdebugx(8, "%s: state unchanged (assumed): first run and not immediately going to sleep", __func__); return -1; } /* 0 or 1 */ + upsdebugx(8, "%s: state changed): %" PRIi32 " -> %" PRIi32, __func__, prev, val); prev = val; return val; } From a6496a6af1c1b90ca4b836bd0f0cf9c07d6ade3b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 20:30:37 +0100 Subject: [PATCH 112/144] common/common.c: only close_sdbus_once() if we are the same PID that originally opened it [#1070] Signed-off-by: Jim Klimov --- common/common.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/common/common.c b/common/common.c index b4f1b5456f..a51dfa5f27 100644 --- a/common/common.c +++ b/common/common.c @@ -79,6 +79,9 @@ static int RET_NERRNO(int ret) { static /*_cleanup_(sd_bus_flush_close_unrefp)*/ sd_bus *systemd_bus = NULL; static int isSupported_Inhibit = -1, isSupported_Inhibit_errno = 0; static int isSupported_PreparingForSleep = -1, isSupported_PreparingForSleep_errno = 0; +/* We can end up forking after opening the SD BUS (e.g. for notifications + * from upsmon) - only the original process should close its own connection */ +static pid_t sdbusOpenedByPID = 0; static void close_sdbus_once(void) { /* Per https://manpages.debian.org/testing/libsystemd-dev/sd_bus_flush_close_unrefp.3.en.html @@ -91,6 +94,13 @@ static void close_sdbus_once(void) { return; } + if (sdbusOpenedByPID && sdbusOpenedByPID != getpid()) { + upsdebugx(3, "%s: skip actual closing (not our connection, likely parent's, just forget it)", __func__); + errno = 0; + systemd_bus = NULL; + return; + } + upsdebugx(1, "%s: trying", __func__); errno = 0; sd_bus_flush_close_unrefp(&systemd_bus); @@ -126,6 +136,10 @@ static int open_sdbus_once(const char *caller) { } else { upsdebugx(1, "%s: succeeded for %s", __func__, NUT_STRARG(caller)); faultReported = 0; + + /* track if we are still the process who owns the bus connection + * by the time we get to close it */ + sdbusOpenedByPID = getpid(); } if (systemd_bus && !openedOnce) { From 8fa18c0417a770417caf816326cc4da681a6d988 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 16:06:21 +0100 Subject: [PATCH 113/144] clients/upsmon.c: break the main sub-loop where we check sleep_inhibitor_status also if exit_flag gets raised (e.g. Ctrl+C during the wait) [#1070] Signed-off-by: Jim Klimov --- clients/upsmon.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index a6ed3d27f5..a1a6106b74 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -3721,7 +3721,7 @@ int main(int argc, char *argv[]) sleep_inhibitor_status = -2; now = start; - while (sleep_inhibitor_status < 0 && dt < sleepval) { + while (sleep_inhibitor_status < 0 && dt < sleepval && !exit_flag) { prev = now; sleep(1); sleep_inhibitor_status = isPreparingForSleep(); @@ -3743,6 +3743,10 @@ int main(int argc, char *argv[]) upsdebugx(2, "Aborting polling delay between main loop cycles because OS is preparing for sleep or just woke up"); goto end_loop_cycle; } + + /* NOTE: if exit_flag was raised in sleep-loop above, + * so we aborted it, we end soon after ifdef/endif, + * and so not handling here specially */ } else { /* sleep tight */ sleep(sleepval); From a1125ab4b1eefa3b71ad02d3bb3a2237fb3c1b8d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 16:27:26 +0100 Subject: [PATCH 114/144] clients/upsmon.c: report start/end/duration of the "sleepval" delay between main loop cycles [#1070] Signed-off-by: Jim Klimov --- clients/upsmon.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index a1a6106b74..f9461c627a 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -3717,6 +3717,7 @@ int main(int argc, char *argv[]) waitpid(-1, NULL, WNOHANG); time(&start); + upsdebugx(4, "Beginning %u-sec delay between main loop cycles", sleepval); if (isPreparingForSleepSupported()) { sleep_inhibitor_status = -2; now = start; @@ -3724,22 +3725,27 @@ int main(int argc, char *argv[]) while (sleep_inhibitor_status < 0 && dt < sleepval && !exit_flag) { prev = now; sleep(1); + /* WARNING: This call can take several seconds itself + * on some systems */ sleep_inhibitor_status = isPreparingForSleep(); time(&now); dt = difftime(now, start); upsdebugx(7, "start=%" PRIiMAX " now=%" PRIiMAX " dt=%g sleepval=%u sleep_inhibitor_status=%d", (intmax_t)start, (intmax_t)now, dt, sleepval, sleep_inhibitor_status); if (dt > (sleepval + 5) || difftime(now, prev) > 5) { - upsdebugx(2, "It seems we have slept without warning or the system clock was changed"); + upsdebugx(2, "It seems we have slept without warning or the system clock was changed (while in delay between main loop cycles)"); if (sleep_inhibitor_status < 0) sleep_inhibitor_status = 0; /* behave as woken up */ } else if (dt < 0) { - upsdebugx(2, "It seems the system clock was changed into the past"); + upsdebugx(2, "It seems the system clock was changed into the past (while in delay between main loop cycles)"); sleep_inhibitor_status = 0; /* behave as woken up */ } } if (sleep_inhibitor_status >= 0) { + time(&end); + upsdebugx(4, "%u-sec delay between main loop cycles finished, took %" PRIiMAX, + sleepval, (intmax_t)difftime(end, start)); upsdebugx(2, "Aborting polling delay between main loop cycles because OS is preparing for sleep or just woke up"); goto end_loop_cycle; } @@ -3752,6 +3758,8 @@ int main(int argc, char *argv[]) sleep(sleepval); } time(&end); + upsdebugx(4, "%u-sec delay between main loop cycles finished, took %" PRIiMAX, + sleepval, (intmax_t)difftime(end, start)); #else maxhandle = 0; memset(&handles, 0, sizeof(handles)); @@ -3770,8 +3778,11 @@ int main(int argc, char *argv[]) maxhandle++; time(&start); + upsdebugx(4, "Beginning %u-sec delay between main loop cycles", sleepval); ret = WaitForMultipleObjects(maxhandle, handles, FALSE, sleepval*1000); time(&end); + upsdebugx(4, "%u-sec delay between main loop cycles finished, took %" PRIiMAX, + sleepval, (intmax_t)difftime(end, start)); if (ret == WAIT_FAILED) { upslogx(LOG_ERR, "Wait failed"); From de0e1c6c53bb768c648a76715ee0cdd0227142b4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 20:26:46 +0100 Subject: [PATCH 115/144] clients/upsmon.c: change time maths in delay between main loop cycles from time_t to more precise timeval [#1070] Signed-off-by: Jim Klimov --- clients/upsmon.c | 64 ++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index f9461c627a..5ad0e5aa1f 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -3582,11 +3582,13 @@ int main(int argc, char *argv[]) while (exit_flag == 0) { utype_t *ups; - time_t start, end, now; + time_t ttNow; + struct timeval start, end, now; #ifndef WIN32 - time_t prev; + struct timeval prev; #endif double dt = 0; + int sleep_overhead_tolerance = 5; if (isInhibitSupported()) init_Inhibitor(prog); @@ -3675,10 +3677,11 @@ int main(int argc, char *argv[]) upsnotify(NOTIFY_STATE_RELOADING, NULL); init_Inhibitor(prog); - time(&now); + gettimeofday(&now, NULL); + time(&ttNow); for (ups = firstups; ups != NULL; ups = ups->next) { ups->status = 0; - ups->lastpoll = now; + ups->lastpoll = ttNow; } set_reload_flag(1); @@ -3716,23 +3719,36 @@ int main(int argc, char *argv[]) /* reap children that have exited */ waitpid(-1, NULL, WNOHANG); - time(&start); + gettimeofday(&start, NULL); upsdebugx(4, "Beginning %u-sec delay between main loop cycles", sleepval); if (isPreparingForSleepSupported()) { sleep_inhibitor_status = -2; + /* talking to sdbus and/or many short sleeps + * on congested systems can take time */ + sleep_overhead_tolerance = 15; now = start; while (sleep_inhibitor_status < 0 && dt < sleepval && !exit_flag) { prev = now; - sleep(1); + upsdebugx(7, "delay between main loop cycles: before sleep 1..."); /* WARNING: This call can take several seconds itself - * on some systems */ + * on some systems, seen e.g. with Ubuntu in WSL after + * the PC spent some life-time sleeping */ + sleep(1); + upsdebugx(7, "delay between main loop cycles: after sleep 1..."); sleep_inhibitor_status = isPreparingForSleep(); - time(&now); - dt = difftime(now, start); - upsdebugx(7, "start=%" PRIiMAX " now=%" PRIiMAX " dt=%g sleepval=%u sleep_inhibitor_status=%d", - (intmax_t)start, (intmax_t)now, dt, sleepval, sleep_inhibitor_status); - if (dt > (sleepval + 5) || difftime(now, prev) > 5) { + upsdebugx(7, "delay between main loop cycles: after isPreparingForSleep()..."); + gettimeofday(&now, NULL); + dt = difftimeval(now, start); + upsdebugx(7, "start=%" PRIiMAX ".%06" PRIiMAX + " prev=%" PRIiMAX ".%06" PRIiMAX + " now=%" PRIiMAX ".%06" PRIiMAX + " dt=%.06f sleepval=%u sleep_inhibitor_status=%d", + (intmax_t)start.tv_sec, (intmax_t)start.tv_usec, + (intmax_t)prev.tv_sec, (intmax_t)prev.tv_usec, + (intmax_t)now.tv_sec, (intmax_t)now.tv_usec, + dt, sleepval, sleep_inhibitor_status); + if (dt > (sleepval + sleep_overhead_tolerance) || difftimeval(now, prev) > sleep_overhead_tolerance) { upsdebugx(2, "It seems we have slept without warning or the system clock was changed (while in delay between main loop cycles)"); if (sleep_inhibitor_status < 0) sleep_inhibitor_status = 0; /* behave as woken up */ @@ -3743,9 +3759,9 @@ int main(int argc, char *argv[]) } if (sleep_inhibitor_status >= 0) { - time(&end); - upsdebugx(4, "%u-sec delay between main loop cycles finished, took %" PRIiMAX, - sleepval, (intmax_t)difftime(end, start)); + gettimeofday(&end, NULL); + upsdebugx(4, "%u-sec delay between main loop cycles finished, took %.06f", + sleepval, difftimeval(end, start)); upsdebugx(2, "Aborting polling delay between main loop cycles because OS is preparing for sleep or just woke up"); goto end_loop_cycle; } @@ -3757,9 +3773,9 @@ int main(int argc, char *argv[]) /* sleep tight */ sleep(sleepval); } - time(&end); - upsdebugx(4, "%u-sec delay between main loop cycles finished, took %" PRIiMAX, - sleepval, (intmax_t)difftime(end, start)); + gettimeofday(&end, NULL); + upsdebugx(4, "%u-sec delay between main loop cycles finished, took %.06f", + sleepval, difftimeval(end, start)); #else maxhandle = 0; memset(&handles, 0, sizeof(handles)); @@ -3777,12 +3793,12 @@ int main(int argc, char *argv[]) handles[maxhandle] = pipe_connection_overlapped.hEvent; maxhandle++; - time(&start); + gettimeofday(&start, NULL); upsdebugx(4, "Beginning %u-sec delay between main loop cycles", sleepval); ret = WaitForMultipleObjects(maxhandle, handles, FALSE, sleepval*1000); - time(&end); - upsdebugx(4, "%u-sec delay between main loop cycles finished, took %" PRIiMAX, - sleepval, (intmax_t)difftime(end, start)); + gettimeofday(&end, NULL); + upsdebugx(4, "%u-sec delay between main loop cycles finished, took %.06f", + sleepval, difftimeval(end, start)); if (ret == WAIT_FAILED) { upslogx(LOG_ERR, "Wait failed"); @@ -3831,8 +3847,8 @@ int main(int argc, char *argv[]) /* General-purpose handling of time jumps for OSes/run-times * without NUT direct support for suspend/inhibit */ - dt = difftime(end, start); - if (dt > (sleepval + 5)) { + dt = difftimeval(end, start); + if (dt > (sleepval + sleep_overhead_tolerance)) { upsdebugx(2, "It seems we have slept without warning or the system clock was changed"); if (sleep_inhibitor_status < 0) sleep_inhibitor_status = 0; /* behave as woken up */ From c9dac611665fb58dd3e8fb834ca2f5fb05383c1c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 20:24:17 +0100 Subject: [PATCH 116/144] clients/upsmon.c: notify(): clarify (when debug-logging) upsmon-centric messages if upsname==NULL [#415, #2709] Signed-off-by: Jim Klimov --- clients/upsmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 5ad0e5aa1f..2a4f02208c 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -299,7 +299,7 @@ static void notify(const char *notice, int flags, const char *ntype, #endif upsdebugx(6, "%s: sending notification for [%s]: type %s with flags 0x%04x: %s", - __func__, upsname, ntype, flags, notice); + __func__, upsname ? upsname : "upsmon itself", ntype, flags, notice); if (flag_isset(flags, NOTIFY_IGNORE)) { upsdebugx(6, "%s: NOTIFY_IGNORE", __func__); From 9af439d5d23f8d2ebfd6df203b7e8989101504f3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Dec 2024 21:23:39 +0100 Subject: [PATCH 117/144] clients/upsmon.c: parse_status(): fix releasing of ups->status_tokens [#415, #2709] Signed-off-by: Jim Klimov --- clients/upsmon.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 2a4f02208c..7f255d6748 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -2681,7 +2681,13 @@ static void parse_status(utype_t *ups, char *status) if (node->left) { node->left->right = node->right; /* May be NULL*/ } - /* forget the neighbors before dropping the "tree" */ + + if (node == ups->status_tokens) { + ups->status_tokens = node->left ? node->left : node->right; + } + + /* forget the neighbors before dropping the remaining + * "tree" of one node */ node->right = NULL; node->left = NULL; @@ -2696,8 +2702,12 @@ static void parse_status(utype_t *ups, char *status) do_notify(ups, NOTIFY_OTHER, other_stat_words); } else { do_notify(ups, NOTIFY_NOTOTHER, NULL); - state_infofree(ups->status_tokens); - ups->status_tokens = NULL; + + /* No words remain, drop the tree if still there */ + if (ups->status_tokens) { + state_infofree(ups->status_tokens); + ups->status_tokens = NULL; + } } } From 50965c0483c88504148ce62cf0ac6dcaeeaeea73 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 6 Dec 2024 09:35:58 +0100 Subject: [PATCH 118/144] clients/upsmon.c: redefine_ups(): comment a concern about "pv" changes Signed-off-by: Jim Klimov --- clients/upsmon.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 7f255d6748..a8f4a38d16 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -1593,6 +1593,17 @@ static void redefine_ups(utype_t *ups, unsigned int pv, const char *un, ups->retain = 1; if (ups->pv != pv) { + /* TOCHECK: We start a config loading loop with totalpv=0 + * initially or in reload_conf(), and ignore duplicates + * in addups() unless reloading. If we reload a config + * with two definitions of same ups->sys name, will their + * power values be not ignored and add up? Should we + * subtract the older value from totalpv here? How do + * we differentiate (cookie?) first seeing an ups->sys + * which we knew about earlier during a reload_conf() + * from seeing it again? This is a somewhat rare case + * of self-inflicted pain, if it does even happen :) + */ upslogx(LOG_INFO, "UPS [%s]: redefined power value to %d", ups->sys, pv); ups->pv = pv; @@ -1669,7 +1680,8 @@ static void redefine_ups(utype_t *ups, unsigned int pv, const char *un, /* secondary|slave -> primary|master */ if ( ( (!strcasecmp(managerialOption, "primary")) || (!strcasecmp(managerialOption, "master")) ) - && (!flag_isset(ups->status, ST_PRIMARY)) ) { + && (!flag_isset(ups->status, ST_PRIMARY)) + ) { upslogx(LOG_INFO, "UPS [%s]: redefined as a primary", ups->sys); setflag(&ups->status, ST_PRIMARY); @@ -1681,7 +1693,8 @@ static void redefine_ups(utype_t *ups, unsigned int pv, const char *un, /* primary|master -> secondary|slave */ if ( ( (!strcasecmp(managerialOption, "secondary")) || (!strcasecmp(managerialOption, "slave")) ) - && (flag_isset(ups->status, ST_PRIMARY)) ) { + && (flag_isset(ups->status, ST_PRIMARY)) + ) { upslogx(LOG_INFO, "UPS [%s]: redefined as a secondary", ups->sys); clearflag(&ups->status, ST_PRIMARY); return; From a36bc2405d70214e46c9398641870abd118fba1f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 6 Dec 2024 09:49:11 +0100 Subject: [PATCH 119/144] clients/upsmon.c: addups(): start with definitive states of all fields Kudos to `make check-NI-sandbox-devel` and LD_LIBRARY_PATH=`pwd`/clients/.libs valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./clients/.libs/upsmon -F Signed-off-by: Jim Klimov --- clients/upsmon.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index a8f4a38d16..24aab74dc9 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -1768,7 +1768,8 @@ static void addups(int reloading, const char *sys, const char *pvs, tmp = tmp->next; } - tmp = xmalloc(sizeof(utype_t)); + tmp = xcalloc(1, sizeof(utype_t)); + /* TOTHINK: init (UPSCONN_t)tmp->conn struct fields too? */ tmp->sys = xstrdup(sys); tmp->pv = pv; @@ -1782,9 +1783,13 @@ static void addups(int reloading, const char *sys, const char *pvs, tmp->status_tokens = NULL; tmp->retain = 1; - /* ignore initial COMMOK and ONLINE by default */ + /* ignore initial COMMOK, ONLINE, etc. by default */ tmp->commstate = -1; tmp->linestate = -1; + tmp->offstate = -1; + tmp->bypassstate = -1; + tmp->ecostate = -1; + tmp->alarmstate = -1; /* forget poll-failure logging throttling */ tmp->pollfail_log_throttle_count = -1; @@ -1795,6 +1800,9 @@ static void addups(int reloading, const char *sys, const char *pvs, tmp->lastrbwarn = 0; tmp->lastncwarn = 0; + tmp->offsince = 0; + tmp->oblbsince = 0; + if ( (!strcasecmp(managerialOption, "primary")) || (!strcasecmp(managerialOption, "master")) ) { setflag(&tmp->status, ST_PRIMARY); From bd97de8e9b8623b5486586c18b9f2233dc6828dd Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 6 Dec 2024 09:50:18 +0100 Subject: [PATCH 120/144] Revert "common/common.c: only close_sdbus_once() if we are the same PID that originally opened it [#1070]" This reverts commit a6496a6af1c1b90ca4b836bd0f0cf9c07d6ade3b: close from a child process does not seem to impact parent, but a non-close is a memory leak when such child exits. Signed-off-by: Jim Klimov --- common/common.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/common/common.c b/common/common.c index a51dfa5f27..b4f1b5456f 100644 --- a/common/common.c +++ b/common/common.c @@ -79,9 +79,6 @@ static int RET_NERRNO(int ret) { static /*_cleanup_(sd_bus_flush_close_unrefp)*/ sd_bus *systemd_bus = NULL; static int isSupported_Inhibit = -1, isSupported_Inhibit_errno = 0; static int isSupported_PreparingForSleep = -1, isSupported_PreparingForSleep_errno = 0; -/* We can end up forking after opening the SD BUS (e.g. for notifications - * from upsmon) - only the original process should close its own connection */ -static pid_t sdbusOpenedByPID = 0; static void close_sdbus_once(void) { /* Per https://manpages.debian.org/testing/libsystemd-dev/sd_bus_flush_close_unrefp.3.en.html @@ -94,13 +91,6 @@ static void close_sdbus_once(void) { return; } - if (sdbusOpenedByPID && sdbusOpenedByPID != getpid()) { - upsdebugx(3, "%s: skip actual closing (not our connection, likely parent's, just forget it)", __func__); - errno = 0; - systemd_bus = NULL; - return; - } - upsdebugx(1, "%s: trying", __func__); errno = 0; sd_bus_flush_close_unrefp(&systemd_bus); @@ -136,10 +126,6 @@ static int open_sdbus_once(const char *caller) { } else { upsdebugx(1, "%s: succeeded for %s", __func__, NUT_STRARG(caller)); faultReported = 0; - - /* track if we are still the process who owns the bus connection - * by the time we get to close it */ - sdbusOpenedByPID = getpid(); } if (systemd_bus && !openedOnce) { From a14a55f84326f716879f0cbab3daa14da0744857 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 6 Dec 2024 10:09:00 +0100 Subject: [PATCH 121/144] tests/NIT/nit.sh: generatecfg_upsmon_trivial() allow to toggle NOTIFYFLAG targets Signed-off-by: Jim Klimov --- tests/NIT/nit.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 5b99256a7e..3aa9bd1b46 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -23,6 +23,8 @@ # TESTDIR=/tmp/nut-NIT to propose a location for "etc" and "run" # mock locations (under some circumstances, the # script can anyway choose to `mktemp` another) +# NUT_DEBUG_UPSMON_NOTIFY_SYSLOG=false To *disable* upsmon +# notifications spilling to the script's console. # # Common sandbox run for testing goes from NUT root build directory like: # DEBUG_SLEEP=600 NUT_PORT=12345 NIT_CASE=testcase_sandbox_start_drivers_after_upsd NUT_FOREGROUND_WITH_PID=true make check-NIT & @@ -574,15 +576,23 @@ generatecfg_upsmon_trivial() { ( echo 'MINSUPPLIES 0' > "$NUT_CONFPATH/upsmon.conf" || exit echo 'SHUTDOWNCMD "echo \"`date`: TESTING_DUMMY_SHUTDOWN_NOW\" | tee \"'"$NUT_STATEPATH"'/upsmon.sd.log\" "' >> "$NUT_CONFPATH/upsmon.conf" || exit + NOTIFYTGT="" if [ -x "${TOP_SRCDIR-}/scripts/misc/notifyme-debug" ] ; then echo "NOTIFYCMD \"TEMPDIR='${NUT_STATEPATH}' ${TOP_SRCDIR-}/scripts/misc/notifyme-debug\"" >> "$NUT_CONFPATH/upsmon.conf" || exit # NOTE: "SYSLOG" typically ends up in console log of the NIT run and # "EXEC" goes to a log file like tests/NIT/tmp/run/notifyme-399.log + NOTIFYTGT="EXEC" + fi + if [ x"$NUT_DEBUG_UPSMON_NOTIFY_SYSLOG" != xfalse ] ; then + [ -n "${NOTIFYTGT}" ] && NOTIFYTGT="${NOTIFYTGT}+SYSLOG" || NOTIFYTGT="SYSLOG" + fi + + if [ -n "${NOTIFYTGT}" ]; then if [ -s "${TOP_SRCDIR-}/conf/upsmon.conf.sample.in" ] ; then grep -E '# NOTIFYFLAG .*SYSLOG\+WALL$' \ < "${TOP_SRCDIR-}/conf/upsmon.conf.sample.in" \ - | sed 's,^# \(NOTIFYFLAG[^A-Z_]*[A-Z_]*\)[^A-Z_]*SYSLOG.*$,\1\tEXEC+SYSLOG,' \ + | sed 's,^# \(NOTIFYFLAG[^A-Z_]*[A-Z_]*\)[^A-Z_]*SYSLOG.*$,\1\t'"${NOTIFYTGT}"',' \ >> "$NUT_CONFPATH/upsmon.conf" || exit fi fi From ef3c86cc3f1585d98a7eed87d00671ce28b839b3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 6 Dec 2024 10:10:51 +0100 Subject: [PATCH 122/144] tests/NIT/nit.sh: refer to scripts/valgrind for debugging in a sandbox Signed-off-by: Jim Klimov --- tests/NIT/nit.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 3aa9bd1b46..2da6495092 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -1698,6 +1698,7 @@ if [ -n "${DEBUG_SLEEP-}" ] ; then log_info "You may want to press Ctrl+Z now and command 'bg' to the shell, if you did not start '$0 &' backgrounded already" log_info "To kill the script early, return it to foreground with 'fg' and press Ctrl+C, or 'kill -2 \$PID_NIT_SCRIPT' (kill -2 $$)" + log_info "To trace built programs, check scripts/valgrind/README.adoc and LD_LIBRARY_PATH for this build" log_info "Remember that in shell/scripting you can probe for '${NUT_CONFPATH}/NIT.env-sandbox-ready' which only appears at this moment" touch "${NUT_CONFPATH}/NIT.env-sandbox-ready" From 9046e230a5eaca6227d3a2542c5112e19559310d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 6 Dec 2024 10:21:35 +0100 Subject: [PATCH 123/144] clients/upsmon.c: ups_free(): release ups->status_tokens if needed [#415, #2709] Signed-off-by: Jim Klimov --- clients/upsmon.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clients/upsmon.c b/clients/upsmon.c index 24aab74dc9..14ef3d364d 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -2344,6 +2344,14 @@ static void ups_free(utype_t *ups) free(ups->hostname); free(ups->un); free(ups->pw); + + /* We usually free and nullify these as we have no use for them, + * but if something remains (had active alert etc.) - do it here: */ + if (ups->status_tokens) { + state_infofree(ups->status_tokens); + ups->status_tokens = NULL; + } + free(ups); } From 245e18e09175f2f14eead29a95a35182dfc1c037 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 6 Dec 2024 10:22:12 +0100 Subject: [PATCH 124/144] clients/upsmon.c: upsmon_cleanup(): free(configfile) in the end Signed-off-by: Jim Klimov --- clients/upsmon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/clients/upsmon.c b/clients/upsmon.c index 14ef3d364d..8c6a7876d2 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -2376,6 +2376,7 @@ static void upsmon_cleanup(void) free(shutdowncmd); free(notifycmd); free(powerdownflag); + free(configfile); for (i = 0; notifylist[i].name != NULL; i++) { free(notifylist[i].msg); From fb1d142bd740332b8131fc2e3945626dcf225019 Mon Sep 17 00:00:00 2001 From: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> Date: Sun, 8 Dec 2024 11:50:25 +0200 Subject: [PATCH 125/144] spme missing vars added Signed-off-by: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> --- data/cmdvartab | 4 ++++ docs/nut-names.txt | 3 +++ 2 files changed, 7 insertions(+) diff --git a/data/cmdvartab b/data/cmdvartab index 67c03af585..ae6de7fdc0 100644 --- a/data/cmdvartab +++ b/data/cmdvartab @@ -74,6 +74,8 @@ VARDESC input.transfer.bypass.outlimits "Rule for auto transfer on Bypass when o VARDESC input.bypass.switchable "Input auto transfer on Bypass when overload or out of tolerance (enabled or disabled)" VARDESC input.bypass.switch.on "Put the UPS in Bypass mode" VARDESC input.bypass.switch.off "Take the UPS out of Bypass mode" +VARDESC input.bypass.voltage "Input bypass voltage (V)" +VARDESC input.bypass.frequency "Input bypass frequency (Hz)" VARDESC input.sensitivity "Input power sensitivity" VARDESC input.quality "Input power quality" VARDESC input.current "Input current (A)" @@ -131,6 +133,7 @@ VARDESC battery.energysave.load "Switch off UPS if on battery and load level low VARDESC battery.energysave.delay "Delay before switch off UPS if on battery and load level low (min)" VARDESC battery.energysave.realpower "Switch off UPS if on battery and load level lower (Watts)" VARDESC battery.charger.status "Battery charger status" +VARDESC battery.charger.type "ABM charger type" VARDESC ambient.temperature "Ambient temperature (degrees C)" VARDESC ambient.temperature.alarm "Ambient temperature alarm is active" @@ -195,6 +198,7 @@ VARDESC outlet.2.delay.shutdown "Interval to wait before shutting down this outl VARDESC outlet.2.delay.start "Interval to wait before restarting this outlet (seconds)" VARDESC device.part "Device Part Number" +VARDESC device.mfr "Device Manufacturer" VARDESC server.info "Server information" VARDESC server.version "Server version" diff --git a/docs/nut-names.txt b/docs/nut-names.txt index c37dde8f34..461c828e3e 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -320,6 +320,8 @@ input: Incoming line/power information | enabled | input.bypass.switch.on | Put the UPS in Bypass mode | on | input.bypass.switch.off | Take the UPS out of Bypass mode | disabled +| input.bypass.voltage | Input bypass voltage (V) | 233 +| input.bypass.frequency | Input bypass frequency (Hz) | 50 | input.load | Load on (ePDU) input (percent of full) | 25 | input.realpower | Current sum value of all (ePDU) @@ -534,6 +536,7 @@ battery: Any battery details to "Warning" state (percent) | 50 | battery.charger.status | Status of the battery charger (see the note below) | charging +| battery.charger.type | ABM charger type | ABM | battery.voltage | Battery voltage (V) | 24.84 | battery.voltage.cell.max | Maximum battery voltage seen of the Li-ion cell (V) | 3.44 From 3a16158c9111790084ebf49cec52781c50284614 Mon Sep 17 00:00:00 2001 From: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:28:20 +0200 Subject: [PATCH 126/144] battery.charger.type description changed Signed-off-by: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> --- data/cmdvartab | 2 +- docs/nut-names.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/cmdvartab b/data/cmdvartab index ae6de7fdc0..48f082d790 100644 --- a/data/cmdvartab +++ b/data/cmdvartab @@ -133,7 +133,7 @@ VARDESC battery.energysave.load "Switch off UPS if on battery and load level low VARDESC battery.energysave.delay "Delay before switch off UPS if on battery and load level low (min)" VARDESC battery.energysave.realpower "Switch off UPS if on battery and load level lower (Watts)" VARDESC battery.charger.status "Battery charger status" -VARDESC battery.charger.type "ABM charger type" +VARDESC battery.charger.type "Type of battery charger" VARDESC ambient.temperature "Ambient temperature (degrees C)" VARDESC ambient.temperature.alarm "Ambient temperature alarm is active" diff --git a/docs/nut-names.txt b/docs/nut-names.txt index 461c828e3e..7e46fdda9e 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -536,7 +536,7 @@ battery: Any battery details to "Warning" state (percent) | 50 | battery.charger.status | Status of the battery charger (see the note below) | charging -| battery.charger.type | ABM charger type | ABM +| battery.charger.type | Type of battery charger | ABM | battery.voltage | Battery voltage (V) | 24.84 | battery.voltage.cell.max | Maximum battery voltage seen of the Li-ion cell (V) | 3.44 From 0f2667017736a60636203c997c5188f365d05be2 Mon Sep 17 00:00:00 2001 From: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> Date: Sun, 8 Dec 2024 17:00:09 +0200 Subject: [PATCH 127/144] add device.x descrptions + more to cmdvartab Signed-off-by: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> --- data/cmdvartab | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/data/cmdvartab b/data/cmdvartab index 48f082d790..de64414509 100644 --- a/data/cmdvartab +++ b/data/cmdvartab @@ -42,6 +42,7 @@ VARDESC ups.type "UPS type" VARDESC ups.start.auto "UPS starts when mains is (re)applied" VARDESC ups.start.battery "Allow to start UPS from battery" VARDESC ups.start.reboot "UPS reboots when power returns during shutdown delay" +VARDESC ups.shutdown "Enable or disable UPS shutdown ability (poweroff)" VARDESC input.voltage "Input voltage (V)" VARDESC input.voltage.extended "Extended input voltage range" @@ -197,8 +198,18 @@ VARDESC outlet.2.autoswitch.charge.low "Remaining battery level to power off thi VARDESC outlet.2.delay.shutdown "Interval to wait before shutting down this outlet (seconds)" VARDESC outlet.2.delay.start "Interval to wait before restarting this outlet (seconds)" -VARDESC device.part "Device Part Number" -VARDESC device.mfr "Device Manufacturer" +VARDESC device.part "Device part number" +VARDESC device.mfr "Device manufacturer" +VARDESC device.model "Device model" +VARDESC device.serial "Device serial number" +VARDESC device.type "Device type" +VARDESC device.description "Device description" +VARDESC device.contact "Device administrator name" +VARDESC device.location "Device physical location" +VARDESC device.macaddr "Physical network address of the device" +VARDESC device.uptime "Device uptime in seconds" +VARDESC device.count "Total number of daisychained devices" +VARDESC device.usb.version "Device USB version" VARDESC server.info "Server information" VARDESC server.version "Server version" @@ -211,6 +222,7 @@ VARDESC driver.flag.allow_killpower "Safety flip-switch to allow the driver daem VARDESC driver.version "Driver version - NUT release" VARDESC driver.version.internal "Internal driver version" VARDESC driver.version.usb "USB library version" +VARDESC driver.state "Current state in driver's lifecycle" # FIXME: driver.parameter and driver.flag can have many possible members # From 331a4310761156d07bffbc9d4a9f98fe47b06d22 Mon Sep 17 00:00:00 2001 From: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> Date: Sun, 8 Dec 2024 17:04:14 +0200 Subject: [PATCH 128/144] add device.usb.version to nut-names.txt Signed-off-by: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> --- docs/nut-names.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/nut-names.txt b/docs/nut-names.txt index 7e46fdda9e..f47617e129 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -134,6 +134,7 @@ during a transition period. The ups.* data will then be removed. | device.macaddr | Physical network address of the device | 68:b5:99:f5:89:27 | device.uptime | Device uptime in seconds | 1782 | device.count | Total number of daisychained devices | 1 +| device.usb.version | Device USB version | 01.29 |==================================================================================== [NOTE] From 7a1d53c97969e14e40a5fafdf1b4b3a95c5adc2d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 8 Dec 2024 18:35:42 +0100 Subject: [PATCH 129/144] docs/new-drivers.txt: clarify relation of dstate_addcmd() and upsh.instcmd and upsdrv_initinfo() Signed-off-by: Jim Klimov --- docs/new-drivers.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/new-drivers.txt b/docs/new-drivers.txt index c711bf49ab..b696141bc3 100644 --- a/docs/new-drivers.txt +++ b/docs/new-drivers.txt @@ -124,7 +124,9 @@ and it doesn't appear to be connected, display an error and exit. This is the last time your driver is allowed to bail out. This is usually a good place to create variables like `ups.mfr`, -`ups.model`, `ups.serial`, and other "one time only" items. +`ups.model`, `ups.serial`, determine and declare supported instant +commands (maybe model-dependent, typically for all devices supported +by the driver), and other "one time only" items. upsdrv_updateinfo ~~~~~~~~~~~~~~~~~ @@ -740,6 +742,12 @@ If your hardware and driver can support a command, register it. dstate_addcmd("load.on"); +Don't forget to define the implementation for such commands in a common +method, and register that your driver has an instant command handler at +all -- with a line in `upsdrv_initinfo()` like: + + upsh.instcmd = blazer_instcmd; + Delays and ser_* functions -------------------------- From b62c2cd3da8eedd09afd8140473daee1959c0d4a Mon Sep 17 00:00:00 2001 From: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> Date: Sun, 8 Dec 2024 19:42:40 +0200 Subject: [PATCH 130/144] some more vars added Signed-off-by: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> --- data/cmdvartab | 2 ++ docs/nut-names.txt | 1 + 2 files changed, 3 insertions(+) diff --git a/data/cmdvartab b/data/cmdvartab index de64414509..8fc4f48948 100644 --- a/data/cmdvartab +++ b/data/cmdvartab @@ -187,6 +187,7 @@ VARDESC outlet.1.ecocontrol "Master Outlet used to automatically power off the s VARDESC outlet.1.autoswitch.charge.low "Remaining battery level to power off this outlet (percent)" VARDESC outlet.1.delay.shutdown "Interval to wait before shutting down this outlet (seconds)" VARDESC outlet.1.delay.start "Interval to wait before restarting this outlet (seconds)" +VARDESC outlet.1.designator "Outlet designator" VARDESC outlet.2.id "Outlet system identifier" VARDESC outlet.2.desc "Outlet description" VARDESC outlet.2.switch "Outlet switch control" @@ -222,6 +223,7 @@ VARDESC driver.flag.allow_killpower "Safety flip-switch to allow the driver daem VARDESC driver.version "Driver version - NUT release" VARDESC driver.version.internal "Internal driver version" VARDESC driver.version.usb "USB library version" +VARDESC driver.version.data "Version of the internal data mapping, for generic drivers" VARDESC driver.state "Current state in driver's lifecycle" # FIXME: driver.parameter and driver.flag can have many possible members diff --git a/docs/nut-names.txt b/docs/nut-names.txt index f47617e129..17600bf9d4 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -722,6 +722,7 @@ of the user manual. | outlet.n.ecocontrol | Master Outlet used to automatically power off the slave outlets | The outlet is not ECO controlled +| outlet.n.designator | Outlet designator | AC OUTPUT | outlet.n.autoswitch.charge.low | Remaining battery level to power off this outlet (percent) | 80 From 622b91aee3005556b0a0965b549acb1d5e45af29 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 8 Dec 2024 18:41:47 +0100 Subject: [PATCH 131/144] drivers/nhs_ser.c: reshuffle HW init code between upsdrv_initups() and upsdrv_initups() [#2692] Signed-off-by: Jim Klimov --- drivers/nhs_ser.c | 76 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/drivers/nhs_ser.c b/drivers/nhs_ser.c index 30d181b079..fb995b9dad 100644 --- a/drivers/nhs_ser.c +++ b/drivers/nhs_ser.c @@ -1657,30 +1657,22 @@ static float get_vin_perc(char * var) { } void upsdrv_initinfo(void) { - char *b = getval("baud"); + /* From docs/new-drivers.txt: + * Try to detect what kind of UPS is out there, + * if any, assuming that's possible for your hardware. + * If there is a way to detect that hardware and it + * doesn't appear to be connected, display an error + * and exit. This is the last time your driver is + * allowed to bail out. + * This is usually a good place to create variables + * like `ups.mfr`, `ups.model`, `ups.serial`, register + * instant commands, and other "one time only" items. + */ upsdebugx(3, "%s: starting...", __func__); - baudrate = DEFAULTBAUD; - - upsdebugx(1, "%s: Port is %s and baud_rate is %s", __func__, device_path, b); + /* TODO: Any instant commands? */ - if (b) - baudrate = atoi(b); - if (device_path) { - if (strcasecmp(device_path, "auto") == 0) - strcpy(porta, DEFAULTPORT); - else - strcpy(porta, device_path); - serial_fd = openfd(porta, baudrate); - if (serial_fd == -1) - fatalx(EXIT_FAILURE, "Unable to open port %s with baud %d", porta, baudrate); - else { - upsdebugx(1, "%s: Communication started on port %s, baud rate %d. Calling updateinfo()", __func__, porta, baudrate); - } - } - else - fatalx(EXIT_FAILURE, "Unable to define port and baud"); upsdebugx(3, "%s: finished", __func__); } @@ -2217,9 +2209,22 @@ void upsdrv_cleanup(void) { } void upsdrv_initups(void) { + /* From docs/new-drivers.txt: + * Open the port (`device_path`) and do any low-level + * things that it may need to start using that port. + * If you have to set DTR or RTS on a serial port, + * do it here. + * Don't do any sort of hardware detection here, since + * you may be quickly going into upsdrv_shutdown next. + */ + + char *b = getval("baud"); + upsdebugx(3, "%s: starting...", __func__); - /* Process optional configuration flags */ + /* Process optional configuration flags that may + * impact HW init methods (debug them or not) + */ if (getval("debug_pkt_raw")) debug_pkt_raw = 1; if (getval("debug_pkt_data")) @@ -2227,7 +2232,34 @@ void upsdrv_initups(void) { if (getval("debug_pkt_hwinfo")) debug_pkt_hwinfo = 1; - upsdrv_initinfo(); + baudrate = DEFAULTBAUD; + + upsdebugx(1, "%s: Port is %s and baud_rate is %s", + __func__, device_path, NUT_STRARG(b)); + + if (b) + baudrate = atoi(b); + if (device_path) { + if (strcasecmp(device_path, "auto") == 0) + strcpy(porta, DEFAULTPORT); + else + strcpy(porta, device_path); + serial_fd = openfd(porta, baudrate); + if (serial_fd == -1) + fatalx(EXIT_FAILURE, "Unable to open port %s with baud %d", + porta, baudrate); + else { + upsdebugx(1, "%s: Communication started on port %s, baud rate %d", + __func__, porta, baudrate); + } + } + else + fatalx(EXIT_FAILURE, "Unable to define port and baud"); + + /* If we got here, the port is opened with desired baud rate. + * If not shutting down ASAP, soon we will call upsdrv_initinfo() + * and "infinitely" loop calling upsdrv_updateinfo() afterwards. + */ upsdebugx(3, "%s: finished", __func__); } From 62c122b2725a422b7131e21b0ad025721a176d13 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 8 Dec 2024 18:59:52 +0100 Subject: [PATCH 132/144] drivers/nhs_ser.c: upsdrv_updateinfo(): simplify reconnect-check, un-indent main code a bit [#2692] Fixed the retries from being an infinite loop along the way... Signed-off-by: Jim Klimov --- drivers/nhs_ser.c | 888 +++++++++++++++++++++++----------------------- 1 file changed, 449 insertions(+), 439 deletions(-) diff --git a/drivers/nhs_ser.c b/drivers/nhs_ser.c index fb995b9dad..32e3b9b56a 100644 --- a/drivers/nhs_ser.c +++ b/drivers/nhs_ser.c @@ -1719,471 +1719,481 @@ void upsdrv_updateinfo(void) { upsinfo ups; upsdebugx(3, "%s: starting...", __func__); - if ((serial_fd <= 0) && (i < retries)) { - upsdebugx(1, "%s: Serial port communications problem", __func__); - while (serial_fd <= 0) { + + /* If comms failed earlier, try to resuscitate */ + if (serial_fd <= 0) { + upsdebugx(1, "%s: Serial port '%s' communications problem", + __func__, porta); + while (serial_fd <= 0 && i < retries) { serial_fd = openfd(porta, baudrate); upsdebugx(1, "%s: Trying to reopen serial...", __func__); usleep(checktime); - retries++; + i++; } } - else { - /* Clean all read buffers to avoid errors: - * To clean OUTPUT buffer is TCOFLUSH. - * To both is TCIOFLUSH. - */ - /* //tcflush(serial_fd, TCIFLUSH); */ - chr = '\0'; - while (read(serial_fd, &chr, 1) > 0) { - if (chr == 0xFF) { /* DataPacket start */ - datapacketstart = true; - } /* end for */ - if (datapacketstart) { - datapacket[datapacket_index] = chr; - datapacket_index++; - if (chr == 0xFE) { /* DataPacket */ - time_t now = time(NULL); - upsdebugx(4, "DATAPACKET INDEX IS %d", datapacket_index); - if (lastdp != 0) { - tempodecorrido = difftime(now, lastdp); - } - lastdp = now; - /* If size is 18 or 50, may be an answer packet. - * Then check if doesn't have already a packet processed. - * We don't need to read all times these information. - * Can be a corrupted packet too. */ - if (((datapacket_index == 18) || (datapacket_index == 50)) && (!lastpkthwinfo.checksum_ok)) { - lastpkthwinfo = mount_hwinfo(datapacket, datapacket_index); - } /* end if */ + if (serial_fd <= 0) { + return; + } + + /* Clean all read buffers to avoid errors: + * To clean OUTPUT buffer is TCOFLUSH. + * To both is TCIOFLUSH. + */ + /* //tcflush(serial_fd, TCIFLUSH); */ + chr = '\0'; + while (read(serial_fd, &chr, 1) > 0) { + if (chr == 0xFF) { /* DataPacket start */ + datapacketstart = true; + } /* end for */ + if (datapacketstart) { + datapacket[datapacket_index] = chr; + datapacket_index++; + if (chr == 0xFE) { /* DataPacket */ + time_t now = time(NULL); + upsdebugx(4, "DATAPACKET INDEX IS %d", datapacket_index); + if (lastdp != 0) { + tempodecorrido = difftime(now, lastdp); + } + lastdp = now; + /* If size is 18 or 50, may be an answer packet. + * Then check if doesn't have already a packet processed. + * We don't need to read all times these information. + * Can be a corrupted packet too. + */ + if (((datapacket_index == 18) || (datapacket_index == 50)) && (!lastpkthwinfo.checksum_ok)) { + lastpkthwinfo = mount_hwinfo(datapacket, datapacket_index); + } /* end if */ + else { + if (datapacket_index == 21) + lastpktdata = mount_datapacket(datapacket, datapacket_index, tempodecorrido, lastpkthwinfo); + } /* end else */ + /* Clean datapacket structure to avoid problems */ + datapacket_index = 0; + memset(datapacket, 0, sizeof(datapacket)); + datapacketstart = false; + if (lastpktdata.checksum_ok) { + /* checksum is OK, then use it to set values */ + upsdebugx(4, "Data Packet seems be OK"); + if (lastpkthwinfo.size == 0) + upsdebugx(2, "Pkt HWINFO is not OK. See if will be requested next time!"); else { - if (datapacket_index == 21) - lastpktdata = mount_datapacket(datapacket, datapacket_index, tempodecorrido, lastpkthwinfo); - } /* end else */ - /* Clean datapacket structure to avoid problems */ - datapacket_index = 0; - memset(datapacket, 0, sizeof(datapacket)); - datapacketstart = false; - if (lastpktdata.checksum_ok) { - /* checksum is OK, then use it to set values */ - upsdebugx(4, "Data Packet seems be OK"); - if (lastpkthwinfo.size == 0) - upsdebugx(2, "Pkt HWINFO is not OK. See if will be requested next time!"); - else { - if (lastpkthwinfo.checksum_ok) { - upsdebugx(4, "Pkt HWINFO is OK. Model is %d, hwversion is %d and swversion is %d", lastpkthwinfo.model, lastpkthwinfo.hardwareversion, lastpkthwinfo.softwareversion); - /* We need to set data on NUT with data - * that I believe that I can calculate. - * Now setting data on NUT */ - ups = getupsinfo(lastpkthwinfo.model); - upsdebugx(4, "UPS Struct data: Code %d Model %s VA %d", ups.upscode, ups.upsdesc, ups.VA); - dstate_setinfo("device.model", "%s", ups.upsdesc); - dstate_setinfo("device.mfr", "%s", MANUFACTURER); - dstate_setinfo("device.serial", "%s", lastpkthwinfo.serial); - dstate_setinfo("device.type", "%s", "ups"); - - /* Setting UPS Status: - * OL -- On line (mains is present): Code below - * OB -- On battery (mains is not present) : Code below - * LB -- Low battery: Code below - * HB -- High battery: NHS doesn't have any variable with that information. Feel free to discover a way to set it - * RB -- The battery needs to be replaced: Well, as mentioned, we can write some infos on nobreak fw, on structures like pkt_hwinfo.year, pkt_hwinfo.month, etc. I never found any equipment with these values. - * CHRG -- The battery is charging: Code below - * DISCHRG -- The battery is discharging (inverter is providing load power): Code Below - * BYPASS -- UPS bypass circuit is active -- no battery protection is available: It's another PROBLEM, because NHS can work in bypass mode in some models, even if you have sealed batteries on it (without any external battery device). On the moment, i'll won't work with that. Feel free to discover how it work correctly. - * CAL -- UPS is currently performing runtime calibration (on battery) - * OFF -- UPS is offline and is not supplying power to the load - * OVER -- UPS is overloaded - * TRIM -- UPS is trimming incoming voltage (called "buck" in some hardware) - * BOOST -- UPS is boosting incoming voltage - * FSD -- Forced Shutdown (restricted use, see the note below) - */ - - /* Decision Chain commented below */ - - /* First we check if system is on battery or not */ - upsdebugx(4, "Set UPS status as OFF and start checking. s_battery_mode is %d", lastpktdata.s_battery_mode); - if (lastpkthwinfo.s_220V_in) { - upsdebugx(4, "I'm on 220v IN!. My overvoltage is %d", lastpkthwinfo.undervoltagein220V); - min_input_power = lastpkthwinfo.undervoltagein220V; - } - else { - upsdebugx(4, "I'm on 120v IN!. My overvoltage is %d", lastpkthwinfo.undervoltagein120V); - min_input_power = lastpkthwinfo.undervoltagein120V; - } - if (lastpktdata.s_battery_mode) { - /* ON BATTERY */ - upsdebugx(4, "UPS is on Battery Mode"); - dstate_setinfo("ups.status", "%s", "OB"); - if (lastpktdata.s_battery_low) { - /* If battery is LOW, warn user! */ - upsdebugx(4, "UPS is on Battery Mode and in Low Battery State"); - dstate_setinfo("ups.status", "%s", "LB"); - } /* end if */ + if (lastpkthwinfo.checksum_ok) { + upsdebugx(4, "Pkt HWINFO is OK. Model is %d, hwversion is %d and swversion is %d", lastpkthwinfo.model, lastpkthwinfo.hardwareversion, lastpkthwinfo.softwareversion); + /* We need to set data on NUT with data + * that I believe that I can calculate. + * Now setting data on NUT + */ + ups = getupsinfo(lastpkthwinfo.model); + upsdebugx(4, "UPS Struct data: Code %d Model %s VA %d", ups.upscode, ups.upsdesc, ups.VA); + dstate_setinfo("device.model", "%s", ups.upsdesc); + dstate_setinfo("device.mfr", "%s", MANUFACTURER); + dstate_setinfo("device.serial", "%s", lastpkthwinfo.serial); + dstate_setinfo("device.type", "%s", "ups"); + + /* Setting UPS Status: + * OL -- On line (mains is present): Code below + * OB -- On battery (mains is not present) : Code below + * LB -- Low battery: Code below + * HB -- High battery: NHS doesn't have any variable with that information. Feel free to discover a way to set it + * RB -- The battery needs to be replaced: Well, as mentioned, we can write some infos on nobreak fw, on structures like pkt_hwinfo.year, pkt_hwinfo.month, etc. I never found any equipment with these values. + * CHRG -- The battery is charging: Code below + * DISCHRG -- The battery is discharging (inverter is providing load power): Code Below + * BYPASS -- UPS bypass circuit is active -- no battery protection is available: It's another PROBLEM, because NHS can work in bypass mode in some models, even if you have sealed batteries on it (without any external battery device). On the moment, i'll won't work with that. Feel free to discover how it work correctly. + * CAL -- UPS is currently performing runtime calibration (on battery) + * OFF -- UPS is offline and is not supplying power to the load + * OVER -- UPS is overloaded + * TRIM -- UPS is trimming incoming voltage (called "buck" in some hardware) + * BOOST -- UPS is boosting incoming voltage + * FSD -- Forced Shutdown (restricted use, see the note below) + */ + + /* Decision Chain commented below */ + + /* First we check if system is on battery or not */ + upsdebugx(4, "Set UPS status as OFF and start checking. s_battery_mode is %d", lastpktdata.s_battery_mode); + if (lastpkthwinfo.s_220V_in) { + upsdebugx(4, "I'm on 220v IN!. My overvoltage is %d", lastpkthwinfo.undervoltagein220V); + min_input_power = lastpkthwinfo.undervoltagein220V; + } + else { + upsdebugx(4, "I'm on 120v IN!. My overvoltage is %d", lastpkthwinfo.undervoltagein120V); + min_input_power = lastpkthwinfo.undervoltagein120V; + } + if (lastpktdata.s_battery_mode) { + /* ON BATTERY */ + upsdebugx(4, "UPS is on Battery Mode"); + dstate_setinfo("ups.status", "%s", "OB"); + if (lastpktdata.s_battery_low) { + /* If battery is LOW, warn user! */ + upsdebugx(4, "UPS is on Battery Mode and in Low Battery State"); + dstate_setinfo("ups.status", "%s", "LB"); + } /* end if */ + } /* end if */ + else { + /* Check if MAINS (power) is not preset. + * Well, we can check pkt_data.s_network_failure too... */ + if ((lastpktdata.vacinrms <= min_input_power) || (lastpktdata.s_network_failure)) { + upsdebugx(4, "UPS has power-in value %0.2f " + "and min_input_power is %d, " + "or network is in failure. Network failure is %d", + lastpktdata.vacinrms, + min_input_power, + lastpktdata.s_network_failure); + dstate_setinfo("ups.status", "%s", "DISCHRG"); } /* end if */ else { - /* Check if MAINS (power) is not preset. - * Well, we can check pkt_data.s_network_failure too... */ - if ((lastpktdata.vacinrms <= min_input_power) || (lastpktdata.s_network_failure)) { - upsdebugx(4, "UPS has power-in value %0.2f " - "and min_input_power is %d, " - "or network is in failure. Network failure is %d", - lastpktdata.vacinrms, - min_input_power, - lastpktdata.s_network_failure); - dstate_setinfo("ups.status", "%s", "DISCHRG"); - } /* end if */ - else { - /* MAINS is present. We need to check some situations. - * NHS only charge if have more than min_input_power. - * If MAINS is less than or equal to min_input_power, - * then the UPS goes to BATTERY */ - if (lastpktdata.vacinrms > min_input_power) { - upsdebugx(4, "UPS is on MAINS"); - if (lastpktdata.s_charger_on) { - upsdebugx(4, "UPS Charging..."); - dstate_setinfo("ups.status", "%s", "CHRG"); - } - else { - if ((lastpktdata.s_network_failure) || (lastpktdata.s_fast_network_failure)) { - upsdebugx(4, "UPS is on battery mode because network failure or fast network failure"); - dstate_setinfo("ups.status", "%s", "OB"); - } /* end if */ - else { - upsdebugx(4, "All is OK. UPS is on ONLINE!"); - dstate_setinfo("ups.status", "%s", "OL"); - } /* end else */ - } /* end else */ - } /* end if */ + /* MAINS is present. We need to check some situations. + * NHS only charge if have more than min_input_power. + * If MAINS is less than or equal to min_input_power, + * then the UPS goes to BATTERY + */ + if (lastpktdata.vacinrms > min_input_power) { + upsdebugx(4, "UPS is on MAINS"); + if (lastpktdata.s_charger_on) { + upsdebugx(4, "UPS Charging..."); + dstate_setinfo("ups.status", "%s", "CHRG"); + } else { - /* Energy is below limit. - * Nobreak is probably in battery mode... */ - if (lastpktdata.s_battery_low) - dstate_setinfo("ups.status", "%s", "LB"); - else { - /* ...or network failure */ + if ((lastpktdata.s_network_failure) || (lastpktdata.s_fast_network_failure)) { + upsdebugx(4, "UPS is on battery mode because network failure or fast network failure"); dstate_setinfo("ups.status", "%s", "OB"); + } /* end if */ + else { + upsdebugx(4, "All is OK. UPS is on ONLINE!"); + dstate_setinfo("ups.status", "%s", "OL"); } /* end else */ } /* end else */ + } /* end if */ + else { + /* Energy is below limit. + * Nobreak is probably in battery mode... */ + if (lastpktdata.s_battery_low) + dstate_setinfo("ups.status", "%s", "LB"); + else { + /* ...or network failure */ + dstate_setinfo("ups.status", "%s", "OB"); + } /* end else */ } /* end else */ } /* end else */ + } /* end else */ - numbat = get_numbat(); - if (numbat == 0) - numbat = lastpkthwinfo.numbatteries; - else - upsdebugx(4, "Number of batteries is set to %d", numbat); - vbat = get_vbat(); - ah = get_ah(); - - /* Set all alarms possible */ - alarm[0] = '\0'; - if (lastpktdata.s_battery_mode) - snprintf(alarm, sizeof(alarm), "%s", "|UPS IN BATTERY MODE|"); - if (lastpktdata.s_battery_low) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|UPS IN BATTERY MODE|"); - if (lastpktdata.s_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|NETWORK FAILURE|"); - - /* FIXME: Really same criteria in these 3? */ - if (lastpktdata.s_fast_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|FAST NETWORK FAILURE|"); - if (lastpktdata.s_fast_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|220v IN|"); - if (lastpktdata.s_fast_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|220v OUT|"); - - if (lastpktdata.s_bypass_on) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|BYPASS ON|"); - if (lastpktdata.s_charger_on) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|CHARGER ON|"); - dstate_setinfo("ups.alarm", "%s", alarm); - dstate_setinfo("ups.model", "%s", ups.upsdesc); - dstate_setinfo("ups.mfr", "%s", MANUFACTURER); - dstate_setinfo("ups.serial", "%s", lastpkthwinfo.serial); - dstate_setinfo("ups.firmware", "%u", lastpkthwinfo.softwareversion); - /* Setting hardware version here. - * Did not find another place to do this. - * Feel free to correct it. - * FIXME: move to upsdrv_initinfo() or so - */ - dstate_setinfo("ups.firmware.aux", "%u", lastpkthwinfo.hardwareversion); - dstate_setinfo("ups.temperature", "%0.2f", lastpktdata.tempmed_real); - dstate_setinfo("ups.load", "%u", lastpktdata.potrms); - dstate_setinfo("ups.efficiency", "%0.2f", calculate_efficiency(lastpktdata.vacoutrms, lastpktdata.vacinrms)); - va = get_va(lastpkthwinfo.model); - pf = get_pf(); - /* vpower is the power in Watts */ - vpower = ((va * pf) * (lastpktdata.potrms / 100.0)); - /* abat is the battery's consumption in Amperes */ - abat = ((vpower / lastpktdata.vdcmed_real) / numbat); - if (vpower > maxpower) - maxpower = vpower; - if (vpower < minpower) - minpower = vpower; - dstate_setinfo("ups.power", "%0.2f", vpower); - dstate_setinfo("ups.power.nominal", "%u", va); - dstate_setinfo("ups.realpower", "%ld", lrint(round(vpower))); - dstate_setinfo("ups.realpower.nominal", "%ld", lrint(round((double)va * (double)pf))); - dstate_setinfo("ups.beeper.status", "%d", !lastpkthwinfo.c_buzzer_disable); - dstate_setinfo("input.voltage", "%0.2f", lastpktdata.vacinrms); - dstate_setinfo("input.voltage.maximum", "%0.2f", lastpktdata.vacinrmsmin); - dstate_setinfo("input.voltage.minimum", "%0.2f", lastpktdata.vacinrmsmax); - vin_underv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V; - vin_overv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V; - perc = f_equal(get_vin_perc("vin_low_warn_perc"), get_vin_perc("vin_low_crit_perc")) ? 2 : 1; - vin_low_warn = vin_underv + (vin_underv * ((get_vin_perc("vin_low_warn_perc") * perc) / 100.0)); - dstate_setinfo("input.voltage.low.warning", "%0.2f", calculated); - vin_low_crit = vin_underv + (vin_underv * (get_vin_perc("vin_low_crit_perc") / 100.0)); - dstate_setinfo("input.voltage.low.critical", "%0.2f", calculated); - vin_high_warn = vin_overv + (vin_overv * ((get_vin_perc("vin_high_warn_perc") * perc) / 100.0)); - dstate_setinfo("input.voltage.high.warning", "%0.2f", calculated); - vin_high_crit = vin_overv + (vin_overv * (get_vin_perc("vin_high_crit_perc") / 100.0)); - dstate_setinfo("input.voltage.high.critical", "%0.2f", calculated); - vin = lastpkthwinfo.s_220V_in ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; - dstate_setinfo("input.voltage.nominal", "%u", vin); - dstate_setinfo("input.transfer.low", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V); - dstate_setinfo("input.transfer.high", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V); - dstate_setinfo("output.voltage", "%0.2f", lastpktdata.vacoutrms); - vout = lastpkthwinfo.s_220V_out ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; - dstate_setinfo("output.voltage.nominal", "%u", vout); - dstate_setinfo("voltage", "%0.2f", lastpktdata.vacoutrms); - dstate_setinfo("voltage.nominal", "%u", vout); - dstate_setinfo("voltage.maximum", "%0.2f", lastpktdata.vacinrmsmax); - dstate_setinfo("voltage.minimum", "%0.2f", lastpktdata.vacinrmsmin); - dstate_setinfo("voltage.low.warning", "%0.2f", vin_low_warn); - dstate_setinfo("voltage.low.critical", "%0.2f", vin_low_crit); - dstate_setinfo("voltage.high.warning", "%0.2f", vin_high_warn); - dstate_setinfo("voltage.high.critical", "%0.2f", vin_high_crit); - dstate_setinfo("power", "%0.2f", vpower); - dstate_setinfo("power.maximum", "%0.2f", maxpower); - dstate_setinfo("power.minimum", "%0.2f", minpower); - dstate_setinfo("power.percent", "%u", lastpktdata.potrms); - if (lastpktdata.potrms > maxpowerperc) - maxpowerperc = lastpktdata.potrms; - if (lastpktdata.potrms < minpowerperc) - minpowerperc = lastpktdata.potrms; - dstate_setinfo("power.maximum.percent", "%u", maxpowerperc); - dstate_setinfo("power.minimum.percent", "%u", minpowerperc); - dstate_setinfo("realpower", "%ld", lrint(round(vpower))); - dstate_setinfo("power", "%ld", lrint(round(va * (lastpktdata.potrms / 100.0)))); - bcharge = lrint(round((lastpktdata.vdcmed_real * 100) / vbat)); - if (bcharge > 100) - bcharge = 100; - dstate_setinfo("battery.charge", "%ld", bcharge); - dstate_setinfo("battery.voltage", "%0.2f", lastpktdata.vdcmed_real); - dstate_setinfo("battery.voltage.nominal", "%u", vbat); - dstate_setinfo("battery.capacity", "%u", ah); - dstate_setinfo("battery.capacity.nominal", "%0.2f", (float)ah * pf); - dstate_setinfo("battery.current", "%0.2f", abat); - dstate_setinfo("battery.current.total", "%0.2f", (float)abat * numbat); - dstate_setinfo("battery.temperature", "%ld", lrint(round(lastpktdata.tempmed_real))); - dstate_setinfo("battery.packs", "%u", numbat); - /* We will calculate autonomy in seconds - * autonomy_secs = (ah / lastpktdata.vdcmed_real) * 3600; - * Maybe wrong, too. - * People say that the correct calculation is - * - * Battery Amp-Hour / (Power in Watts / battery voltage) - * - * Is that correct? I don't know. I'll use it for now. - */ - - /* That result is IN HOURS. We need to convert it to seconds */ - actual_current = vpower / vbat; /* Current consumption in A*/ - autonomy_secs = (ah / actual_current) * 3600; - - dstate_setinfo("battery.runtime", "%u", autonomy_secs); - dstate_setinfo("battery.runtime.low", "%u", 30); - if (lastpktdata.s_charger_on) - dstate_setinfo("battery.charger.status", "%s", "CHARGING"); - else { - if (lastpktdata.s_battery_mode) - dstate_setinfo("battery.charger.status", "%s", "DISCHARGING"); - else - dstate_setinfo("battery.charger.status", "%s", "RESTING"); - } - /* Now, creating a structure called NHS, */ - dstate_setinfo("experimental.nhs.hw.header", "%u", lastpkthwinfo.header); - dstate_setinfo("experimental.nhs.hw.size", "%u", lastpkthwinfo.size); - dstate_setinfo("experimental.nhs.hw.type", "%c", lastpkthwinfo.type); - dstate_setinfo("experimental.nhs.hw.model", "%u", lastpkthwinfo.model); - dstate_setinfo("experimental.nhs.hw.hardwareversion", "%u", lastpkthwinfo.hardwareversion); - dstate_setinfo("experimental.nhs.hw.softwareversion", "%u", lastpkthwinfo.softwareversion); - dstate_setinfo("experimental.nhs.hw.configuration", "%u", lastpkthwinfo.configuration); - for (i = 0; i < 5; i++) { - /* Reusing variable */ - snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.configuration_array_p%d", i); - dstate_setinfo(alarm, "%u", lastpkthwinfo.configuration_array[i]); - } - dstate_setinfo("experimental.nhs.hw.c_oem_mode", "%s", lastpkthwinfo.c_oem_mode ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_buzzer_disable", "%s", lastpkthwinfo.c_buzzer_disable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_potmin_disable", "%s", lastpkthwinfo.c_potmin_disable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_rearm_enable", "%s", lastpkthwinfo.c_rearm_enable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_bootloader_enable", "%s", lastpkthwinfo.c_bootloader_enable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.numbatteries", "%u", lastpkthwinfo.numbatteries); - dstate_setinfo("experimental.nhs.hw.undervoltagein120V", "%u", lastpkthwinfo.undervoltagein120V); - dstate_setinfo("experimental.nhs.hw.overvoltagein120V", "%u", lastpkthwinfo.overvoltagein120V); - dstate_setinfo("experimental.nhs.hw.undervoltagein220V", "%u", lastpkthwinfo.undervoltagein220V); - dstate_setinfo("experimental.nhs.hw.overvoltagein220V", "%u", lastpkthwinfo.overvoltagein220V); - dstate_setinfo("experimental.nhs.hw.tensionout120V", "%u", lastpkthwinfo.tensionout120V); - dstate_setinfo("experimental.nhs.hw.tensionout220V", "%u", lastpkthwinfo.tensionout220V); - dstate_setinfo("experimental.nhs.hw.statusval", "%u", lastpkthwinfo.statusval); - for (i = 0; i < 6; i++) { - /* Reusing variable */ - snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.status_p%d", i); - dstate_setinfo(alarm, "%u", lastpkthwinfo.status[i]); - } - dstate_setinfo("experimental.nhs.hw.s_220V_in", "%s", lastpkthwinfo.s_220V_in ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_220V_out", "%s", lastpkthwinfo.s_220V_out ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_sealed_battery", "%s", lastpkthwinfo.s_sealed_battery ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_show_out_tension", "%s", lastpkthwinfo.s_show_out_tension ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_show_temperature", "%s", lastpkthwinfo.s_show_temperature ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_show_charger_current", "%s", lastpkthwinfo.s_show_charger_current ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.chargercurrent", "%u", lastpkthwinfo.chargercurrent); - dstate_setinfo("experimental.nhs.hw.checksum", "%u", lastpkthwinfo.checksum); - dstate_setinfo("experimental.nhs.hw.checksum_calc", "%u", lastpkthwinfo.checksum_calc); - dstate_setinfo("experimental.nhs.hw.checksum_ok", "%s", lastpkthwinfo.checksum_ok ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.serial", "%s", lastpkthwinfo.serial); - dstate_setinfo("experimental.nhs.hw.year", "%u", lastpkthwinfo.year); - dstate_setinfo("experimental.nhs.hw.month", "%u", lastpkthwinfo.month); - dstate_setinfo("experimental.nhs.hw.wday", "%u", lastpkthwinfo.wday); - dstate_setinfo("experimental.nhs.hw.hour", "%u", lastpkthwinfo.hour); - dstate_setinfo("experimental.nhs.hw.minute", "%u", lastpkthwinfo.minute); - dstate_setinfo("experimental.nhs.hw.second", "%u", lastpkthwinfo.second); - dstate_setinfo("experimental.nhs.hw.alarmyear", "%u", lastpkthwinfo.alarmyear); - dstate_setinfo("experimental.nhs.hw.alarmmonth", "%u", lastpkthwinfo.alarmmonth); - dstate_setinfo("experimental.nhs.hw.alarmwday", "%u", lastpkthwinfo.alarmwday); - dstate_setinfo("experimental.nhs.hw.alarmday", "%u", lastpkthwinfo.alarmday); - dstate_setinfo("experimental.nhs.hw.alarmhour", "%u", lastpkthwinfo.alarmhour); - dstate_setinfo("experimental.nhs.hw.alarmminute", "%u", lastpkthwinfo.alarmminute); - dstate_setinfo("experimental.nhs.hw.alarmsecond", "%u", lastpkthwinfo.alarmsecond); - dstate_setinfo("experimental.nhs.hw.end_marker", "%u", lastpkthwinfo.end_marker); - - /* Data packet */ - dstate_setinfo("experimental.nhs.data.header", "%u", lastpktdata.header); - dstate_setinfo("experimental.nhs.data.length", "%u", lastpktdata.length); - dstate_setinfo("experimental.nhs.data.packet_type", "%c", lastpktdata.packet_type); - dstate_setinfo("experimental.nhs.data.vacinrms_high", "%u", lastpktdata.vacinrms_high); - dstate_setinfo("experimental.nhs.data.vacinrms_low", "%u", lastpktdata.vacinrms_low); - dstate_setinfo("experimental.nhs.data.vacinrms", "%0.2f", lastpktdata.vacinrms); - dstate_setinfo("experimental.nhs.data.vdcmed_high", "%u", lastpktdata.vdcmed_high); - dstate_setinfo("experimental.nhs.data.vdcmed_low", "%u", lastpktdata.vdcmed_low); - dstate_setinfo("experimental.nhs.data.vdcmed", "%0.2f", lastpktdata.vdcmed); - dstate_setinfo("experimental.nhs.data.vdcmed_real", "%0.2f", lastpktdata.vdcmed_real); - dstate_setinfo("experimental.nhs.data.potrms", "%u", lastpktdata.potrms); - dstate_setinfo("experimental.nhs.data.vacinrmsmin_high", "%u", lastpktdata.vacinrmsmin_high); - dstate_setinfo("experimental.nhs.data.vacinrmsmin_low", "%u", lastpktdata.vacinrmsmin_low); - dstate_setinfo("experimental.nhs.data.vacinrmsmin", "%0.2f", lastpktdata.vacinrmsmin); - dstate_setinfo("experimental.nhs.data.vacinrmsmax_high", "%u", lastpktdata.vacinrmsmax_high); - dstate_setinfo("experimental.nhs.data.vacinrmsmax_low", "%u", lastpktdata.vacinrmsmax_low); - dstate_setinfo("experimental.nhs.data.vacinrmsmax", "%0.2f", lastpktdata.vacinrmsmax); - dstate_setinfo("experimental.nhs.data.vacoutrms_high", "%u", lastpktdata.vacoutrms_high); - dstate_setinfo("experimental.nhs.data.vacoutrms_low", "%u", lastpktdata.vacoutrms_low); - dstate_setinfo("experimental.nhs.data.vacoutrms", "%0.2f", lastpktdata.vacoutrms); - dstate_setinfo("experimental.nhs.data.tempmed_high", "%u", lastpktdata.tempmed_high); - dstate_setinfo("experimental.nhs.data.tempmed_low", "%u", lastpktdata.tempmed_low); - dstate_setinfo("experimental.nhs.data.tempmed", "%0.2f", lastpktdata.tempmed); - dstate_setinfo("experimental.nhs.data.tempmed_real", "%0.2f", lastpktdata.tempmed_real); - dstate_setinfo("experimental.nhs.data.icarregrms", "%u", lastpktdata.icarregrms); - dstate_setinfo("experimental.nhs.data.icarregrms_real", "%u", lastpktdata.icarregrms_real); - dstate_setinfo("experimental.nhs.data.battery_tension", "%0.2f", lastpktdata.battery_tension); - dstate_setinfo("experimental.nhs.data.perc_output", "%u", lastpktdata.perc_output); - dstate_setinfo("experimental.nhs.data.statusval", "%u", lastpktdata.statusval); - for (i = 0; i < 8; i++) { - /* Reusing variable */ - snprintf(alarm, sizeof(alarm), "experimental.nhs.data.status_p%d", i); - dstate_setinfo(alarm, "%u", lastpktdata.status[i]); - } - dstate_setinfo("experimental.nhs.data.nominaltension", "%u", lastpktdata.nominaltension); - dstate_setinfo("experimental.nhs.data.timeremain", "%0.2f", lastpktdata.timeremain); - dstate_setinfo("experimental.nhs.data.s_battery_mode", "%s", lastpktdata.s_battery_mode ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_battery_low", "%s", lastpktdata.s_battery_low ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_network_failure", "%s", lastpktdata.s_network_failure ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_fast_network_failure", "%s", lastpktdata.s_fast_network_failure ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_220_in", "%s", lastpktdata.s_220_in ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_220_out", "%s", lastpktdata.s_220_out ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_bypass_on", "%s", lastpktdata.s_bypass_on ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_charger_on", "%s", lastpktdata.s_charger_on ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.checksum", "%u", lastpktdata.checksum); - dstate_setinfo("experimental.nhs.data.checksum_ok", "%s", lastpktdata.checksum_ok ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.checksum_calc", "%u", lastpktdata.checksum_calc); - dstate_setinfo("experimental.nhs.data.end_marker", "%u", lastpktdata.end_marker); - dstate_setinfo("experimental.nhs.param.va", "%u", va); - dstate_setinfo("experimental.nhs.param.pf", "%0.2f", pf); - dstate_setinfo("experimental.nhs.param.ah", "%u", ah); - dstate_setinfo("experimental.nhs.param.vin_low_warn_perc", "%0.2f", get_vin_perc("vin_low_warn_perc")); - dstate_setinfo("experimental.nhs.param.vin_low_crit_perc", "%0.2f", get_vin_perc("vin_low_crit_perc")); - dstate_setinfo("experimental.nhs.param.vin_high_warn_perc", "%0.2f", get_vin_perc("vin_high_warn_perc")); - dstate_setinfo("experimental.nhs.param.vin_high_crit_perc", "%0.2f", get_vin_perc("vin_high_crit_perc")); - - dstate_dataok(); - } /* end if */ + numbat = get_numbat(); + if (numbat == 0) + numbat = lastpkthwinfo.numbatteries; else - upsdebugx(4, "Checksum of pkt_hwinfo is corrupted or not initialized. Waiting for new request..."); - } /* end else */ - } /* end if */ + upsdebugx(4, "Number of batteries is set to %d", numbat); + vbat = get_vbat(); + ah = get_ah(); + + /* Set all alarms possible */ + alarm[0] = '\0'; + if (lastpktdata.s_battery_mode) + snprintf(alarm, sizeof(alarm), "%s", "|UPS IN BATTERY MODE|"); + if (lastpktdata.s_battery_low) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|UPS IN BATTERY MODE|"); + if (lastpktdata.s_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|NETWORK FAILURE|"); + + /* FIXME: Really same criteria in these 3? */ + if (lastpktdata.s_fast_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|FAST NETWORK FAILURE|"); + if (lastpktdata.s_fast_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|220v IN|"); + if (lastpktdata.s_fast_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|220v OUT|"); + + if (lastpktdata.s_bypass_on) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|BYPASS ON|"); + if (lastpktdata.s_charger_on) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|CHARGER ON|"); + dstate_setinfo("ups.alarm", "%s", alarm); + dstate_setinfo("ups.model", "%s", ups.upsdesc); + dstate_setinfo("ups.mfr", "%s", MANUFACTURER); + dstate_setinfo("ups.serial", "%s", lastpkthwinfo.serial); + dstate_setinfo("ups.firmware", "%u", lastpkthwinfo.softwareversion); + /* Setting hardware version here. + * Did not find another place to do this. + * Feel free to correct it. + * FIXME: move to upsdrv_initinfo() or so + */ + dstate_setinfo("ups.firmware.aux", "%u", lastpkthwinfo.hardwareversion); + dstate_setinfo("ups.temperature", "%0.2f", lastpktdata.tempmed_real); + dstate_setinfo("ups.load", "%u", lastpktdata.potrms); + dstate_setinfo("ups.efficiency", "%0.2f", calculate_efficiency(lastpktdata.vacoutrms, lastpktdata.vacinrms)); + va = get_va(lastpkthwinfo.model); + pf = get_pf(); + /* vpower is the power in Watts */ + vpower = ((va * pf) * (lastpktdata.potrms / 100.0)); + /* abat is the battery's consumption in Amperes */ + abat = ((vpower / lastpktdata.vdcmed_real) / numbat); + if (vpower > maxpower) + maxpower = vpower; + if (vpower < minpower) + minpower = vpower; + dstate_setinfo("ups.power", "%0.2f", vpower); + dstate_setinfo("ups.power.nominal", "%u", va); + dstate_setinfo("ups.realpower", "%ld", lrint(round(vpower))); + dstate_setinfo("ups.realpower.nominal", "%ld", lrint(round((double)va * (double)pf))); + dstate_setinfo("ups.beeper.status", "%d", !lastpkthwinfo.c_buzzer_disable); + dstate_setinfo("input.voltage", "%0.2f", lastpktdata.vacinrms); + dstate_setinfo("input.voltage.maximum", "%0.2f", lastpktdata.vacinrmsmin); + dstate_setinfo("input.voltage.minimum", "%0.2f", lastpktdata.vacinrmsmax); + vin_underv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V; + vin_overv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V; + perc = f_equal(get_vin_perc("vin_low_warn_perc"), get_vin_perc("vin_low_crit_perc")) ? 2 : 1; + vin_low_warn = vin_underv + (vin_underv * ((get_vin_perc("vin_low_warn_perc") * perc) / 100.0)); + dstate_setinfo("input.voltage.low.warning", "%0.2f", calculated); + vin_low_crit = vin_underv + (vin_underv * (get_vin_perc("vin_low_crit_perc") / 100.0)); + dstate_setinfo("input.voltage.low.critical", "%0.2f", calculated); + vin_high_warn = vin_overv + (vin_overv * ((get_vin_perc("vin_high_warn_perc") * perc) / 100.0)); + dstate_setinfo("input.voltage.high.warning", "%0.2f", calculated); + vin_high_crit = vin_overv + (vin_overv * (get_vin_perc("vin_high_crit_perc") / 100.0)); + dstate_setinfo("input.voltage.high.critical", "%0.2f", calculated); + vin = lastpkthwinfo.s_220V_in ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; + dstate_setinfo("input.voltage.nominal", "%u", vin); + dstate_setinfo("input.transfer.low", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V); + dstate_setinfo("input.transfer.high", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V); + dstate_setinfo("output.voltage", "%0.2f", lastpktdata.vacoutrms); + vout = lastpkthwinfo.s_220V_out ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; + dstate_setinfo("output.voltage.nominal", "%u", vout); + dstate_setinfo("voltage", "%0.2f", lastpktdata.vacoutrms); + dstate_setinfo("voltage.nominal", "%u", vout); + dstate_setinfo("voltage.maximum", "%0.2f", lastpktdata.vacinrmsmax); + dstate_setinfo("voltage.minimum", "%0.2f", lastpktdata.vacinrmsmin); + dstate_setinfo("voltage.low.warning", "%0.2f", vin_low_warn); + dstate_setinfo("voltage.low.critical", "%0.2f", vin_low_crit); + dstate_setinfo("voltage.high.warning", "%0.2f", vin_high_warn); + dstate_setinfo("voltage.high.critical", "%0.2f", vin_high_crit); + dstate_setinfo("power", "%0.2f", vpower); + dstate_setinfo("power.maximum", "%0.2f", maxpower); + dstate_setinfo("power.minimum", "%0.2f", minpower); + dstate_setinfo("power.percent", "%u", lastpktdata.potrms); + if (lastpktdata.potrms > maxpowerperc) + maxpowerperc = lastpktdata.potrms; + if (lastpktdata.potrms < minpowerperc) + minpowerperc = lastpktdata.potrms; + dstate_setinfo("power.maximum.percent", "%u", maxpowerperc); + dstate_setinfo("power.minimum.percent", "%u", minpowerperc); + dstate_setinfo("realpower", "%ld", lrint(round(vpower))); + dstate_setinfo("power", "%ld", lrint(round(va * (lastpktdata.potrms / 100.0)))); + bcharge = lrint(round((lastpktdata.vdcmed_real * 100) / vbat)); + if (bcharge > 100) + bcharge = 100; + dstate_setinfo("battery.charge", "%ld", bcharge); + dstate_setinfo("battery.voltage", "%0.2f", lastpktdata.vdcmed_real); + dstate_setinfo("battery.voltage.nominal", "%u", vbat); + dstate_setinfo("battery.capacity", "%u", ah); + dstate_setinfo("battery.capacity.nominal", "%0.2f", (float)ah * pf); + dstate_setinfo("battery.current", "%0.2f", abat); + dstate_setinfo("battery.current.total", "%0.2f", (float)abat * numbat); + dstate_setinfo("battery.temperature", "%ld", lrint(round(lastpktdata.tempmed_real))); + dstate_setinfo("battery.packs", "%u", numbat); + /* We will calculate autonomy in seconds + * autonomy_secs = (ah / lastpktdata.vdcmed_real) * 3600; + * Maybe wrong, too. + * People say that the correct calculation is + * + * Battery Amp-Hour / (Power in Watts / battery voltage) + * + * Is that correct? I don't know. I'll use it for now. + */ + + /* That result is IN HOURS. We need to convert it to seconds */ + actual_current = vpower / vbat; /* Current consumption in A*/ + autonomy_secs = (ah / actual_current) * 3600; + + dstate_setinfo("battery.runtime", "%u", autonomy_secs); + dstate_setinfo("battery.runtime.low", "%u", 30); + if (lastpktdata.s_charger_on) { + dstate_setinfo("battery.charger.status", "%s", "CHARGING"); + } else { + if (lastpktdata.s_battery_mode) + dstate_setinfo("battery.charger.status", "%s", "DISCHARGING"); + else + dstate_setinfo("battery.charger.status", "%s", "RESTING"); + } + /* Now, creating a structure called NHS, */ + dstate_setinfo("experimental.nhs.hw.header", "%u", lastpkthwinfo.header); + dstate_setinfo("experimental.nhs.hw.size", "%u", lastpkthwinfo.size); + dstate_setinfo("experimental.nhs.hw.type", "%c", lastpkthwinfo.type); + dstate_setinfo("experimental.nhs.hw.model", "%u", lastpkthwinfo.model); + dstate_setinfo("experimental.nhs.hw.hardwareversion", "%u", lastpkthwinfo.hardwareversion); + dstate_setinfo("experimental.nhs.hw.softwareversion", "%u", lastpkthwinfo.softwareversion); + dstate_setinfo("experimental.nhs.hw.configuration", "%u", lastpkthwinfo.configuration); + for (i = 0; i < 5; i++) { + /* Reusing variable */ + snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.configuration_array_p%d", i); + dstate_setinfo(alarm, "%u", lastpkthwinfo.configuration_array[i]); + } + dstate_setinfo("experimental.nhs.hw.c_oem_mode", "%s", lastpkthwinfo.c_oem_mode ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_buzzer_disable", "%s", lastpkthwinfo.c_buzzer_disable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_potmin_disable", "%s", lastpkthwinfo.c_potmin_disable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_rearm_enable", "%s", lastpkthwinfo.c_rearm_enable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_bootloader_enable", "%s", lastpkthwinfo.c_bootloader_enable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.numbatteries", "%u", lastpkthwinfo.numbatteries); + dstate_setinfo("experimental.nhs.hw.undervoltagein120V", "%u", lastpkthwinfo.undervoltagein120V); + dstate_setinfo("experimental.nhs.hw.overvoltagein120V", "%u", lastpkthwinfo.overvoltagein120V); + dstate_setinfo("experimental.nhs.hw.undervoltagein220V", "%u", lastpkthwinfo.undervoltagein220V); + dstate_setinfo("experimental.nhs.hw.overvoltagein220V", "%u", lastpkthwinfo.overvoltagein220V); + dstate_setinfo("experimental.nhs.hw.tensionout120V", "%u", lastpkthwinfo.tensionout120V); + dstate_setinfo("experimental.nhs.hw.tensionout220V", "%u", lastpkthwinfo.tensionout220V); + dstate_setinfo("experimental.nhs.hw.statusval", "%u", lastpkthwinfo.statusval); + for (i = 0; i < 6; i++) { + /* Reusing variable */ + snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.status_p%d", i); + dstate_setinfo(alarm, "%u", lastpkthwinfo.status[i]); + } + dstate_setinfo("experimental.nhs.hw.s_220V_in", "%s", lastpkthwinfo.s_220V_in ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_220V_out", "%s", lastpkthwinfo.s_220V_out ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_sealed_battery", "%s", lastpkthwinfo.s_sealed_battery ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_show_out_tension", "%s", lastpkthwinfo.s_show_out_tension ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_show_temperature", "%s", lastpkthwinfo.s_show_temperature ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_show_charger_current", "%s", lastpkthwinfo.s_show_charger_current ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.chargercurrent", "%u", lastpkthwinfo.chargercurrent); + dstate_setinfo("experimental.nhs.hw.checksum", "%u", lastpkthwinfo.checksum); + dstate_setinfo("experimental.nhs.hw.checksum_calc", "%u", lastpkthwinfo.checksum_calc); + dstate_setinfo("experimental.nhs.hw.checksum_ok", "%s", lastpkthwinfo.checksum_ok ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.serial", "%s", lastpkthwinfo.serial); + dstate_setinfo("experimental.nhs.hw.year", "%u", lastpkthwinfo.year); + dstate_setinfo("experimental.nhs.hw.month", "%u", lastpkthwinfo.month); + dstate_setinfo("experimental.nhs.hw.wday", "%u", lastpkthwinfo.wday); + dstate_setinfo("experimental.nhs.hw.hour", "%u", lastpkthwinfo.hour); + dstate_setinfo("experimental.nhs.hw.minute", "%u", lastpkthwinfo.minute); + dstate_setinfo("experimental.nhs.hw.second", "%u", lastpkthwinfo.second); + dstate_setinfo("experimental.nhs.hw.alarmyear", "%u", lastpkthwinfo.alarmyear); + dstate_setinfo("experimental.nhs.hw.alarmmonth", "%u", lastpkthwinfo.alarmmonth); + dstate_setinfo("experimental.nhs.hw.alarmwday", "%u", lastpkthwinfo.alarmwday); + dstate_setinfo("experimental.nhs.hw.alarmday", "%u", lastpkthwinfo.alarmday); + dstate_setinfo("experimental.nhs.hw.alarmhour", "%u", lastpkthwinfo.alarmhour); + dstate_setinfo("experimental.nhs.hw.alarmminute", "%u", lastpkthwinfo.alarmminute); + dstate_setinfo("experimental.nhs.hw.alarmsecond", "%u", lastpkthwinfo.alarmsecond); + dstate_setinfo("experimental.nhs.hw.end_marker", "%u", lastpkthwinfo.end_marker); + + /* Data packet */ + dstate_setinfo("experimental.nhs.data.header", "%u", lastpktdata.header); + dstate_setinfo("experimental.nhs.data.length", "%u", lastpktdata.length); + dstate_setinfo("experimental.nhs.data.packet_type", "%c", lastpktdata.packet_type); + dstate_setinfo("experimental.nhs.data.vacinrms_high", "%u", lastpktdata.vacinrms_high); + dstate_setinfo("experimental.nhs.data.vacinrms_low", "%u", lastpktdata.vacinrms_low); + dstate_setinfo("experimental.nhs.data.vacinrms", "%0.2f", lastpktdata.vacinrms); + dstate_setinfo("experimental.nhs.data.vdcmed_high", "%u", lastpktdata.vdcmed_high); + dstate_setinfo("experimental.nhs.data.vdcmed_low", "%u", lastpktdata.vdcmed_low); + dstate_setinfo("experimental.nhs.data.vdcmed", "%0.2f", lastpktdata.vdcmed); + dstate_setinfo("experimental.nhs.data.vdcmed_real", "%0.2f", lastpktdata.vdcmed_real); + dstate_setinfo("experimental.nhs.data.potrms", "%u", lastpktdata.potrms); + dstate_setinfo("experimental.nhs.data.vacinrmsmin_high", "%u", lastpktdata.vacinrmsmin_high); + dstate_setinfo("experimental.nhs.data.vacinrmsmin_low", "%u", lastpktdata.vacinrmsmin_low); + dstate_setinfo("experimental.nhs.data.vacinrmsmin", "%0.2f", lastpktdata.vacinrmsmin); + dstate_setinfo("experimental.nhs.data.vacinrmsmax_high", "%u", lastpktdata.vacinrmsmax_high); + dstate_setinfo("experimental.nhs.data.vacinrmsmax_low", "%u", lastpktdata.vacinrmsmax_low); + dstate_setinfo("experimental.nhs.data.vacinrmsmax", "%0.2f", lastpktdata.vacinrmsmax); + dstate_setinfo("experimental.nhs.data.vacoutrms_high", "%u", lastpktdata.vacoutrms_high); + dstate_setinfo("experimental.nhs.data.vacoutrms_low", "%u", lastpktdata.vacoutrms_low); + dstate_setinfo("experimental.nhs.data.vacoutrms", "%0.2f", lastpktdata.vacoutrms); + dstate_setinfo("experimental.nhs.data.tempmed_high", "%u", lastpktdata.tempmed_high); + dstate_setinfo("experimental.nhs.data.tempmed_low", "%u", lastpktdata.tempmed_low); + dstate_setinfo("experimental.nhs.data.tempmed", "%0.2f", lastpktdata.tempmed); + dstate_setinfo("experimental.nhs.data.tempmed_real", "%0.2f", lastpktdata.tempmed_real); + dstate_setinfo("experimental.nhs.data.icarregrms", "%u", lastpktdata.icarregrms); + dstate_setinfo("experimental.nhs.data.icarregrms_real", "%u", lastpktdata.icarregrms_real); + dstate_setinfo("experimental.nhs.data.battery_tension", "%0.2f", lastpktdata.battery_tension); + dstate_setinfo("experimental.nhs.data.perc_output", "%u", lastpktdata.perc_output); + dstate_setinfo("experimental.nhs.data.statusval", "%u", lastpktdata.statusval); + for (i = 0; i < 8; i++) { + /* Reusing variable */ + snprintf(alarm, sizeof(alarm), "experimental.nhs.data.status_p%d", i); + dstate_setinfo(alarm, "%u", lastpktdata.status[i]); + } + dstate_setinfo("experimental.nhs.data.nominaltension", "%u", lastpktdata.nominaltension); + dstate_setinfo("experimental.nhs.data.timeremain", "%0.2f", lastpktdata.timeremain); + dstate_setinfo("experimental.nhs.data.s_battery_mode", "%s", lastpktdata.s_battery_mode ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_battery_low", "%s", lastpktdata.s_battery_low ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_network_failure", "%s", lastpktdata.s_network_failure ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_fast_network_failure", "%s", lastpktdata.s_fast_network_failure ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_220_in", "%s", lastpktdata.s_220_in ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_220_out", "%s", lastpktdata.s_220_out ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_bypass_on", "%s", lastpktdata.s_bypass_on ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_charger_on", "%s", lastpktdata.s_charger_on ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.checksum", "%u", lastpktdata.checksum); + dstate_setinfo("experimental.nhs.data.checksum_ok", "%s", lastpktdata.checksum_ok ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.checksum_calc", "%u", lastpktdata.checksum_calc); + dstate_setinfo("experimental.nhs.data.end_marker", "%u", lastpktdata.end_marker); + dstate_setinfo("experimental.nhs.param.va", "%u", va); + dstate_setinfo("experimental.nhs.param.pf", "%0.2f", pf); + dstate_setinfo("experimental.nhs.param.ah", "%u", ah); + dstate_setinfo("experimental.nhs.param.vin_low_warn_perc", "%0.2f", get_vin_perc("vin_low_warn_perc")); + dstate_setinfo("experimental.nhs.param.vin_low_crit_perc", "%0.2f", get_vin_perc("vin_low_crit_perc")); + dstate_setinfo("experimental.nhs.param.vin_high_warn_perc", "%0.2f", get_vin_perc("vin_high_warn_perc")); + dstate_setinfo("experimental.nhs.param.vin_high_crit_perc", "%0.2f", get_vin_perc("vin_high_crit_perc")); + + dstate_dataok(); + } /* end if */ + else { + upsdebugx(4, "Checksum of pkt_hwinfo is corrupted or not initialized. Waiting for new request..."); + } + } /* end else */ } /* end if */ } /* end if */ } /* end if */ + } /* end while read */ - /* Now the nobreak read buffer is empty. - * We need a hw info packet to discover several variables, - * like number of batteries, to calculate some data - * FIXME: move (semi)static info discovery to upsdrv_initinfo() or so + /* Now the nobreak read buffer is empty. + * We need a hw info packet to discover several variables, + * like number of batteries, to calculate some data + * FIXME: move (semi)static info discovery to upsdrv_initinfo() or so + */ + if (!lastpkthwinfo.checksum_ok) { + upsdebugx(4, "pkt_hwinfo loss -- Requesting"); + /* If size == 0, packet maybe not initizated, + * then send an initialization packet to obtain data. + * Send two times the extended initialization string, + * but, on fail, try randomly send extended or normal. */ - if (!lastpkthwinfo.checksum_ok) { - upsdebugx(4, "pkt_hwinfo loss -- Requesting"); - /* If size == 0, packet maybe not initizated, - * then send an initialization packet to obtain data. - * Send two times the extended initialization string, - * but, on fail, try randomly send extended or normal. - */ - if (send_extended < 6) { - upsdebugx(4, "Sending extended initialization packet. Try %d", send_extended+1); + if (send_extended < 6) { + upsdebugx(4, "Sending extended initialization packet. Try %d", send_extended+1); + bwritten = write_serial_int(serial_fd, string_initialization_long, 9); + send_extended++; + } /* end if */ + else { + /* randomly send */ + if (rand() % 2 == 0) { + upsdebugx(4, "Sending long initialization packet"); bwritten = write_serial_int(serial_fd, string_initialization_long, 9); - send_extended++; - } /* end if */ - else { - /* randomly send */ - if (rand() % 2 == 0) { - upsdebugx(4, "Sending long initialization packet"); - bwritten = write_serial_int(serial_fd, string_initialization_long, 9); - } /* end if */ - else { - upsdebugx(4, "Sending short initialization packet"); - bwritten = write_serial_int(serial_fd, string_initialization_short, 9); - } /* end else */ - } /* end else */ - if (bwritten < 0) { - upsdebugx(1, "%s: Problem to write data to %s", __func__, porta); - if (bwritten == -1) { - upsdebugx(1, "%s: Data problem", __func__); - } - if (bwritten == -2) { - upsdebugx(1, "%s: Flush problem", __func__); - } - close(serial_fd); - serial_fd = -1; } /* end if */ else { - if (checktime > max_checktime) - checktime = max_checktime; - else { - upsdebugx(3, "Increase checktime to %d", checktime + 100000); - checktime = checktime + 100000; - } - usleep(checktime); + upsdebugx(4, "Sending short initialization packet"); + bwritten = write_serial_int(serial_fd, string_initialization_short, 9); } /* end else */ + } /* end else */ + if (bwritten < 0) { + upsdebugx(1, "%s: Problem to write data to %s", __func__, porta); + if (bwritten == -1) { + upsdebugx(1, "%s: Data problem", __func__); + } + if (bwritten == -2) { + upsdebugx(1, "%s: Flush problem", __func__); + } + close(serial_fd); + serial_fd = -1; } /* end if */ - } /* end else */ + else { + if (checktime > max_checktime) + checktime = max_checktime; + else { + upsdebugx(3, "Increase checktime to %d", checktime + 100000); + checktime = checktime + 100000; + } + usleep(checktime); + } /* end else */ + } /* end if bad checksum */ + upsdebugx(3, "%s: finished", __func__); } From badad3e526fa0bccb4a2d8fd258498e346f78342 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 8 Dec 2024 19:16:14 +0100 Subject: [PATCH 133/144] drivers/nhs_ser.c: upsdrv_updateinfo(): revise reconnections with data-stale related messages and status changes [#2692] Signed-off-by: Jim Klimov --- drivers/nhs_ser.c | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/nhs_ser.c b/drivers/nhs_ser.c index 32e3b9b56a..53c5031ed2 100644 --- a/drivers/nhs_ser.c +++ b/drivers/nhs_ser.c @@ -53,6 +53,9 @@ #define DATAPACKETSIZE 100 #define DEFAULTBATV 12.0 +/* comms revival attempts before declaring them stale */ +#define MAXTRIES 3 + /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -1691,7 +1694,7 @@ static unsigned int get_numbat(void) { void upsdrv_updateinfo(void) { /* retries to open port */ - unsigned int retries = 3; + static unsigned int retries = 0; unsigned int i = 0; char alarm[1024]; unsigned int va = 0; @@ -1724,15 +1727,33 @@ void upsdrv_updateinfo(void) { if (serial_fd <= 0) { upsdebugx(1, "%s: Serial port '%s' communications problem", __func__, porta); - while (serial_fd <= 0 && i < retries) { - serial_fd = openfd(porta, baudrate); + + /* Uh oh, got to reconnect! */ + dstate_setinfo("driver.state", "reconnect.trying"); + + while (serial_fd <= 0) { upsdebugx(1, "%s: Trying to reopen serial...", __func__); + serial_fd = openfd(porta, baudrate); + retries++; + /* Try above at least once per main cycle */ + if (retries >= MAXTRIES) + break; usleep(checktime); - i++; } - } - if (serial_fd <= 0) { - return; + + if (serial_fd > 0) { + if (retries > MAXTRIES) { + upslogx(LOG_NOTICE, "Communications with UPS re-established"); + } + retries = 0; + dstate_setinfo("driver.state", "quiet"); + } else { + if (retries == MAXTRIES) { + upslogx(LOG_WARNING, "Communications with UPS lost: port reopen failed!"); + } + dstate_datastale(); + return; + } } /* Clean all read buffers to avoid errors: @@ -2149,7 +2170,10 @@ void upsdrv_updateinfo(void) { * like number of batteries, to calculate some data * FIXME: move (semi)static info discovery to upsdrv_initinfo() or so */ - if (!lastpkthwinfo.checksum_ok) { + if (lastpkthwinfo.checksum_ok) { + /* Refresh the healthy timer */ + dstate_dataok(); + } else { upsdebugx(4, "pkt_hwinfo loss -- Requesting"); /* If size == 0, packet maybe not initizated, * then send an initialization packet to obtain data. From fd5c35311c1d451869037253779aec0aa847601c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 8 Dec 2024 19:24:28 +0100 Subject: [PATCH 134/144] drivers/nhs_ser.c: upsdrv_updateinfo(): refactor with reconnect_ups_if_needed() [#2692] Signed-off-by: Jim Klimov --- drivers/nhs_ser.c | 67 +++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/drivers/nhs_ser.c b/drivers/nhs_ser.c index 53c5031ed2..6e220bdebc 100644 --- a/drivers/nhs_ser.c +++ b/drivers/nhs_ser.c @@ -1692,36 +1692,10 @@ static unsigned int get_numbat(void) { return retval; } -void upsdrv_updateinfo(void) { +/* Return serial_fd after the reconnection attempt, for easier calls */ +static int reconnect_ups_if_needed(void) { /* retries to open port */ static unsigned int retries = 0; - unsigned int i = 0; - char alarm[1024]; - unsigned int va = 0; - unsigned int ah = 0; - unsigned int vin_underv = 0; - unsigned int vin_overv = 0; - unsigned int perc = 0; - unsigned int vin = 0; - unsigned int vout = 0; - unsigned int autonomy_secs = 0; - float vin_low_warn = 0; - float vin_low_crit = 0; - float vin_high_warn = 0; - float vin_high_crit = 0; - float calculated = 0; - float vpower = 0; - float pf = 0; - long bcharge = 0; - int min_input_power = 0; - double tempodecorrido = 0.0; - unsigned int numbat = 0; - unsigned int vbat = 0; - float abat = 0; - float actual_current = 0; - upsinfo ups; - - upsdebugx(3, "%s: starting...", __func__); /* If comms failed earlier, try to resuscitate */ if (serial_fd <= 0) { @@ -1752,10 +1726,45 @@ void upsdrv_updateinfo(void) { upslogx(LOG_WARNING, "Communications with UPS lost: port reopen failed!"); } dstate_datastale(); - return; } } + return serial_fd; +} + +void upsdrv_updateinfo(void) { + unsigned int i = 0; + char alarm[1024]; + unsigned int va = 0; + unsigned int ah = 0; + unsigned int vin_underv = 0; + unsigned int vin_overv = 0; + unsigned int perc = 0; + unsigned int vin = 0; + unsigned int vout = 0; + unsigned int autonomy_secs = 0; + float vin_low_warn = 0; + float vin_low_crit = 0; + float vin_high_warn = 0; + float vin_high_crit = 0; + float calculated = 0; + float vpower = 0; + float pf = 0; + long bcharge = 0; + int min_input_power = 0; + double tempodecorrido = 0.0; + unsigned int numbat = 0; + unsigned int vbat = 0; + float abat = 0; + float actual_current = 0; + upsinfo ups; + + upsdebugx(3, "%s: starting...", __func__); + + /* If comms failed earlier, try to resuscitate */ + if (reconnect_ups_if_needed() <= 0) + return; + /* Clean all read buffers to avoid errors: * To clean OUTPUT buffer is TCOFLUSH. * To both is TCIOFLUSH. From 5a7ee6a2af620b12d889e7105fa91ab710d610c2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 8 Dec 2024 19:29:02 +0100 Subject: [PATCH 135/144] drivers/nhs_ser.c: use strncpy() to init porta[] [#2692] Signed-off-by: Jim Klimov --- drivers/nhs_ser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nhs_ser.c b/drivers/nhs_ser.c index 6e220bdebc..1a13e0caa7 100644 --- a/drivers/nhs_ser.c +++ b/drivers/nhs_ser.c @@ -2284,9 +2284,9 @@ void upsdrv_initups(void) { baudrate = atoi(b); if (device_path) { if (strcasecmp(device_path, "auto") == 0) - strcpy(porta, DEFAULTPORT); + strncpy(porta, DEFAULTPORT, sizeof(porta) - 1); else - strcpy(porta, device_path); + strncpy(porta, device_path, sizeof(porta) - 1); serial_fd = openfd(porta, baudrate); if (serial_fd == -1) fatalx(EXIT_FAILURE, "Unable to open port %s with baud %d", From ea015ec9f2ba5739fe039f63f9c4802c311c3ae9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 8 Dec 2024 19:33:57 +0100 Subject: [PATCH 136/144] drivers/nhs_ser.c: size porta[] as PATH_MAX [#2692] Signed-off-by: Jim Klimov --- drivers/nhs_ser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nhs_ser.c b/drivers/nhs_ser.c index 1a13e0caa7..a17ab2645f 100644 --- a/drivers/nhs_ser.c +++ b/drivers/nhs_ser.c @@ -224,7 +224,7 @@ static unsigned int max_checktime = 6000000; /* max wait time: 6 seconds */ static unsigned int send_extended = 0; static int bwritten = 0; static unsigned char datapacket[DATAPACKETSIZE]; -static char porta[1024] = DEFAULTPORT; +static char porta[PATH_MAX] = DEFAULTPORT; static int baudrate = DEFAULTBAUD; static float minpower = 0; static float maxpower = 0; From 3c1e3a2849a347bfd10407e7d198ed9aecc66e7d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 9 Dec 2024 00:40:28 +0100 Subject: [PATCH 137/144] drivers/nhs_ser.c: upsdrv_updateinfo(): revise byte-reading loop vs. packet data interpretation; un-indent more code [#2692] Add range-check to not overflow the datapacket[] buffer. Signed-off-by: Jim Klimov --- drivers/nhs_ser.c | 796 ++++++++++++++++++++++++---------------------- 1 file changed, 413 insertions(+), 383 deletions(-) diff --git a/drivers/nhs_ser.c b/drivers/nhs_ser.c index a17ab2645f..2398385ab7 100644 --- a/drivers/nhs_ser.c +++ b/drivers/nhs_ser.c @@ -50,7 +50,7 @@ #define DEFAULTPORT "/dev/ttyACM0" #define DEFAULTPF 0.9 #define DEFAULTPERC 2.0 -#define DATAPACKETSIZE 100 +#define DATAPACKETSIZE 100 /* NOTE: Practical anticipated max is 50 */ #define DEFAULTBATV 12.0 /* comms revival attempts before declaring them stale */ @@ -1758,6 +1758,7 @@ void upsdrv_updateinfo(void) { float abat = 0; float actual_current = 0; upsinfo ups; + time_t now; upsdebugx(3, "%s: starting...", __func__); @@ -1768,411 +1769,440 @@ void upsdrv_updateinfo(void) { /* Clean all read buffers to avoid errors: * To clean OUTPUT buffer is TCOFLUSH. * To both is TCIOFLUSH. + * //tcflush(serial_fd, TCIFLUSH); + * + * Alternative implemented below - we might + * potentially break off in the middle of + * the read() loop and continue in the next + * call to the method. In reality this is + * unlikely. */ - /* //tcflush(serial_fd, TCIFLUSH); */ chr = '\0'; while (read(serial_fd, &chr, 1) > 0) { if (chr == 0xFF) { /* DataPacket start */ datapacketstart = true; + memset(datapacket, 0, sizeof(datapacket)); + datapacket_index = 0; } /* end for */ if (datapacketstart) { datapacket[datapacket_index] = chr; datapacket_index++; if (chr == 0xFE) { /* DataPacket */ - time_t now = time(NULL); - upsdebugx(4, "DATAPACKET INDEX IS %d", datapacket_index); - if (lastdp != 0) { - tempodecorrido = difftime(now, lastdp); - } - lastdp = now; - /* If size is 18 or 50, may be an answer packet. - * Then check if doesn't have already a packet processed. - * We don't need to read all times these information. - * Can be a corrupted packet too. + break; + } + if (datapacket_index >= sizeof(datapacket)) { + upslogx(LOG_WARNING, "Incoming packet does not seem to end, discarding!"); + datapacketstart = false; + /* // datapacket_index = 0; */ + break; + } + } + } /* end while read */ + + if (chr != 0xFE || !datapacketstart) { + upsdebugx(2, "%s: packet reading did not finish, not interpreting yet", __func__); + return; + } + + /* Interpret the just-finished packet buffer */ + now = time(NULL); + upsdebugx(4, "DATAPACKET INDEX IS %d", datapacket_index); + if (lastdp != 0) { + tempodecorrido = difftime(now, lastdp); + } + lastdp = now; + + /* Parse the bytes into a structure to handle below: + * If size is 18 or 50, may be an answer packet. + * Then check if doesn't have already a packet processed. + * We don't need to read all times these information. + * Can be a corrupted packet too. + */ + if (((datapacket_index == 18) || (datapacket_index == 50)) && (!lastpkthwinfo.checksum_ok)) { + /* Re-read HW info only if the old one is broken */ + lastpkthwinfo = mount_hwinfo(datapacket, datapacket_index); + } /* end if */ + else if (datapacket_index == 21) { + lastpktdata = mount_datapacket(datapacket, datapacket_index, tempodecorrido, lastpkthwinfo); + } /* end else-if */ + else { + upslogx(LOG_WARNING, "Incoming packet size not recognized, discarding!"); + } /* end else */ + + /* Clean datapacket structure to avoid problems for next parse */ + datapacket_index = 0; + memset(datapacket, 0, sizeof(datapacket)); + datapacketstart = false; + + if (lastpktdata.checksum_ok) { + /* checksum is OK, then use it to set values */ + upsdebugx(4, "Data Packet seems be OK"); + if (lastpkthwinfo.size == 0) + upsdebugx(2, "Pkt HWINFO is not OK. See if will be requested next time!"); + else { + if (lastpkthwinfo.checksum_ok) { + upsdebugx(4, "Pkt HWINFO is OK. Model is %d, hwversion is %d and swversion is %d", lastpkthwinfo.model, lastpkthwinfo.hardwareversion, lastpkthwinfo.softwareversion); + /* We need to set data on NUT with data + * that I believe that I can calculate. + * Now setting data on NUT + */ + ups = getupsinfo(lastpkthwinfo.model); + upsdebugx(4, "UPS Struct data: Code %d Model %s VA %d", ups.upscode, ups.upsdesc, ups.VA); + dstate_setinfo("device.model", "%s", ups.upsdesc); + dstate_setinfo("device.mfr", "%s", MANUFACTURER); + dstate_setinfo("device.serial", "%s", lastpkthwinfo.serial); + dstate_setinfo("device.type", "%s", "ups"); + + /* Setting UPS Status: + * OL -- On line (mains is present): Code below + * OB -- On battery (mains is not present) : Code below + * LB -- Low battery: Code below + * HB -- High battery: NHS doesn't have any variable with that information. Feel free to discover a way to set it + * RB -- The battery needs to be replaced: Well, as mentioned, we can write some infos on nobreak fw, on structures like pkt_hwinfo.year, pkt_hwinfo.month, etc. I never found any equipment with these values. + * CHRG -- The battery is charging: Code below + * DISCHRG -- The battery is discharging (inverter is providing load power): Code Below + * BYPASS -- UPS bypass circuit is active -- no battery protection is available: It's another PROBLEM, because NHS can work in bypass mode in some models, even if you have sealed batteries on it (without any external battery device). On the moment, i'll won't work with that. Feel free to discover how it work correctly. + * CAL -- UPS is currently performing runtime calibration (on battery) + * OFF -- UPS is offline and is not supplying power to the load + * OVER -- UPS is overloaded + * TRIM -- UPS is trimming incoming voltage (called "buck" in some hardware) + * BOOST -- UPS is boosting incoming voltage + * FSD -- Forced Shutdown (restricted use, see the note below) */ - if (((datapacket_index == 18) || (datapacket_index == 50)) && (!lastpkthwinfo.checksum_ok)) { - lastpkthwinfo = mount_hwinfo(datapacket, datapacket_index); + + /* Decision Chain commented below */ + + /* First we check if system is on battery or not */ + upsdebugx(4, "Set UPS status as OFF and start checking. s_battery_mode is %d", lastpktdata.s_battery_mode); + if (lastpkthwinfo.s_220V_in) { + upsdebugx(4, "I'm on 220v IN!. My overvoltage is %d", lastpkthwinfo.undervoltagein220V); + min_input_power = lastpkthwinfo.undervoltagein220V; + } + else { + upsdebugx(4, "I'm on 120v IN!. My overvoltage is %d", lastpkthwinfo.undervoltagein120V); + min_input_power = lastpkthwinfo.undervoltagein120V; + } + if (lastpktdata.s_battery_mode) { + /* ON BATTERY */ + upsdebugx(4, "UPS is on Battery Mode"); + dstate_setinfo("ups.status", "%s", "OB"); + if (lastpktdata.s_battery_low) { + /* If battery is LOW, warn user! */ + upsdebugx(4, "UPS is on Battery Mode and in Low Battery State"); + dstate_setinfo("ups.status", "%s", "LB"); + } /* end if */ } /* end if */ else { - if (datapacket_index == 21) - lastpktdata = mount_datapacket(datapacket, datapacket_index, tempodecorrido, lastpkthwinfo); - } /* end else */ - /* Clean datapacket structure to avoid problems */ - datapacket_index = 0; - memset(datapacket, 0, sizeof(datapacket)); - datapacketstart = false; - if (lastpktdata.checksum_ok) { - /* checksum is OK, then use it to set values */ - upsdebugx(4, "Data Packet seems be OK"); - if (lastpkthwinfo.size == 0) - upsdebugx(2, "Pkt HWINFO is not OK. See if will be requested next time!"); + /* Check if MAINS (power) is not preset. + * Well, we can check pkt_data.s_network_failure too... */ + if ((lastpktdata.vacinrms <= min_input_power) || (lastpktdata.s_network_failure)) { + upsdebugx(4, "UPS has power-in value %0.2f " + "and min_input_power is %d, " + "or network is in failure. Network failure is %d", + lastpktdata.vacinrms, + min_input_power, + lastpktdata.s_network_failure); + dstate_setinfo("ups.status", "%s", "DISCHRG"); + } /* end if */ else { - if (lastpkthwinfo.checksum_ok) { - upsdebugx(4, "Pkt HWINFO is OK. Model is %d, hwversion is %d and swversion is %d", lastpkthwinfo.model, lastpkthwinfo.hardwareversion, lastpkthwinfo.softwareversion); - /* We need to set data on NUT with data - * that I believe that I can calculate. - * Now setting data on NUT - */ - ups = getupsinfo(lastpkthwinfo.model); - upsdebugx(4, "UPS Struct data: Code %d Model %s VA %d", ups.upscode, ups.upsdesc, ups.VA); - dstate_setinfo("device.model", "%s", ups.upsdesc); - dstate_setinfo("device.mfr", "%s", MANUFACTURER); - dstate_setinfo("device.serial", "%s", lastpkthwinfo.serial); - dstate_setinfo("device.type", "%s", "ups"); - - /* Setting UPS Status: - * OL -- On line (mains is present): Code below - * OB -- On battery (mains is not present) : Code below - * LB -- Low battery: Code below - * HB -- High battery: NHS doesn't have any variable with that information. Feel free to discover a way to set it - * RB -- The battery needs to be replaced: Well, as mentioned, we can write some infos on nobreak fw, on structures like pkt_hwinfo.year, pkt_hwinfo.month, etc. I never found any equipment with these values. - * CHRG -- The battery is charging: Code below - * DISCHRG -- The battery is discharging (inverter is providing load power): Code Below - * BYPASS -- UPS bypass circuit is active -- no battery protection is available: It's another PROBLEM, because NHS can work in bypass mode in some models, even if you have sealed batteries on it (without any external battery device). On the moment, i'll won't work with that. Feel free to discover how it work correctly. - * CAL -- UPS is currently performing runtime calibration (on battery) - * OFF -- UPS is offline and is not supplying power to the load - * OVER -- UPS is overloaded - * TRIM -- UPS is trimming incoming voltage (called "buck" in some hardware) - * BOOST -- UPS is boosting incoming voltage - * FSD -- Forced Shutdown (restricted use, see the note below) - */ - - /* Decision Chain commented below */ - - /* First we check if system is on battery or not */ - upsdebugx(4, "Set UPS status as OFF and start checking. s_battery_mode is %d", lastpktdata.s_battery_mode); - if (lastpkthwinfo.s_220V_in) { - upsdebugx(4, "I'm on 220v IN!. My overvoltage is %d", lastpkthwinfo.undervoltagein220V); - min_input_power = lastpkthwinfo.undervoltagein220V; + /* MAINS is present. We need to check some situations. + * NHS only charge if have more than min_input_power. + * If MAINS is less than or equal to min_input_power, + * then the UPS goes to BATTERY + */ + if (lastpktdata.vacinrms > min_input_power) { + upsdebugx(4, "UPS is on MAINS"); + if (lastpktdata.s_charger_on) { + upsdebugx(4, "UPS Charging..."); + dstate_setinfo("ups.status", "%s", "CHRG"); } else { - upsdebugx(4, "I'm on 120v IN!. My overvoltage is %d", lastpkthwinfo.undervoltagein120V); - min_input_power = lastpkthwinfo.undervoltagein120V; - } - if (lastpktdata.s_battery_mode) { - /* ON BATTERY */ - upsdebugx(4, "UPS is on Battery Mode"); - dstate_setinfo("ups.status", "%s", "OB"); - if (lastpktdata.s_battery_low) { - /* If battery is LOW, warn user! */ - upsdebugx(4, "UPS is on Battery Mode and in Low Battery State"); - dstate_setinfo("ups.status", "%s", "LB"); - } /* end if */ - } /* end if */ - else { - /* Check if MAINS (power) is not preset. - * Well, we can check pkt_data.s_network_failure too... */ - if ((lastpktdata.vacinrms <= min_input_power) || (lastpktdata.s_network_failure)) { - upsdebugx(4, "UPS has power-in value %0.2f " - "and min_input_power is %d, " - "or network is in failure. Network failure is %d", - lastpktdata.vacinrms, - min_input_power, - lastpktdata.s_network_failure); - dstate_setinfo("ups.status", "%s", "DISCHRG"); + if ((lastpktdata.s_network_failure) || (lastpktdata.s_fast_network_failure)) { + upsdebugx(4, "UPS is on battery mode because network failure or fast network failure"); + dstate_setinfo("ups.status", "%s", "OB"); } /* end if */ else { - /* MAINS is present. We need to check some situations. - * NHS only charge if have more than min_input_power. - * If MAINS is less than or equal to min_input_power, - * then the UPS goes to BATTERY - */ - if (lastpktdata.vacinrms > min_input_power) { - upsdebugx(4, "UPS is on MAINS"); - if (lastpktdata.s_charger_on) { - upsdebugx(4, "UPS Charging..."); - dstate_setinfo("ups.status", "%s", "CHRG"); - } - else { - if ((lastpktdata.s_network_failure) || (lastpktdata.s_fast_network_failure)) { - upsdebugx(4, "UPS is on battery mode because network failure or fast network failure"); - dstate_setinfo("ups.status", "%s", "OB"); - } /* end if */ - else { - upsdebugx(4, "All is OK. UPS is on ONLINE!"); - dstate_setinfo("ups.status", "%s", "OL"); - } /* end else */ - } /* end else */ - } /* end if */ - else { - /* Energy is below limit. - * Nobreak is probably in battery mode... */ - if (lastpktdata.s_battery_low) - dstate_setinfo("ups.status", "%s", "LB"); - else { - /* ...or network failure */ - dstate_setinfo("ups.status", "%s", "OB"); - } /* end else */ - } /* end else */ + upsdebugx(4, "All is OK. UPS is on ONLINE!"); + dstate_setinfo("ups.status", "%s", "OL"); } /* end else */ } /* end else */ - - numbat = get_numbat(); - if (numbat == 0) - numbat = lastpkthwinfo.numbatteries; - else - upsdebugx(4, "Number of batteries is set to %d", numbat); - vbat = get_vbat(); - ah = get_ah(); - - /* Set all alarms possible */ - alarm[0] = '\0'; - if (lastpktdata.s_battery_mode) - snprintf(alarm, sizeof(alarm), "%s", "|UPS IN BATTERY MODE|"); - if (lastpktdata.s_battery_low) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|UPS IN BATTERY MODE|"); - if (lastpktdata.s_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|NETWORK FAILURE|"); - - /* FIXME: Really same criteria in these 3? */ - if (lastpktdata.s_fast_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|FAST NETWORK FAILURE|"); - if (lastpktdata.s_fast_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|220v IN|"); - if (lastpktdata.s_fast_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|220v OUT|"); - - if (lastpktdata.s_bypass_on) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|BYPASS ON|"); - if (lastpktdata.s_charger_on) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|CHARGER ON|"); - dstate_setinfo("ups.alarm", "%s", alarm); - dstate_setinfo("ups.model", "%s", ups.upsdesc); - dstate_setinfo("ups.mfr", "%s", MANUFACTURER); - dstate_setinfo("ups.serial", "%s", lastpkthwinfo.serial); - dstate_setinfo("ups.firmware", "%u", lastpkthwinfo.softwareversion); - /* Setting hardware version here. - * Did not find another place to do this. - * Feel free to correct it. - * FIXME: move to upsdrv_initinfo() or so - */ - dstate_setinfo("ups.firmware.aux", "%u", lastpkthwinfo.hardwareversion); - dstate_setinfo("ups.temperature", "%0.2f", lastpktdata.tempmed_real); - dstate_setinfo("ups.load", "%u", lastpktdata.potrms); - dstate_setinfo("ups.efficiency", "%0.2f", calculate_efficiency(lastpktdata.vacoutrms, lastpktdata.vacinrms)); - va = get_va(lastpkthwinfo.model); - pf = get_pf(); - /* vpower is the power in Watts */ - vpower = ((va * pf) * (lastpktdata.potrms / 100.0)); - /* abat is the battery's consumption in Amperes */ - abat = ((vpower / lastpktdata.vdcmed_real) / numbat); - if (vpower > maxpower) - maxpower = vpower; - if (vpower < minpower) - minpower = vpower; - dstate_setinfo("ups.power", "%0.2f", vpower); - dstate_setinfo("ups.power.nominal", "%u", va); - dstate_setinfo("ups.realpower", "%ld", lrint(round(vpower))); - dstate_setinfo("ups.realpower.nominal", "%ld", lrint(round((double)va * (double)pf))); - dstate_setinfo("ups.beeper.status", "%d", !lastpkthwinfo.c_buzzer_disable); - dstate_setinfo("input.voltage", "%0.2f", lastpktdata.vacinrms); - dstate_setinfo("input.voltage.maximum", "%0.2f", lastpktdata.vacinrmsmin); - dstate_setinfo("input.voltage.minimum", "%0.2f", lastpktdata.vacinrmsmax); - vin_underv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V; - vin_overv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V; - perc = f_equal(get_vin_perc("vin_low_warn_perc"), get_vin_perc("vin_low_crit_perc")) ? 2 : 1; - vin_low_warn = vin_underv + (vin_underv * ((get_vin_perc("vin_low_warn_perc") * perc) / 100.0)); - dstate_setinfo("input.voltage.low.warning", "%0.2f", calculated); - vin_low_crit = vin_underv + (vin_underv * (get_vin_perc("vin_low_crit_perc") / 100.0)); - dstate_setinfo("input.voltage.low.critical", "%0.2f", calculated); - vin_high_warn = vin_overv + (vin_overv * ((get_vin_perc("vin_high_warn_perc") * perc) / 100.0)); - dstate_setinfo("input.voltage.high.warning", "%0.2f", calculated); - vin_high_crit = vin_overv + (vin_overv * (get_vin_perc("vin_high_crit_perc") / 100.0)); - dstate_setinfo("input.voltage.high.critical", "%0.2f", calculated); - vin = lastpkthwinfo.s_220V_in ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; - dstate_setinfo("input.voltage.nominal", "%u", vin); - dstate_setinfo("input.transfer.low", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V); - dstate_setinfo("input.transfer.high", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V); - dstate_setinfo("output.voltage", "%0.2f", lastpktdata.vacoutrms); - vout = lastpkthwinfo.s_220V_out ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; - dstate_setinfo("output.voltage.nominal", "%u", vout); - dstate_setinfo("voltage", "%0.2f", lastpktdata.vacoutrms); - dstate_setinfo("voltage.nominal", "%u", vout); - dstate_setinfo("voltage.maximum", "%0.2f", lastpktdata.vacinrmsmax); - dstate_setinfo("voltage.minimum", "%0.2f", lastpktdata.vacinrmsmin); - dstate_setinfo("voltage.low.warning", "%0.2f", vin_low_warn); - dstate_setinfo("voltage.low.critical", "%0.2f", vin_low_crit); - dstate_setinfo("voltage.high.warning", "%0.2f", vin_high_warn); - dstate_setinfo("voltage.high.critical", "%0.2f", vin_high_crit); - dstate_setinfo("power", "%0.2f", vpower); - dstate_setinfo("power.maximum", "%0.2f", maxpower); - dstate_setinfo("power.minimum", "%0.2f", minpower); - dstate_setinfo("power.percent", "%u", lastpktdata.potrms); - if (lastpktdata.potrms > maxpowerperc) - maxpowerperc = lastpktdata.potrms; - if (lastpktdata.potrms < minpowerperc) - minpowerperc = lastpktdata.potrms; - dstate_setinfo("power.maximum.percent", "%u", maxpowerperc); - dstate_setinfo("power.minimum.percent", "%u", minpowerperc); - dstate_setinfo("realpower", "%ld", lrint(round(vpower))); - dstate_setinfo("power", "%ld", lrint(round(va * (lastpktdata.potrms / 100.0)))); - bcharge = lrint(round((lastpktdata.vdcmed_real * 100) / vbat)); - if (bcharge > 100) - bcharge = 100; - dstate_setinfo("battery.charge", "%ld", bcharge); - dstate_setinfo("battery.voltage", "%0.2f", lastpktdata.vdcmed_real); - dstate_setinfo("battery.voltage.nominal", "%u", vbat); - dstate_setinfo("battery.capacity", "%u", ah); - dstate_setinfo("battery.capacity.nominal", "%0.2f", (float)ah * pf); - dstate_setinfo("battery.current", "%0.2f", abat); - dstate_setinfo("battery.current.total", "%0.2f", (float)abat * numbat); - dstate_setinfo("battery.temperature", "%ld", lrint(round(lastpktdata.tempmed_real))); - dstate_setinfo("battery.packs", "%u", numbat); - /* We will calculate autonomy in seconds - * autonomy_secs = (ah / lastpktdata.vdcmed_real) * 3600; - * Maybe wrong, too. - * People say that the correct calculation is - * - * Battery Amp-Hour / (Power in Watts / battery voltage) - * - * Is that correct? I don't know. I'll use it for now. - */ - - /* That result is IN HOURS. We need to convert it to seconds */ - actual_current = vpower / vbat; /* Current consumption in A*/ - autonomy_secs = (ah / actual_current) * 3600; - - dstate_setinfo("battery.runtime", "%u", autonomy_secs); - dstate_setinfo("battery.runtime.low", "%u", 30); - if (lastpktdata.s_charger_on) { - dstate_setinfo("battery.charger.status", "%s", "CHARGING"); - } else { - if (lastpktdata.s_battery_mode) - dstate_setinfo("battery.charger.status", "%s", "DISCHARGING"); - else - dstate_setinfo("battery.charger.status", "%s", "RESTING"); - } - /* Now, creating a structure called NHS, */ - dstate_setinfo("experimental.nhs.hw.header", "%u", lastpkthwinfo.header); - dstate_setinfo("experimental.nhs.hw.size", "%u", lastpkthwinfo.size); - dstate_setinfo("experimental.nhs.hw.type", "%c", lastpkthwinfo.type); - dstate_setinfo("experimental.nhs.hw.model", "%u", lastpkthwinfo.model); - dstate_setinfo("experimental.nhs.hw.hardwareversion", "%u", lastpkthwinfo.hardwareversion); - dstate_setinfo("experimental.nhs.hw.softwareversion", "%u", lastpkthwinfo.softwareversion); - dstate_setinfo("experimental.nhs.hw.configuration", "%u", lastpkthwinfo.configuration); - for (i = 0; i < 5; i++) { - /* Reusing variable */ - snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.configuration_array_p%d", i); - dstate_setinfo(alarm, "%u", lastpkthwinfo.configuration_array[i]); - } - dstate_setinfo("experimental.nhs.hw.c_oem_mode", "%s", lastpkthwinfo.c_oem_mode ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_buzzer_disable", "%s", lastpkthwinfo.c_buzzer_disable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_potmin_disable", "%s", lastpkthwinfo.c_potmin_disable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_rearm_enable", "%s", lastpkthwinfo.c_rearm_enable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_bootloader_enable", "%s", lastpkthwinfo.c_bootloader_enable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.numbatteries", "%u", lastpkthwinfo.numbatteries); - dstate_setinfo("experimental.nhs.hw.undervoltagein120V", "%u", lastpkthwinfo.undervoltagein120V); - dstate_setinfo("experimental.nhs.hw.overvoltagein120V", "%u", lastpkthwinfo.overvoltagein120V); - dstate_setinfo("experimental.nhs.hw.undervoltagein220V", "%u", lastpkthwinfo.undervoltagein220V); - dstate_setinfo("experimental.nhs.hw.overvoltagein220V", "%u", lastpkthwinfo.overvoltagein220V); - dstate_setinfo("experimental.nhs.hw.tensionout120V", "%u", lastpkthwinfo.tensionout120V); - dstate_setinfo("experimental.nhs.hw.tensionout220V", "%u", lastpkthwinfo.tensionout220V); - dstate_setinfo("experimental.nhs.hw.statusval", "%u", lastpkthwinfo.statusval); - for (i = 0; i < 6; i++) { - /* Reusing variable */ - snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.status_p%d", i); - dstate_setinfo(alarm, "%u", lastpkthwinfo.status[i]); - } - dstate_setinfo("experimental.nhs.hw.s_220V_in", "%s", lastpkthwinfo.s_220V_in ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_220V_out", "%s", lastpkthwinfo.s_220V_out ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_sealed_battery", "%s", lastpkthwinfo.s_sealed_battery ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_show_out_tension", "%s", lastpkthwinfo.s_show_out_tension ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_show_temperature", "%s", lastpkthwinfo.s_show_temperature ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_show_charger_current", "%s", lastpkthwinfo.s_show_charger_current ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.chargercurrent", "%u", lastpkthwinfo.chargercurrent); - dstate_setinfo("experimental.nhs.hw.checksum", "%u", lastpkthwinfo.checksum); - dstate_setinfo("experimental.nhs.hw.checksum_calc", "%u", lastpkthwinfo.checksum_calc); - dstate_setinfo("experimental.nhs.hw.checksum_ok", "%s", lastpkthwinfo.checksum_ok ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.serial", "%s", lastpkthwinfo.serial); - dstate_setinfo("experimental.nhs.hw.year", "%u", lastpkthwinfo.year); - dstate_setinfo("experimental.nhs.hw.month", "%u", lastpkthwinfo.month); - dstate_setinfo("experimental.nhs.hw.wday", "%u", lastpkthwinfo.wday); - dstate_setinfo("experimental.nhs.hw.hour", "%u", lastpkthwinfo.hour); - dstate_setinfo("experimental.nhs.hw.minute", "%u", lastpkthwinfo.minute); - dstate_setinfo("experimental.nhs.hw.second", "%u", lastpkthwinfo.second); - dstate_setinfo("experimental.nhs.hw.alarmyear", "%u", lastpkthwinfo.alarmyear); - dstate_setinfo("experimental.nhs.hw.alarmmonth", "%u", lastpkthwinfo.alarmmonth); - dstate_setinfo("experimental.nhs.hw.alarmwday", "%u", lastpkthwinfo.alarmwday); - dstate_setinfo("experimental.nhs.hw.alarmday", "%u", lastpkthwinfo.alarmday); - dstate_setinfo("experimental.nhs.hw.alarmhour", "%u", lastpkthwinfo.alarmhour); - dstate_setinfo("experimental.nhs.hw.alarmminute", "%u", lastpkthwinfo.alarmminute); - dstate_setinfo("experimental.nhs.hw.alarmsecond", "%u", lastpkthwinfo.alarmsecond); - dstate_setinfo("experimental.nhs.hw.end_marker", "%u", lastpkthwinfo.end_marker); - - /* Data packet */ - dstate_setinfo("experimental.nhs.data.header", "%u", lastpktdata.header); - dstate_setinfo("experimental.nhs.data.length", "%u", lastpktdata.length); - dstate_setinfo("experimental.nhs.data.packet_type", "%c", lastpktdata.packet_type); - dstate_setinfo("experimental.nhs.data.vacinrms_high", "%u", lastpktdata.vacinrms_high); - dstate_setinfo("experimental.nhs.data.vacinrms_low", "%u", lastpktdata.vacinrms_low); - dstate_setinfo("experimental.nhs.data.vacinrms", "%0.2f", lastpktdata.vacinrms); - dstate_setinfo("experimental.nhs.data.vdcmed_high", "%u", lastpktdata.vdcmed_high); - dstate_setinfo("experimental.nhs.data.vdcmed_low", "%u", lastpktdata.vdcmed_low); - dstate_setinfo("experimental.nhs.data.vdcmed", "%0.2f", lastpktdata.vdcmed); - dstate_setinfo("experimental.nhs.data.vdcmed_real", "%0.2f", lastpktdata.vdcmed_real); - dstate_setinfo("experimental.nhs.data.potrms", "%u", lastpktdata.potrms); - dstate_setinfo("experimental.nhs.data.vacinrmsmin_high", "%u", lastpktdata.vacinrmsmin_high); - dstate_setinfo("experimental.nhs.data.vacinrmsmin_low", "%u", lastpktdata.vacinrmsmin_low); - dstate_setinfo("experimental.nhs.data.vacinrmsmin", "%0.2f", lastpktdata.vacinrmsmin); - dstate_setinfo("experimental.nhs.data.vacinrmsmax_high", "%u", lastpktdata.vacinrmsmax_high); - dstate_setinfo("experimental.nhs.data.vacinrmsmax_low", "%u", lastpktdata.vacinrmsmax_low); - dstate_setinfo("experimental.nhs.data.vacinrmsmax", "%0.2f", lastpktdata.vacinrmsmax); - dstate_setinfo("experimental.nhs.data.vacoutrms_high", "%u", lastpktdata.vacoutrms_high); - dstate_setinfo("experimental.nhs.data.vacoutrms_low", "%u", lastpktdata.vacoutrms_low); - dstate_setinfo("experimental.nhs.data.vacoutrms", "%0.2f", lastpktdata.vacoutrms); - dstate_setinfo("experimental.nhs.data.tempmed_high", "%u", lastpktdata.tempmed_high); - dstate_setinfo("experimental.nhs.data.tempmed_low", "%u", lastpktdata.tempmed_low); - dstate_setinfo("experimental.nhs.data.tempmed", "%0.2f", lastpktdata.tempmed); - dstate_setinfo("experimental.nhs.data.tempmed_real", "%0.2f", lastpktdata.tempmed_real); - dstate_setinfo("experimental.nhs.data.icarregrms", "%u", lastpktdata.icarregrms); - dstate_setinfo("experimental.nhs.data.icarregrms_real", "%u", lastpktdata.icarregrms_real); - dstate_setinfo("experimental.nhs.data.battery_tension", "%0.2f", lastpktdata.battery_tension); - dstate_setinfo("experimental.nhs.data.perc_output", "%u", lastpktdata.perc_output); - dstate_setinfo("experimental.nhs.data.statusval", "%u", lastpktdata.statusval); - for (i = 0; i < 8; i++) { - /* Reusing variable */ - snprintf(alarm, sizeof(alarm), "experimental.nhs.data.status_p%d", i); - dstate_setinfo(alarm, "%u", lastpktdata.status[i]); - } - dstate_setinfo("experimental.nhs.data.nominaltension", "%u", lastpktdata.nominaltension); - dstate_setinfo("experimental.nhs.data.timeremain", "%0.2f", lastpktdata.timeremain); - dstate_setinfo("experimental.nhs.data.s_battery_mode", "%s", lastpktdata.s_battery_mode ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_battery_low", "%s", lastpktdata.s_battery_low ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_network_failure", "%s", lastpktdata.s_network_failure ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_fast_network_failure", "%s", lastpktdata.s_fast_network_failure ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_220_in", "%s", lastpktdata.s_220_in ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_220_out", "%s", lastpktdata.s_220_out ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_bypass_on", "%s", lastpktdata.s_bypass_on ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_charger_on", "%s", lastpktdata.s_charger_on ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.checksum", "%u", lastpktdata.checksum); - dstate_setinfo("experimental.nhs.data.checksum_ok", "%s", lastpktdata.checksum_ok ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.checksum_calc", "%u", lastpktdata.checksum_calc); - dstate_setinfo("experimental.nhs.data.end_marker", "%u", lastpktdata.end_marker); - dstate_setinfo("experimental.nhs.param.va", "%u", va); - dstate_setinfo("experimental.nhs.param.pf", "%0.2f", pf); - dstate_setinfo("experimental.nhs.param.ah", "%u", ah); - dstate_setinfo("experimental.nhs.param.vin_low_warn_perc", "%0.2f", get_vin_perc("vin_low_warn_perc")); - dstate_setinfo("experimental.nhs.param.vin_low_crit_perc", "%0.2f", get_vin_perc("vin_low_crit_perc")); - dstate_setinfo("experimental.nhs.param.vin_high_warn_perc", "%0.2f", get_vin_perc("vin_high_warn_perc")); - dstate_setinfo("experimental.nhs.param.vin_high_crit_perc", "%0.2f", get_vin_perc("vin_high_crit_perc")); - - dstate_dataok(); } /* end if */ else { - upsdebugx(4, "Checksum of pkt_hwinfo is corrupted or not initialized. Waiting for new request..."); - } + /* Energy is below limit. + * Nobreak is probably in battery mode... */ + if (lastpktdata.s_battery_low) + dstate_setinfo("ups.status", "%s", "LB"); + else { + /* ...or network failure */ + dstate_setinfo("ups.status", "%s", "OB"); + } /* end else */ + } /* end else */ } /* end else */ - } /* end if */ + } /* end else */ + + numbat = get_numbat(); + if (numbat == 0) + numbat = lastpkthwinfo.numbatteries; + else + upsdebugx(4, "Number of batteries is set to %d", numbat); + vbat = get_vbat(); + ah = get_ah(); + + /* Set all alarms possible */ + alarm[0] = '\0'; + if (lastpktdata.s_battery_mode) + snprintf(alarm, sizeof(alarm), "%s", "|UPS IN BATTERY MODE|"); + if (lastpktdata.s_battery_low) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|UPS IN BATTERY MODE|"); + if (lastpktdata.s_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|NETWORK FAILURE|"); + + /* FIXME: Really same criteria in these 3? */ + if (lastpktdata.s_fast_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|FAST NETWORK FAILURE|"); + if (lastpktdata.s_fast_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|220v IN|"); + if (lastpktdata.s_fast_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|220v OUT|"); + + if (lastpktdata.s_bypass_on) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|BYPASS ON|"); + if (lastpktdata.s_charger_on) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|CHARGER ON|"); + dstate_setinfo("ups.alarm", "%s", alarm); + dstate_setinfo("ups.model", "%s", ups.upsdesc); + dstate_setinfo("ups.mfr", "%s", MANUFACTURER); + dstate_setinfo("ups.serial", "%s", lastpkthwinfo.serial); + dstate_setinfo("ups.firmware", "%u", lastpkthwinfo.softwareversion); + /* Setting hardware version here. + * Did not find another place to do this. + * Feel free to correct it. + * FIXME: move to upsdrv_initinfo() or so + */ + dstate_setinfo("ups.firmware.aux", "%u", lastpkthwinfo.hardwareversion); + dstate_setinfo("ups.temperature", "%0.2f", lastpktdata.tempmed_real); + dstate_setinfo("ups.load", "%u", lastpktdata.potrms); + dstate_setinfo("ups.efficiency", "%0.2f", calculate_efficiency(lastpktdata.vacoutrms, lastpktdata.vacinrms)); + va = get_va(lastpkthwinfo.model); + pf = get_pf(); + /* vpower is the power in Watts */ + vpower = ((va * pf) * (lastpktdata.potrms / 100.0)); + /* abat is the battery's consumption in Amperes */ + abat = ((vpower / lastpktdata.vdcmed_real) / numbat); + if (vpower > maxpower) + maxpower = vpower; + if (vpower < minpower) + minpower = vpower; + dstate_setinfo("ups.power", "%0.2f", vpower); + dstate_setinfo("ups.power.nominal", "%u", va); + dstate_setinfo("ups.realpower", "%ld", lrint(round(vpower))); + dstate_setinfo("ups.realpower.nominal", "%ld", lrint(round((double)va * (double)pf))); + dstate_setinfo("ups.beeper.status", "%d", !lastpkthwinfo.c_buzzer_disable); + dstate_setinfo("input.voltage", "%0.2f", lastpktdata.vacinrms); + dstate_setinfo("input.voltage.maximum", "%0.2f", lastpktdata.vacinrmsmin); + dstate_setinfo("input.voltage.minimum", "%0.2f", lastpktdata.vacinrmsmax); + vin_underv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V; + vin_overv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V; + perc = f_equal(get_vin_perc("vin_low_warn_perc"), get_vin_perc("vin_low_crit_perc")) ? 2 : 1; + vin_low_warn = vin_underv + (vin_underv * ((get_vin_perc("vin_low_warn_perc") * perc) / 100.0)); + dstate_setinfo("input.voltage.low.warning", "%0.2f", calculated); + vin_low_crit = vin_underv + (vin_underv * (get_vin_perc("vin_low_crit_perc") / 100.0)); + dstate_setinfo("input.voltage.low.critical", "%0.2f", calculated); + vin_high_warn = vin_overv + (vin_overv * ((get_vin_perc("vin_high_warn_perc") * perc) / 100.0)); + dstate_setinfo("input.voltage.high.warning", "%0.2f", calculated); + vin_high_crit = vin_overv + (vin_overv * (get_vin_perc("vin_high_crit_perc") / 100.0)); + dstate_setinfo("input.voltage.high.critical", "%0.2f", calculated); + vin = lastpkthwinfo.s_220V_in ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; + dstate_setinfo("input.voltage.nominal", "%u", vin); + dstate_setinfo("input.transfer.low", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V); + dstate_setinfo("input.transfer.high", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V); + dstate_setinfo("output.voltage", "%0.2f", lastpktdata.vacoutrms); + vout = lastpkthwinfo.s_220V_out ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; + dstate_setinfo("output.voltage.nominal", "%u", vout); + dstate_setinfo("voltage", "%0.2f", lastpktdata.vacoutrms); + dstate_setinfo("voltage.nominal", "%u", vout); + dstate_setinfo("voltage.maximum", "%0.2f", lastpktdata.vacinrmsmax); + dstate_setinfo("voltage.minimum", "%0.2f", lastpktdata.vacinrmsmin); + dstate_setinfo("voltage.low.warning", "%0.2f", vin_low_warn); + dstate_setinfo("voltage.low.critical", "%0.2f", vin_low_crit); + dstate_setinfo("voltage.high.warning", "%0.2f", vin_high_warn); + dstate_setinfo("voltage.high.critical", "%0.2f", vin_high_crit); + dstate_setinfo("power", "%0.2f", vpower); + dstate_setinfo("power.maximum", "%0.2f", maxpower); + dstate_setinfo("power.minimum", "%0.2f", minpower); + dstate_setinfo("power.percent", "%u", lastpktdata.potrms); + if (lastpktdata.potrms > maxpowerperc) + maxpowerperc = lastpktdata.potrms; + if (lastpktdata.potrms < minpowerperc) + minpowerperc = lastpktdata.potrms; + dstate_setinfo("power.maximum.percent", "%u", maxpowerperc); + dstate_setinfo("power.minimum.percent", "%u", minpowerperc); + dstate_setinfo("realpower", "%ld", lrint(round(vpower))); + dstate_setinfo("power", "%ld", lrint(round(va * (lastpktdata.potrms / 100.0)))); + bcharge = lrint(round((lastpktdata.vdcmed_real * 100) / vbat)); + if (bcharge > 100) + bcharge = 100; + dstate_setinfo("battery.charge", "%ld", bcharge); + dstate_setinfo("battery.voltage", "%0.2f", lastpktdata.vdcmed_real); + dstate_setinfo("battery.voltage.nominal", "%u", vbat); + dstate_setinfo("battery.capacity", "%u", ah); + dstate_setinfo("battery.capacity.nominal", "%0.2f", (float)ah * pf); + dstate_setinfo("battery.current", "%0.2f", abat); + dstate_setinfo("battery.current.total", "%0.2f", (float)abat * numbat); + dstate_setinfo("battery.temperature", "%ld", lrint(round(lastpktdata.tempmed_real))); + dstate_setinfo("battery.packs", "%u", numbat); + /* We will calculate autonomy in seconds + * autonomy_secs = (ah / lastpktdata.vdcmed_real) * 3600; + * Maybe wrong, too. + * People say that the correct calculation is + * + * Battery Amp-Hour / (Power in Watts / battery voltage) + * + * Is that correct? I don't know. I'll use it for now. + */ + + /* That result is IN HOURS. We need to convert it to seconds */ + actual_current = vpower / vbat; /* Current consumption in A*/ + autonomy_secs = (ah / actual_current) * 3600; + + dstate_setinfo("battery.runtime", "%u", autonomy_secs); + dstate_setinfo("battery.runtime.low", "%u", 30); + if (lastpktdata.s_charger_on) { + dstate_setinfo("battery.charger.status", "%s", "CHARGING"); + } else { + if (lastpktdata.s_battery_mode) + dstate_setinfo("battery.charger.status", "%s", "DISCHARGING"); + else + dstate_setinfo("battery.charger.status", "%s", "RESTING"); + } + /* Now, creating a structure called NHS, */ + dstate_setinfo("experimental.nhs.hw.header", "%u", lastpkthwinfo.header); + dstate_setinfo("experimental.nhs.hw.size", "%u", lastpkthwinfo.size); + dstate_setinfo("experimental.nhs.hw.type", "%c", lastpkthwinfo.type); + dstate_setinfo("experimental.nhs.hw.model", "%u", lastpkthwinfo.model); + dstate_setinfo("experimental.nhs.hw.hardwareversion", "%u", lastpkthwinfo.hardwareversion); + dstate_setinfo("experimental.nhs.hw.softwareversion", "%u", lastpkthwinfo.softwareversion); + dstate_setinfo("experimental.nhs.hw.configuration", "%u", lastpkthwinfo.configuration); + for (i = 0; i < 5; i++) { + /* Reusing variable */ + snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.configuration_array_p%d", i); + dstate_setinfo(alarm, "%u", lastpkthwinfo.configuration_array[i]); + } + dstate_setinfo("experimental.nhs.hw.c_oem_mode", "%s", lastpkthwinfo.c_oem_mode ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_buzzer_disable", "%s", lastpkthwinfo.c_buzzer_disable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_potmin_disable", "%s", lastpkthwinfo.c_potmin_disable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_rearm_enable", "%s", lastpkthwinfo.c_rearm_enable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_bootloader_enable", "%s", lastpkthwinfo.c_bootloader_enable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.numbatteries", "%u", lastpkthwinfo.numbatteries); + dstate_setinfo("experimental.nhs.hw.undervoltagein120V", "%u", lastpkthwinfo.undervoltagein120V); + dstate_setinfo("experimental.nhs.hw.overvoltagein120V", "%u", lastpkthwinfo.overvoltagein120V); + dstate_setinfo("experimental.nhs.hw.undervoltagein220V", "%u", lastpkthwinfo.undervoltagein220V); + dstate_setinfo("experimental.nhs.hw.overvoltagein220V", "%u", lastpkthwinfo.overvoltagein220V); + dstate_setinfo("experimental.nhs.hw.tensionout120V", "%u", lastpkthwinfo.tensionout120V); + dstate_setinfo("experimental.nhs.hw.tensionout220V", "%u", lastpkthwinfo.tensionout220V); + dstate_setinfo("experimental.nhs.hw.statusval", "%u", lastpkthwinfo.statusval); + for (i = 0; i < 6; i++) { + /* Reusing variable */ + snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.status_p%d", i); + dstate_setinfo(alarm, "%u", lastpkthwinfo.status[i]); + } + dstate_setinfo("experimental.nhs.hw.s_220V_in", "%s", lastpkthwinfo.s_220V_in ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_220V_out", "%s", lastpkthwinfo.s_220V_out ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_sealed_battery", "%s", lastpkthwinfo.s_sealed_battery ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_show_out_tension", "%s", lastpkthwinfo.s_show_out_tension ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_show_temperature", "%s", lastpkthwinfo.s_show_temperature ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_show_charger_current", "%s", lastpkthwinfo.s_show_charger_current ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.chargercurrent", "%u", lastpkthwinfo.chargercurrent); + dstate_setinfo("experimental.nhs.hw.checksum", "%u", lastpkthwinfo.checksum); + dstate_setinfo("experimental.nhs.hw.checksum_calc", "%u", lastpkthwinfo.checksum_calc); + dstate_setinfo("experimental.nhs.hw.checksum_ok", "%s", lastpkthwinfo.checksum_ok ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.serial", "%s", lastpkthwinfo.serial); + dstate_setinfo("experimental.nhs.hw.year", "%u", lastpkthwinfo.year); + dstate_setinfo("experimental.nhs.hw.month", "%u", lastpkthwinfo.month); + dstate_setinfo("experimental.nhs.hw.wday", "%u", lastpkthwinfo.wday); + dstate_setinfo("experimental.nhs.hw.hour", "%u", lastpkthwinfo.hour); + dstate_setinfo("experimental.nhs.hw.minute", "%u", lastpkthwinfo.minute); + dstate_setinfo("experimental.nhs.hw.second", "%u", lastpkthwinfo.second); + dstate_setinfo("experimental.nhs.hw.alarmyear", "%u", lastpkthwinfo.alarmyear); + dstate_setinfo("experimental.nhs.hw.alarmmonth", "%u", lastpkthwinfo.alarmmonth); + dstate_setinfo("experimental.nhs.hw.alarmwday", "%u", lastpkthwinfo.alarmwday); + dstate_setinfo("experimental.nhs.hw.alarmday", "%u", lastpkthwinfo.alarmday); + dstate_setinfo("experimental.nhs.hw.alarmhour", "%u", lastpkthwinfo.alarmhour); + dstate_setinfo("experimental.nhs.hw.alarmminute", "%u", lastpkthwinfo.alarmminute); + dstate_setinfo("experimental.nhs.hw.alarmsecond", "%u", lastpkthwinfo.alarmsecond); + dstate_setinfo("experimental.nhs.hw.end_marker", "%u", lastpkthwinfo.end_marker); + + /* Data packet */ + dstate_setinfo("experimental.nhs.data.header", "%u", lastpktdata.header); + dstate_setinfo("experimental.nhs.data.length", "%u", lastpktdata.length); + dstate_setinfo("experimental.nhs.data.packet_type", "%c", lastpktdata.packet_type); + dstate_setinfo("experimental.nhs.data.vacinrms_high", "%u", lastpktdata.vacinrms_high); + dstate_setinfo("experimental.nhs.data.vacinrms_low", "%u", lastpktdata.vacinrms_low); + dstate_setinfo("experimental.nhs.data.vacinrms", "%0.2f", lastpktdata.vacinrms); + dstate_setinfo("experimental.nhs.data.vdcmed_high", "%u", lastpktdata.vdcmed_high); + dstate_setinfo("experimental.nhs.data.vdcmed_low", "%u", lastpktdata.vdcmed_low); + dstate_setinfo("experimental.nhs.data.vdcmed", "%0.2f", lastpktdata.vdcmed); + dstate_setinfo("experimental.nhs.data.vdcmed_real", "%0.2f", lastpktdata.vdcmed_real); + dstate_setinfo("experimental.nhs.data.potrms", "%u", lastpktdata.potrms); + dstate_setinfo("experimental.nhs.data.vacinrmsmin_high", "%u", lastpktdata.vacinrmsmin_high); + dstate_setinfo("experimental.nhs.data.vacinrmsmin_low", "%u", lastpktdata.vacinrmsmin_low); + dstate_setinfo("experimental.nhs.data.vacinrmsmin", "%0.2f", lastpktdata.vacinrmsmin); + dstate_setinfo("experimental.nhs.data.vacinrmsmax_high", "%u", lastpktdata.vacinrmsmax_high); + dstate_setinfo("experimental.nhs.data.vacinrmsmax_low", "%u", lastpktdata.vacinrmsmax_low); + dstate_setinfo("experimental.nhs.data.vacinrmsmax", "%0.2f", lastpktdata.vacinrmsmax); + dstate_setinfo("experimental.nhs.data.vacoutrms_high", "%u", lastpktdata.vacoutrms_high); + dstate_setinfo("experimental.nhs.data.vacoutrms_low", "%u", lastpktdata.vacoutrms_low); + dstate_setinfo("experimental.nhs.data.vacoutrms", "%0.2f", lastpktdata.vacoutrms); + dstate_setinfo("experimental.nhs.data.tempmed_high", "%u", lastpktdata.tempmed_high); + dstate_setinfo("experimental.nhs.data.tempmed_low", "%u", lastpktdata.tempmed_low); + dstate_setinfo("experimental.nhs.data.tempmed", "%0.2f", lastpktdata.tempmed); + dstate_setinfo("experimental.nhs.data.tempmed_real", "%0.2f", lastpktdata.tempmed_real); + dstate_setinfo("experimental.nhs.data.icarregrms", "%u", lastpktdata.icarregrms); + dstate_setinfo("experimental.nhs.data.icarregrms_real", "%u", lastpktdata.icarregrms_real); + dstate_setinfo("experimental.nhs.data.battery_tension", "%0.2f", lastpktdata.battery_tension); + dstate_setinfo("experimental.nhs.data.perc_output", "%u", lastpktdata.perc_output); + dstate_setinfo("experimental.nhs.data.statusval", "%u", lastpktdata.statusval); + for (i = 0; i < 8; i++) { + /* Reusing variable */ + snprintf(alarm, sizeof(alarm), "experimental.nhs.data.status_p%d", i); + dstate_setinfo(alarm, "%u", lastpktdata.status[i]); + } + dstate_setinfo("experimental.nhs.data.nominaltension", "%u", lastpktdata.nominaltension); + dstate_setinfo("experimental.nhs.data.timeremain", "%0.2f", lastpktdata.timeremain); + dstate_setinfo("experimental.nhs.data.s_battery_mode", "%s", lastpktdata.s_battery_mode ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_battery_low", "%s", lastpktdata.s_battery_low ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_network_failure", "%s", lastpktdata.s_network_failure ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_fast_network_failure", "%s", lastpktdata.s_fast_network_failure ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_220_in", "%s", lastpktdata.s_220_in ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_220_out", "%s", lastpktdata.s_220_out ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_bypass_on", "%s", lastpktdata.s_bypass_on ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_charger_on", "%s", lastpktdata.s_charger_on ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.checksum", "%u", lastpktdata.checksum); + dstate_setinfo("experimental.nhs.data.checksum_ok", "%s", lastpktdata.checksum_ok ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.checksum_calc", "%u", lastpktdata.checksum_calc); + dstate_setinfo("experimental.nhs.data.end_marker", "%u", lastpktdata.end_marker); + dstate_setinfo("experimental.nhs.param.va", "%u", va); + dstate_setinfo("experimental.nhs.param.pf", "%0.2f", pf); + dstate_setinfo("experimental.nhs.param.ah", "%u", ah); + dstate_setinfo("experimental.nhs.param.vin_low_warn_perc", "%0.2f", get_vin_perc("vin_low_warn_perc")); + dstate_setinfo("experimental.nhs.param.vin_low_crit_perc", "%0.2f", get_vin_perc("vin_low_crit_perc")); + dstate_setinfo("experimental.nhs.param.vin_high_warn_perc", "%0.2f", get_vin_perc("vin_high_warn_perc")); + dstate_setinfo("experimental.nhs.param.vin_high_crit_perc", "%0.2f", get_vin_perc("vin_high_crit_perc")); + + dstate_dataok(); } /* end if */ - } /* end if */ - } /* end while read */ + else { + upsdebugx(4, "Checksum of pkt_hwinfo is corrupted or not initialized. Waiting for new request..."); + } + } /* end else */ + } /* end if lastpktdata.checksum_ok */ /* Now the nobreak read buffer is empty. * We need a hw info packet to discover several variables, @@ -2225,7 +2255,7 @@ void upsdrv_updateinfo(void) { } usleep(checktime); } /* end else */ - } /* end if bad checksum */ + } /* end if lastpkthwinfo good/bad checksum */ upsdebugx(3, "%s: finished", __func__); } From e43abcd0a7dc94710358c433eacf07ec9317bb90 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 9 Dec 2024 03:10:18 +0100 Subject: [PATCH 138/144] drivers/nhs_ser.c: upsdrv_updateinfo(): refactor away interpret_pkt_hwinfo() and interpret_pkt_data() logic, separated by domain [#2692] Also rename some data points that do not conform to NUT standards, or drop if redundant. Signed-off-by: Jim Klimov --- drivers/nhs_ser.c | 855 +++++++++++++++++++++++++--------------------- 1 file changed, 473 insertions(+), 382 deletions(-) diff --git a/drivers/nhs_ser.c b/drivers/nhs_ser.c index 2398385ab7..8faeba6467 100644 --- a/drivers/nhs_ser.c +++ b/drivers/nhs_ser.c @@ -216,7 +216,7 @@ static int debug_pkt_data = 0, debug_pkt_hwinfo = 0, debug_pkt_raw = 0; static int serial_fd = -1; static unsigned char chr; -static int datapacket_index = 0; +static size_t datapacket_index = 0; static bool datapacketstart = false; static time_t lastdp = 0; static unsigned int checktime = 2000000; /* 2 seconds */ @@ -1732,9 +1732,127 @@ static int reconnect_ups_if_needed(void) { return serial_fd; } -void upsdrv_updateinfo(void) { - unsigned int i = 0; - char alarm[1024]; +static void interpret_pkt_hwinfo(void) { + /* TOTHINK: Consider passing in the packet struct as parameter? */ + upsinfo ups; + char alarm[1024]; /* Also used as a general string buffer */ + + if (!lastpktdata.checksum_ok) { + upslogx(LOG_WARNING, "%s: bad lastpkthwinfo.checksum", + __func__); + return; + } + + if (lastpkthwinfo.size < 1) { + upslogx(LOG_WARNING, "%s: Pkt HWINFO is not OK. " + "See if will be requested next time!", + __func__); + return; + } + + /* checksum is OK, then use it to set values */ + upsdebugx(4, "Pkt HWINFO is OK. Model code is %d, hwversion is %d " + "and swversion is %d", + lastpkthwinfo.model, + lastpkthwinfo.hardwareversion, + lastpkthwinfo.softwareversion); + + /* We need to set data on NUT with data + * that I believe that I can calculate. + * Now setting data on NUT + */ + ups = getupsinfo(lastpkthwinfo.model); + upsdebugx(4, "UPS Struct data: Code %d Model %s VA %d", ups.upscode, ups.upsdesc, ups.VA); + dstate_setinfo("device.model", "%s", ups.upsdesc); + dstate_setinfo("device.mfr", "%s", MANUFACTURER); + dstate_setinfo("device.serial", "%s", lastpkthwinfo.serial); + dstate_setinfo("device.type", "%s", "ups"); + + dstate_setinfo("ups.model", "%s", ups.upsdesc); + dstate_setinfo("ups.mfr", "%s", MANUFACTURER); + dstate_setinfo("ups.serial", "%s", lastpkthwinfo.serial); + dstate_setinfo("ups.firmware", "%u", lastpkthwinfo.softwareversion); + + /* Setting hardware version here. + * Did not find another place to do this. + * Feel free to correct it. + * FIXME: move to upsdrv_initinfo() or so + */ + dstate_setinfo("ups.firmware.aux", "%u", lastpkthwinfo.hardwareversion); + + if (debug_pkt_hwinfo) { + unsigned int i = 0; + + /* Now, creating a structure called NHS.HW, for latest HW + * info packet contents and raw data points, including those + * that were sorted above into NUT standard variables - + * for debug. + */ + dstate_setinfo("experimental.nhs.hw.header", "%u", lastpkthwinfo.header); + dstate_setinfo("experimental.nhs.hw.size", "%u", lastpkthwinfo.size); + dstate_setinfo("experimental.nhs.hw.type", "%c", lastpkthwinfo.type); + dstate_setinfo("experimental.nhs.hw.model", "%u", lastpkthwinfo.model); + dstate_setinfo("experimental.nhs.hw.hardwareversion", "%u", lastpkthwinfo.hardwareversion); + dstate_setinfo("experimental.nhs.hw.softwareversion", "%u", lastpkthwinfo.softwareversion); + dstate_setinfo("experimental.nhs.hw.configuration", "%u", lastpkthwinfo.configuration); + for (i = 0; i < 5; i++) { + /* Reusing variable */ + snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.configuration_array_p%d", i); + dstate_setinfo(alarm, "%u", lastpkthwinfo.configuration_array[i]); + } + dstate_setinfo("experimental.nhs.hw.c_oem_mode", "%s", lastpkthwinfo.c_oem_mode ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_buzzer_disable", "%s", lastpkthwinfo.c_buzzer_disable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_potmin_disable", "%s", lastpkthwinfo.c_potmin_disable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_rearm_enable", "%s", lastpkthwinfo.c_rearm_enable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.c_bootloader_enable", "%s", lastpkthwinfo.c_bootloader_enable ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.numbatteries", "%u", lastpkthwinfo.numbatteries); + dstate_setinfo("experimental.nhs.hw.undervoltagein120V", "%u", lastpkthwinfo.undervoltagein120V); + dstate_setinfo("experimental.nhs.hw.overvoltagein120V", "%u", lastpkthwinfo.overvoltagein120V); + dstate_setinfo("experimental.nhs.hw.undervoltagein220V", "%u", lastpkthwinfo.undervoltagein220V); + dstate_setinfo("experimental.nhs.hw.overvoltagein220V", "%u", lastpkthwinfo.overvoltagein220V); + dstate_setinfo("experimental.nhs.hw.tensionout120V", "%u", lastpkthwinfo.tensionout120V); + dstate_setinfo("experimental.nhs.hw.tensionout220V", "%u", lastpkthwinfo.tensionout220V); + dstate_setinfo("experimental.nhs.hw.statusval", "%u", lastpkthwinfo.statusval); + for (i = 0; i < 6; i++) { + /* Reusing variable */ + snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.status_p%d", i); + dstate_setinfo(alarm, "%u", lastpkthwinfo.status[i]); + } + dstate_setinfo("experimental.nhs.hw.s_220V_in", "%s", lastpkthwinfo.s_220V_in ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_220V_out", "%s", lastpkthwinfo.s_220V_out ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_sealed_battery", "%s", lastpkthwinfo.s_sealed_battery ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_show_out_tension", "%s", lastpkthwinfo.s_show_out_tension ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_show_temperature", "%s", lastpkthwinfo.s_show_temperature ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.s_show_charger_current", "%s", lastpkthwinfo.s_show_charger_current ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.chargercurrent", "%u", lastpkthwinfo.chargercurrent); + dstate_setinfo("experimental.nhs.hw.checksum", "%u", lastpkthwinfo.checksum); + dstate_setinfo("experimental.nhs.hw.checksum_calc", "%u", lastpkthwinfo.checksum_calc); + dstate_setinfo("experimental.nhs.hw.checksum_ok", "%s", lastpkthwinfo.checksum_ok ? "true" : "false"); + dstate_setinfo("experimental.nhs.hw.serial", "%s", lastpkthwinfo.serial); + dstate_setinfo("experimental.nhs.hw.year", "%u", lastpkthwinfo.year); + dstate_setinfo("experimental.nhs.hw.month", "%u", lastpkthwinfo.month); + dstate_setinfo("experimental.nhs.hw.wday", "%u", lastpkthwinfo.wday); + dstate_setinfo("experimental.nhs.hw.hour", "%u", lastpkthwinfo.hour); + dstate_setinfo("experimental.nhs.hw.minute", "%u", lastpkthwinfo.minute); + dstate_setinfo("experimental.nhs.hw.second", "%u", lastpkthwinfo.second); + dstate_setinfo("experimental.nhs.hw.alarmyear", "%u", lastpkthwinfo.alarmyear); + dstate_setinfo("experimental.nhs.hw.alarmmonth", "%u", lastpkthwinfo.alarmmonth); + dstate_setinfo("experimental.nhs.hw.alarmwday", "%u", lastpkthwinfo.alarmwday); + dstate_setinfo("experimental.nhs.hw.alarmday", "%u", lastpkthwinfo.alarmday); + dstate_setinfo("experimental.nhs.hw.alarmhour", "%u", lastpkthwinfo.alarmhour); + dstate_setinfo("experimental.nhs.hw.alarmminute", "%u", lastpkthwinfo.alarmminute); + dstate_setinfo("experimental.nhs.hw.alarmsecond", "%u", lastpkthwinfo.alarmsecond); + dstate_setinfo("experimental.nhs.hw.end_marker", "%u", lastpkthwinfo.end_marker); + } +} + +static void interpret_pkt_data(void) { + /* TOTHINK: Consider passing in the packet struct as parameter? + * Note that certain points from lastpkthwinfo do play a role + * in decisions here (so maybe two parameters?) + */ + int got_hwinfo = (lastpkthwinfo.checksum_ok && lastpkthwinfo.size > 0); + char alarm[1024]; /* Also used as a general string buffer */ unsigned int va = 0; unsigned int ah = 0; unsigned int vin_underv = 0; @@ -1747,17 +1865,353 @@ void upsdrv_updateinfo(void) { float vin_low_crit = 0; float vin_high_warn = 0; float vin_high_crit = 0; - float calculated = 0; float vpower = 0; float pf = 0; long bcharge = 0; int min_input_power = 0; - double tempodecorrido = 0.0; unsigned int numbat = 0; unsigned int vbat = 0; float abat = 0; float actual_current = 0; - upsinfo ups; + + if (!lastpktdata.checksum_ok) { + upslogx(LOG_WARNING, "%s: bad lastpktdata.checksum", __func__); + return; + } + + /* checksum is OK, then use it to set values */ + upsdebugx(4, "%s: Data Packet seems be OK", __func__); + + if (!got_hwinfo) { + upsdebugx(2, "%s: Pkt HWINFO is not OK. " + "See if will be requested next time. " + "Some data points will not be set on this pass!", + __func__); + /* Not return, but we would miss some data points */ + } + + /* Setting UPS Status: + * OL -- On line (mains is present): Code below + * OB -- On battery (mains is not present) : Code below + * LB -- Low battery: Code below + * HB -- High battery: NHS doesn't have any variable with that information. Feel free to discover a way to set it + * RB -- The battery needs to be replaced: Well, as mentioned, we can write some infos on nobreak fw, on structures like pkt_hwinfo.year, pkt_hwinfo.month, etc. I never found any equipment with these values. + * CHRG -- The battery is charging: Code below + * DISCHRG -- The battery is discharging (inverter is providing load power): Code Below + * BYPASS -- UPS bypass circuit is active -- no battery protection is available: It's another PROBLEM, because NHS can work in bypass mode in some models, even if you have sealed batteries on it (without any external battery device). On the moment, i'll won't work with that. Feel free to discover how it work correctly. + * CAL -- UPS is currently performing runtime calibration (on battery) + * OFF -- UPS is offline and is not supplying power to the load + * OVER -- UPS is overloaded + * TRIM -- UPS is trimming incoming voltage (called "buck" in some hardware) + * BOOST -- UPS is boosting incoming voltage + * FSD -- Forced Shutdown (restricted use, see the note below) + */ + + /* Decision Chain commented below */ + + /* First we check if system is on battery or not */ + upsdebugx(4, "Set UPS status as OFF and start checking. s_battery_mode is %d", + lastpktdata.s_battery_mode); + + if (got_hwinfo) { + if (lastpkthwinfo.s_220V_in) { + upsdebugx(4, "I'm on 220v IN!. My undervoltage is %d", lastpkthwinfo.undervoltagein220V); + min_input_power = lastpkthwinfo.undervoltagein220V; + } + else { + upsdebugx(4, "I'm on 120v IN!. My undervoltage is %d", lastpkthwinfo.undervoltagein120V); + min_input_power = lastpkthwinfo.undervoltagein120V; + } + } else { + min_input_power = 96; + upsdebugx(4, "I'm on unknown input!. My undervoltage is default %d", min_input_power); + } + + if (lastpktdata.s_battery_mode) { + /* ON BATTERY */ + upsdebugx(4, "UPS is on Battery Mode"); + dstate_setinfo("ups.status", "%s", "OB"); + if (lastpktdata.s_battery_low) { + /* If battery is LOW, warn user! */ + upsdebugx(4, "UPS is on Battery Mode and in Low Battery State"); + dstate_setinfo("ups.status", "%s", "LB"); + } /* end if */ + } /* end if */ + else { + /* Check if MAINS (power) is not preset. + * Well, we can check pkt_data.s_network_failure too... */ + if ((lastpktdata.vacinrms <= min_input_power) || (lastpktdata.s_network_failure)) { + upsdebugx(4, "UPS has power-in value %0.2f " + "and min_input_power is %d, " + "or network is in failure. Network failure is %d", + lastpktdata.vacinrms, + min_input_power, + lastpktdata.s_network_failure); + dstate_setinfo("ups.status", "%s", "DISCHRG"); + } /* end if */ + else { + /* MAINS is present. We need to check some situations. + * NHS only charge if have more than min_input_power. + * If MAINS is less than or equal to min_input_power, + * then the UPS goes to BATTERY + */ + if (lastpktdata.vacinrms > min_input_power) { + upsdebugx(4, "UPS is on MAINS"); + if (lastpktdata.s_charger_on) { + upsdebugx(4, "UPS Charging..."); + dstate_setinfo("ups.status", "%s", "CHRG"); + } + else { + if ((lastpktdata.s_network_failure) || (lastpktdata.s_fast_network_failure)) { + upsdebugx(4, "UPS is on battery mode because network failure or fast network failure"); + dstate_setinfo("ups.status", "%s", "OB"); + } /* end if */ + else { + upsdebugx(4, "All is OK. UPS is on ONLINE!"); + dstate_setinfo("ups.status", "%s", "OL"); + } /* end else */ + } /* end else */ + } /* end if */ + else { + /* Energy is below limit. + * Nobreak is probably in battery mode... */ + if (lastpktdata.s_battery_low) + dstate_setinfo("ups.status", "%s", "LB"); + else { + /* ...or network failure */ + dstate_setinfo("ups.status", "%s", "OB"); + } /* end else */ + } /* end else */ + } /* end else */ + } /* end else */ + + /* TODO: Report in NUT datapoints (battery.packs etc.), + * cache in C vars to not re-getval on every loop. + * Perhaps set in upsdrv_initinfo() and refresh in + * interpret_pkt_hwinfo() if needed, but not here? + */ + vbat = get_vbat(); + ah = get_ah(); + if (va == 0 && got_hwinfo) + va = get_va(lastpkthwinfo.model); + pf = get_pf(); + numbat = get_numbat(); + if (numbat == 0 && got_hwinfo) + numbat = lastpkthwinfo.numbatteries; + else + upsdebugx(4, "Number of batteries is set to %d", numbat); + + /* Set all alarms possible */ + alarm[0] = '\0'; + if (lastpktdata.s_battery_mode) + snprintf(alarm, sizeof(alarm), "%s", "|UPS IN BATTERY MODE|"); + if (lastpktdata.s_battery_low) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|UPS IN BATTERY MODE|"); + if (lastpktdata.s_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|NETWORK FAILURE|"); + + /* FIXME: Really same criteria in these 3? */ + if (lastpktdata.s_fast_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|FAST NETWORK FAILURE|"); + if (lastpktdata.s_fast_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|220v IN|"); + if (lastpktdata.s_fast_network_failure) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|220v OUT|"); + + if (lastpktdata.s_bypass_on) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|BYPASS ON|"); + if (lastpktdata.s_charger_on) + snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", + "|CHARGER ON|"); + dstate_setinfo("ups.alarm", "%s", alarm); + + dstate_setinfo("ups.temperature", "%0.2f", lastpktdata.tempmed_real); + dstate_setinfo("ups.load", "%u", lastpktdata.potrms); + dstate_setinfo("ups.efficiency", "%0.2f", calculate_efficiency(lastpktdata.vacoutrms, lastpktdata.vacinrms)); + + /* We've got the power? */ + if (va > 0 && pf > 0) { + /* vpower is the power in Watts */ + vpower = ((va * pf) * (lastpktdata.potrms / 100.0)); + /* abat is the battery's consumption in Amperes */ + abat = ((vpower / lastpktdata.vdcmed_real) / numbat); + if (vpower > maxpower) + maxpower = vpower; + if (vpower < minpower) + minpower = vpower; + dstate_setinfo("ups.power", "%0.2f", vpower); + dstate_setinfo("ups.realpower", "%ld", lrint(round(vpower))); + + /* FIXME: Move nominal settings to upsdrv_initinfo() + * or at worst to interpret_pkt_hwinfo() */ + dstate_setinfo("ups.power.nominal", "%u", va); + dstate_setinfo("ups.realpower.nominal", "%ld", lrint(round((double)va * (double)pf))); + + dstate_setinfo("output.realpower", "%ld", lrint(round(va * (lastpktdata.potrms / 100.0)))); + dstate_setinfo("output.power", "%0.2f", vpower); + dstate_setinfo("output.power.maximum", "%0.2f", maxpower); + dstate_setinfo("output.power.minimum", "%0.2f", minpower); + dstate_setinfo("output.power.percent", "%u", lastpktdata.potrms); + + if (lastpktdata.potrms > maxpowerperc) + maxpowerperc = lastpktdata.potrms; + if (lastpktdata.potrms < minpowerperc) + minpowerperc = lastpktdata.potrms; + dstate_setinfo("output.power.maximum.percent", "%u", maxpowerperc); + dstate_setinfo("output.power.minimum.percent", "%u", minpowerperc); + } + + dstate_setinfo("output.voltage", "%0.2f", lastpktdata.vacoutrms); + dstate_setinfo("input.voltage", "%0.2f", lastpktdata.vacinrms); + dstate_setinfo("input.voltage.maximum", "%0.2f", lastpktdata.vacinrmsmin); + dstate_setinfo("input.voltage.minimum", "%0.2f", lastpktdata.vacinrmsmax); + + if (got_hwinfo) { + dstate_setinfo("ups.beeper.status", "%d", !lastpkthwinfo.c_buzzer_disable); + + vin_underv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V; + vin_overv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V; + perc = f_equal(get_vin_perc("vin_low_warn_perc"), get_vin_perc("vin_low_crit_perc")) ? 2 : 1; + vin_low_warn = vin_underv + (vin_underv * ((get_vin_perc("vin_low_warn_perc") * perc) / 100.0)); + dstate_setinfo("input.voltage.low.warning", "%0.2f", vin_low_warn); + vin_low_crit = vin_underv + (vin_underv * (get_vin_perc("vin_low_crit_perc") / 100.0)); + dstate_setinfo("input.voltage.low.critical", "%0.2f", vin_low_crit); + vin_high_warn = vin_overv + (vin_overv * ((get_vin_perc("vin_high_warn_perc") * perc) / 100.0)); + dstate_setinfo("input.voltage.high.warning", "%0.2f", vin_high_warn); + vin_high_crit = vin_overv + (vin_overv * (get_vin_perc("vin_high_crit_perc") / 100.0)); + dstate_setinfo("input.voltage.high.critical", "%0.2f", vin_high_crit); + + dstate_setinfo("input.transfer.low", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V); + dstate_setinfo("input.transfer.high", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V); + + /* FIXME: Move nominal settings to upsdrv_initinfo() + * or at worst to interpret_pkt_hwinfo() */ + vin = lastpkthwinfo.s_220V_in ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; + dstate_setinfo("input.voltage.nominal", "%u", vin); + vout = lastpkthwinfo.s_220V_out ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; + dstate_setinfo("output.voltage.nominal", "%u", vout); + } + + /* Battery electric info */ + bcharge = lrint(round((lastpktdata.vdcmed_real * 100) / vbat)); + if (bcharge > 100) + bcharge = 100; + dstate_setinfo("battery.charge", "%ld", bcharge); + dstate_setinfo("battery.voltage", "%0.2f", lastpktdata.vdcmed_real); + dstate_setinfo("battery.current", "%0.2f", abat); + dstate_setinfo("battery.current.total", "%0.2f", (float)abat * numbat); + dstate_setinfo("battery.temperature", "%ld", lrint(round(lastpktdata.tempmed_real))); + + /* FIXME: Move nominal and other static settings to upsdrv_initinfo() + * or at worst to interpret_pkt_hwinfo() */ + dstate_setinfo("battery.packs", "%u", numbat); + dstate_setinfo("battery.voltage.nominal", "%u", vbat); + dstate_setinfo("battery.capacity", "%u", ah); + dstate_setinfo("battery.capacity.nominal", "%0.2f", (float)ah * pf); + dstate_setinfo("battery.runtime.low", "%u", 30); + + if (vpower > 0) { + /* We will calculate autonomy in seconds + * autonomy_secs = (ah / lastpktdata.vdcmed_real) * 3600; + * Maybe wrong, too. + * People say that the correct calculation is + * + * Battery Amp-Hour / (Power in Watts / battery voltage) + * + * Is that correct? I don't know. I'll use it for now. + */ + + /* That result is IN HOURS. We need to convert it to seconds */ + actual_current = vpower / vbat; /* Current consumption in A*/ + autonomy_secs = (ah / actual_current) * 3600; + + dstate_setinfo("battery.runtime", "%u", autonomy_secs); + } + + /* Battery charger status */ + if (lastpktdata.s_charger_on) { + dstate_setinfo("battery.charger.status", "%s", "CHARGING"); + } else { + if (lastpktdata.s_battery_mode) + dstate_setinfo("battery.charger.status", "%s", "DISCHARGING"); + else + dstate_setinfo("battery.charger.status", "%s", "RESTING"); + } + + if (debug_pkt_data) { + unsigned int i = 0; + + /* Now, creating a structure called NHS.DATA, for latest + * data packet contents and raw data points, including those + * that were sorted above into NUT standard variables - + * for debug. + */ + dstate_setinfo("experimental.nhs.data.header", "%u", lastpktdata.header); + dstate_setinfo("experimental.nhs.data.length", "%u", lastpktdata.length); + dstate_setinfo("experimental.nhs.data.packet_type", "%c", lastpktdata.packet_type); + dstate_setinfo("experimental.nhs.data.vacinrms_high", "%u", lastpktdata.vacinrms_high); + dstate_setinfo("experimental.nhs.data.vacinrms_low", "%u", lastpktdata.vacinrms_low); + dstate_setinfo("experimental.nhs.data.vacinrms", "%0.2f", lastpktdata.vacinrms); + dstate_setinfo("experimental.nhs.data.vdcmed_high", "%u", lastpktdata.vdcmed_high); + dstate_setinfo("experimental.nhs.data.vdcmed_low", "%u", lastpktdata.vdcmed_low); + dstate_setinfo("experimental.nhs.data.vdcmed", "%0.2f", lastpktdata.vdcmed); + dstate_setinfo("experimental.nhs.data.vdcmed_real", "%0.2f", lastpktdata.vdcmed_real); + dstate_setinfo("experimental.nhs.data.potrms", "%u", lastpktdata.potrms); + dstate_setinfo("experimental.nhs.data.vacinrmsmin_high", "%u", lastpktdata.vacinrmsmin_high); + dstate_setinfo("experimental.nhs.data.vacinrmsmin_low", "%u", lastpktdata.vacinrmsmin_low); + dstate_setinfo("experimental.nhs.data.vacinrmsmin", "%0.2f", lastpktdata.vacinrmsmin); + dstate_setinfo("experimental.nhs.data.vacinrmsmax_high", "%u", lastpktdata.vacinrmsmax_high); + dstate_setinfo("experimental.nhs.data.vacinrmsmax_low", "%u", lastpktdata.vacinrmsmax_low); + dstate_setinfo("experimental.nhs.data.vacinrmsmax", "%0.2f", lastpktdata.vacinrmsmax); + dstate_setinfo("experimental.nhs.data.vacoutrms_high", "%u", lastpktdata.vacoutrms_high); + dstate_setinfo("experimental.nhs.data.vacoutrms_low", "%u", lastpktdata.vacoutrms_low); + dstate_setinfo("experimental.nhs.data.vacoutrms", "%0.2f", lastpktdata.vacoutrms); + dstate_setinfo("experimental.nhs.data.tempmed_high", "%u", lastpktdata.tempmed_high); + dstate_setinfo("experimental.nhs.data.tempmed_low", "%u", lastpktdata.tempmed_low); + dstate_setinfo("experimental.nhs.data.tempmed", "%0.2f", lastpktdata.tempmed); + dstate_setinfo("experimental.nhs.data.tempmed_real", "%0.2f", lastpktdata.tempmed_real); + dstate_setinfo("experimental.nhs.data.icarregrms", "%u", lastpktdata.icarregrms); + dstate_setinfo("experimental.nhs.data.icarregrms_real", "%u", lastpktdata.icarregrms_real); + dstate_setinfo("experimental.nhs.data.battery_tension", "%0.2f", lastpktdata.battery_tension); + dstate_setinfo("experimental.nhs.data.perc_output", "%u", lastpktdata.perc_output); + dstate_setinfo("experimental.nhs.data.statusval", "%u", lastpktdata.statusval); + for (i = 0; i < 8; i++) { + /* Reusing variable */ + snprintf(alarm, sizeof(alarm), "experimental.nhs.data.status_p%d", i); + dstate_setinfo(alarm, "%u", lastpktdata.status[i]); + } + dstate_setinfo("experimental.nhs.data.nominaltension", "%u", lastpktdata.nominaltension); + dstate_setinfo("experimental.nhs.data.timeremain", "%0.2f", lastpktdata.timeremain); + dstate_setinfo("experimental.nhs.data.s_battery_mode", "%s", lastpktdata.s_battery_mode ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_battery_low", "%s", lastpktdata.s_battery_low ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_network_failure", "%s", lastpktdata.s_network_failure ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_fast_network_failure", "%s", lastpktdata.s_fast_network_failure ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_220_in", "%s", lastpktdata.s_220_in ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_220_out", "%s", lastpktdata.s_220_out ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_bypass_on", "%s", lastpktdata.s_bypass_on ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.s_charger_on", "%s", lastpktdata.s_charger_on ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.checksum", "%u", lastpktdata.checksum); + dstate_setinfo("experimental.nhs.data.checksum_ok", "%s", lastpktdata.checksum_ok ? "true" : "false"); + dstate_setinfo("experimental.nhs.data.checksum_calc", "%u", lastpktdata.checksum_calc); + dstate_setinfo("experimental.nhs.data.end_marker", "%u", lastpktdata.end_marker); + dstate_setinfo("experimental.nhs.param.va", "%u", va); + dstate_setinfo("experimental.nhs.param.pf", "%0.2f", pf); + dstate_setinfo("experimental.nhs.param.ah", "%u", ah); + dstate_setinfo("experimental.nhs.param.vin_low_warn_perc", "%0.2f", get_vin_perc("vin_low_warn_perc")); + dstate_setinfo("experimental.nhs.param.vin_low_crit_perc", "%0.2f", get_vin_perc("vin_low_crit_perc")); + dstate_setinfo("experimental.nhs.param.vin_high_warn_perc", "%0.2f", get_vin_perc("vin_high_warn_perc")); + dstate_setinfo("experimental.nhs.param.vin_high_crit_perc", "%0.2f", get_vin_perc("vin_high_crit_perc")); + } +} + +void upsdrv_updateinfo(void) { + double tempodecorrido = 0.0; time_t now; upsdebugx(3, "%s: starting...", __func__); @@ -1806,7 +2260,7 @@ void upsdrv_updateinfo(void) { /* Interpret the just-finished packet buffer */ now = time(NULL); - upsdebugx(4, "DATAPACKET INDEX IS %d", datapacket_index); + upsdebugx(4, "DATAPACKET INDEX IS %" PRIuSIZE, datapacket_index); if (lastdp != 0) { tempodecorrido = difftime(now, lastdp); } @@ -1821,9 +2275,19 @@ void upsdrv_updateinfo(void) { if (((datapacket_index == 18) || (datapacket_index == 50)) && (!lastpkthwinfo.checksum_ok)) { /* Re-read HW info only if the old one is broken */ lastpkthwinfo = mount_hwinfo(datapacket, datapacket_index); + if (lastpkthwinfo.checksum_ok) { + interpret_pkt_hwinfo(); + /* Refresh the healthy timer */ + dstate_dataok(); + } } /* end if */ else if (datapacket_index == 21) { lastpktdata = mount_datapacket(datapacket, datapacket_index, tempodecorrido, lastpkthwinfo); + if (lastpktdata.checksum_ok) { + interpret_pkt_data(); + /* Refresh the healthy timer */ + dstate_dataok(); + } } /* end else-if */ else { upslogx(LOG_WARNING, "Incoming packet size not recognized, discarding!"); @@ -1834,385 +2298,12 @@ void upsdrv_updateinfo(void) { memset(datapacket, 0, sizeof(datapacket)); datapacketstart = false; - if (lastpktdata.checksum_ok) { - /* checksum is OK, then use it to set values */ - upsdebugx(4, "Data Packet seems be OK"); - if (lastpkthwinfo.size == 0) - upsdebugx(2, "Pkt HWINFO is not OK. See if will be requested next time!"); - else { - if (lastpkthwinfo.checksum_ok) { - upsdebugx(4, "Pkt HWINFO is OK. Model is %d, hwversion is %d and swversion is %d", lastpkthwinfo.model, lastpkthwinfo.hardwareversion, lastpkthwinfo.softwareversion); - /* We need to set data on NUT with data - * that I believe that I can calculate. - * Now setting data on NUT - */ - ups = getupsinfo(lastpkthwinfo.model); - upsdebugx(4, "UPS Struct data: Code %d Model %s VA %d", ups.upscode, ups.upsdesc, ups.VA); - dstate_setinfo("device.model", "%s", ups.upsdesc); - dstate_setinfo("device.mfr", "%s", MANUFACTURER); - dstate_setinfo("device.serial", "%s", lastpkthwinfo.serial); - dstate_setinfo("device.type", "%s", "ups"); - - /* Setting UPS Status: - * OL -- On line (mains is present): Code below - * OB -- On battery (mains is not present) : Code below - * LB -- Low battery: Code below - * HB -- High battery: NHS doesn't have any variable with that information. Feel free to discover a way to set it - * RB -- The battery needs to be replaced: Well, as mentioned, we can write some infos on nobreak fw, on structures like pkt_hwinfo.year, pkt_hwinfo.month, etc. I never found any equipment with these values. - * CHRG -- The battery is charging: Code below - * DISCHRG -- The battery is discharging (inverter is providing load power): Code Below - * BYPASS -- UPS bypass circuit is active -- no battery protection is available: It's another PROBLEM, because NHS can work in bypass mode in some models, even if you have sealed batteries on it (without any external battery device). On the moment, i'll won't work with that. Feel free to discover how it work correctly. - * CAL -- UPS is currently performing runtime calibration (on battery) - * OFF -- UPS is offline and is not supplying power to the load - * OVER -- UPS is overloaded - * TRIM -- UPS is trimming incoming voltage (called "buck" in some hardware) - * BOOST -- UPS is boosting incoming voltage - * FSD -- Forced Shutdown (restricted use, see the note below) - */ - - /* Decision Chain commented below */ - - /* First we check if system is on battery or not */ - upsdebugx(4, "Set UPS status as OFF and start checking. s_battery_mode is %d", lastpktdata.s_battery_mode); - if (lastpkthwinfo.s_220V_in) { - upsdebugx(4, "I'm on 220v IN!. My overvoltage is %d", lastpkthwinfo.undervoltagein220V); - min_input_power = lastpkthwinfo.undervoltagein220V; - } - else { - upsdebugx(4, "I'm on 120v IN!. My overvoltage is %d", lastpkthwinfo.undervoltagein120V); - min_input_power = lastpkthwinfo.undervoltagein120V; - } - if (lastpktdata.s_battery_mode) { - /* ON BATTERY */ - upsdebugx(4, "UPS is on Battery Mode"); - dstate_setinfo("ups.status", "%s", "OB"); - if (lastpktdata.s_battery_low) { - /* If battery is LOW, warn user! */ - upsdebugx(4, "UPS is on Battery Mode and in Low Battery State"); - dstate_setinfo("ups.status", "%s", "LB"); - } /* end if */ - } /* end if */ - else { - /* Check if MAINS (power) is not preset. - * Well, we can check pkt_data.s_network_failure too... */ - if ((lastpktdata.vacinrms <= min_input_power) || (lastpktdata.s_network_failure)) { - upsdebugx(4, "UPS has power-in value %0.2f " - "and min_input_power is %d, " - "or network is in failure. Network failure is %d", - lastpktdata.vacinrms, - min_input_power, - lastpktdata.s_network_failure); - dstate_setinfo("ups.status", "%s", "DISCHRG"); - } /* end if */ - else { - /* MAINS is present. We need to check some situations. - * NHS only charge if have more than min_input_power. - * If MAINS is less than or equal to min_input_power, - * then the UPS goes to BATTERY - */ - if (lastpktdata.vacinrms > min_input_power) { - upsdebugx(4, "UPS is on MAINS"); - if (lastpktdata.s_charger_on) { - upsdebugx(4, "UPS Charging..."); - dstate_setinfo("ups.status", "%s", "CHRG"); - } - else { - if ((lastpktdata.s_network_failure) || (lastpktdata.s_fast_network_failure)) { - upsdebugx(4, "UPS is on battery mode because network failure or fast network failure"); - dstate_setinfo("ups.status", "%s", "OB"); - } /* end if */ - else { - upsdebugx(4, "All is OK. UPS is on ONLINE!"); - dstate_setinfo("ups.status", "%s", "OL"); - } /* end else */ - } /* end else */ - } /* end if */ - else { - /* Energy is below limit. - * Nobreak is probably in battery mode... */ - if (lastpktdata.s_battery_low) - dstate_setinfo("ups.status", "%s", "LB"); - else { - /* ...or network failure */ - dstate_setinfo("ups.status", "%s", "OB"); - } /* end else */ - } /* end else */ - } /* end else */ - } /* end else */ - - numbat = get_numbat(); - if (numbat == 0) - numbat = lastpkthwinfo.numbatteries; - else - upsdebugx(4, "Number of batteries is set to %d", numbat); - vbat = get_vbat(); - ah = get_ah(); - - /* Set all alarms possible */ - alarm[0] = '\0'; - if (lastpktdata.s_battery_mode) - snprintf(alarm, sizeof(alarm), "%s", "|UPS IN BATTERY MODE|"); - if (lastpktdata.s_battery_low) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|UPS IN BATTERY MODE|"); - if (lastpktdata.s_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|NETWORK FAILURE|"); - - /* FIXME: Really same criteria in these 3? */ - if (lastpktdata.s_fast_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|FAST NETWORK FAILURE|"); - if (lastpktdata.s_fast_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|220v IN|"); - if (lastpktdata.s_fast_network_failure) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|220v OUT|"); - - if (lastpktdata.s_bypass_on) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|BYPASS ON|"); - if (lastpktdata.s_charger_on) - snprintfcat(alarm, sizeof(alarm), "%s%s", *alarm ? " " : "", - "|CHARGER ON|"); - dstate_setinfo("ups.alarm", "%s", alarm); - dstate_setinfo("ups.model", "%s", ups.upsdesc); - dstate_setinfo("ups.mfr", "%s", MANUFACTURER); - dstate_setinfo("ups.serial", "%s", lastpkthwinfo.serial); - dstate_setinfo("ups.firmware", "%u", lastpkthwinfo.softwareversion); - /* Setting hardware version here. - * Did not find another place to do this. - * Feel free to correct it. - * FIXME: move to upsdrv_initinfo() or so - */ - dstate_setinfo("ups.firmware.aux", "%u", lastpkthwinfo.hardwareversion); - dstate_setinfo("ups.temperature", "%0.2f", lastpktdata.tempmed_real); - dstate_setinfo("ups.load", "%u", lastpktdata.potrms); - dstate_setinfo("ups.efficiency", "%0.2f", calculate_efficiency(lastpktdata.vacoutrms, lastpktdata.vacinrms)); - va = get_va(lastpkthwinfo.model); - pf = get_pf(); - /* vpower is the power in Watts */ - vpower = ((va * pf) * (lastpktdata.potrms / 100.0)); - /* abat is the battery's consumption in Amperes */ - abat = ((vpower / lastpktdata.vdcmed_real) / numbat); - if (vpower > maxpower) - maxpower = vpower; - if (vpower < minpower) - minpower = vpower; - dstate_setinfo("ups.power", "%0.2f", vpower); - dstate_setinfo("ups.power.nominal", "%u", va); - dstate_setinfo("ups.realpower", "%ld", lrint(round(vpower))); - dstate_setinfo("ups.realpower.nominal", "%ld", lrint(round((double)va * (double)pf))); - dstate_setinfo("ups.beeper.status", "%d", !lastpkthwinfo.c_buzzer_disable); - dstate_setinfo("input.voltage", "%0.2f", lastpktdata.vacinrms); - dstate_setinfo("input.voltage.maximum", "%0.2f", lastpktdata.vacinrmsmin); - dstate_setinfo("input.voltage.minimum", "%0.2f", lastpktdata.vacinrmsmax); - vin_underv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V; - vin_overv = lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V; - perc = f_equal(get_vin_perc("vin_low_warn_perc"), get_vin_perc("vin_low_crit_perc")) ? 2 : 1; - vin_low_warn = vin_underv + (vin_underv * ((get_vin_perc("vin_low_warn_perc") * perc) / 100.0)); - dstate_setinfo("input.voltage.low.warning", "%0.2f", calculated); - vin_low_crit = vin_underv + (vin_underv * (get_vin_perc("vin_low_crit_perc") / 100.0)); - dstate_setinfo("input.voltage.low.critical", "%0.2f", calculated); - vin_high_warn = vin_overv + (vin_overv * ((get_vin_perc("vin_high_warn_perc") * perc) / 100.0)); - dstate_setinfo("input.voltage.high.warning", "%0.2f", calculated); - vin_high_crit = vin_overv + (vin_overv * (get_vin_perc("vin_high_crit_perc") / 100.0)); - dstate_setinfo("input.voltage.high.critical", "%0.2f", calculated); - vin = lastpkthwinfo.s_220V_in ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; - dstate_setinfo("input.voltage.nominal", "%u", vin); - dstate_setinfo("input.transfer.low", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.undervoltagein220V : lastpkthwinfo.undervoltagein120V); - dstate_setinfo("input.transfer.high", "%u", lastpkthwinfo.s_220V_in ? lastpkthwinfo.overvoltagein220V : lastpkthwinfo.overvoltagein120V); - dstate_setinfo("output.voltage", "%0.2f", lastpktdata.vacoutrms); - vout = lastpkthwinfo.s_220V_out ? lastpkthwinfo.tensionout220V : lastpkthwinfo.tensionout120V; - dstate_setinfo("output.voltage.nominal", "%u", vout); - dstate_setinfo("voltage", "%0.2f", lastpktdata.vacoutrms); - dstate_setinfo("voltage.nominal", "%u", vout); - dstate_setinfo("voltage.maximum", "%0.2f", lastpktdata.vacinrmsmax); - dstate_setinfo("voltage.minimum", "%0.2f", lastpktdata.vacinrmsmin); - dstate_setinfo("voltage.low.warning", "%0.2f", vin_low_warn); - dstate_setinfo("voltage.low.critical", "%0.2f", vin_low_crit); - dstate_setinfo("voltage.high.warning", "%0.2f", vin_high_warn); - dstate_setinfo("voltage.high.critical", "%0.2f", vin_high_crit); - dstate_setinfo("power", "%0.2f", vpower); - dstate_setinfo("power.maximum", "%0.2f", maxpower); - dstate_setinfo("power.minimum", "%0.2f", minpower); - dstate_setinfo("power.percent", "%u", lastpktdata.potrms); - if (lastpktdata.potrms > maxpowerperc) - maxpowerperc = lastpktdata.potrms; - if (lastpktdata.potrms < minpowerperc) - minpowerperc = lastpktdata.potrms; - dstate_setinfo("power.maximum.percent", "%u", maxpowerperc); - dstate_setinfo("power.minimum.percent", "%u", minpowerperc); - dstate_setinfo("realpower", "%ld", lrint(round(vpower))); - dstate_setinfo("power", "%ld", lrint(round(va * (lastpktdata.potrms / 100.0)))); - bcharge = lrint(round((lastpktdata.vdcmed_real * 100) / vbat)); - if (bcharge > 100) - bcharge = 100; - dstate_setinfo("battery.charge", "%ld", bcharge); - dstate_setinfo("battery.voltage", "%0.2f", lastpktdata.vdcmed_real); - dstate_setinfo("battery.voltage.nominal", "%u", vbat); - dstate_setinfo("battery.capacity", "%u", ah); - dstate_setinfo("battery.capacity.nominal", "%0.2f", (float)ah * pf); - dstate_setinfo("battery.current", "%0.2f", abat); - dstate_setinfo("battery.current.total", "%0.2f", (float)abat * numbat); - dstate_setinfo("battery.temperature", "%ld", lrint(round(lastpktdata.tempmed_real))); - dstate_setinfo("battery.packs", "%u", numbat); - /* We will calculate autonomy in seconds - * autonomy_secs = (ah / lastpktdata.vdcmed_real) * 3600; - * Maybe wrong, too. - * People say that the correct calculation is - * - * Battery Amp-Hour / (Power in Watts / battery voltage) - * - * Is that correct? I don't know. I'll use it for now. - */ - - /* That result is IN HOURS. We need to convert it to seconds */ - actual_current = vpower / vbat; /* Current consumption in A*/ - autonomy_secs = (ah / actual_current) * 3600; - - dstate_setinfo("battery.runtime", "%u", autonomy_secs); - dstate_setinfo("battery.runtime.low", "%u", 30); - if (lastpktdata.s_charger_on) { - dstate_setinfo("battery.charger.status", "%s", "CHARGING"); - } else { - if (lastpktdata.s_battery_mode) - dstate_setinfo("battery.charger.status", "%s", "DISCHARGING"); - else - dstate_setinfo("battery.charger.status", "%s", "RESTING"); - } - /* Now, creating a structure called NHS, */ - dstate_setinfo("experimental.nhs.hw.header", "%u", lastpkthwinfo.header); - dstate_setinfo("experimental.nhs.hw.size", "%u", lastpkthwinfo.size); - dstate_setinfo("experimental.nhs.hw.type", "%c", lastpkthwinfo.type); - dstate_setinfo("experimental.nhs.hw.model", "%u", lastpkthwinfo.model); - dstate_setinfo("experimental.nhs.hw.hardwareversion", "%u", lastpkthwinfo.hardwareversion); - dstate_setinfo("experimental.nhs.hw.softwareversion", "%u", lastpkthwinfo.softwareversion); - dstate_setinfo("experimental.nhs.hw.configuration", "%u", lastpkthwinfo.configuration); - for (i = 0; i < 5; i++) { - /* Reusing variable */ - snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.configuration_array_p%d", i); - dstate_setinfo(alarm, "%u", lastpkthwinfo.configuration_array[i]); - } - dstate_setinfo("experimental.nhs.hw.c_oem_mode", "%s", lastpkthwinfo.c_oem_mode ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_buzzer_disable", "%s", lastpkthwinfo.c_buzzer_disable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_potmin_disable", "%s", lastpkthwinfo.c_potmin_disable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_rearm_enable", "%s", lastpkthwinfo.c_rearm_enable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.c_bootloader_enable", "%s", lastpkthwinfo.c_bootloader_enable ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.numbatteries", "%u", lastpkthwinfo.numbatteries); - dstate_setinfo("experimental.nhs.hw.undervoltagein120V", "%u", lastpkthwinfo.undervoltagein120V); - dstate_setinfo("experimental.nhs.hw.overvoltagein120V", "%u", lastpkthwinfo.overvoltagein120V); - dstate_setinfo("experimental.nhs.hw.undervoltagein220V", "%u", lastpkthwinfo.undervoltagein220V); - dstate_setinfo("experimental.nhs.hw.overvoltagein220V", "%u", lastpkthwinfo.overvoltagein220V); - dstate_setinfo("experimental.nhs.hw.tensionout120V", "%u", lastpkthwinfo.tensionout120V); - dstate_setinfo("experimental.nhs.hw.tensionout220V", "%u", lastpkthwinfo.tensionout220V); - dstate_setinfo("experimental.nhs.hw.statusval", "%u", lastpkthwinfo.statusval); - for (i = 0; i < 6; i++) { - /* Reusing variable */ - snprintf(alarm, sizeof(alarm), "experimental.nhs.hw.status_p%d", i); - dstate_setinfo(alarm, "%u", lastpkthwinfo.status[i]); - } - dstate_setinfo("experimental.nhs.hw.s_220V_in", "%s", lastpkthwinfo.s_220V_in ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_220V_out", "%s", lastpkthwinfo.s_220V_out ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_sealed_battery", "%s", lastpkthwinfo.s_sealed_battery ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_show_out_tension", "%s", lastpkthwinfo.s_show_out_tension ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_show_temperature", "%s", lastpkthwinfo.s_show_temperature ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.s_show_charger_current", "%s", lastpkthwinfo.s_show_charger_current ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.chargercurrent", "%u", lastpkthwinfo.chargercurrent); - dstate_setinfo("experimental.nhs.hw.checksum", "%u", lastpkthwinfo.checksum); - dstate_setinfo("experimental.nhs.hw.checksum_calc", "%u", lastpkthwinfo.checksum_calc); - dstate_setinfo("experimental.nhs.hw.checksum_ok", "%s", lastpkthwinfo.checksum_ok ? "true" : "false"); - dstate_setinfo("experimental.nhs.hw.serial", "%s", lastpkthwinfo.serial); - dstate_setinfo("experimental.nhs.hw.year", "%u", lastpkthwinfo.year); - dstate_setinfo("experimental.nhs.hw.month", "%u", lastpkthwinfo.month); - dstate_setinfo("experimental.nhs.hw.wday", "%u", lastpkthwinfo.wday); - dstate_setinfo("experimental.nhs.hw.hour", "%u", lastpkthwinfo.hour); - dstate_setinfo("experimental.nhs.hw.minute", "%u", lastpkthwinfo.minute); - dstate_setinfo("experimental.nhs.hw.second", "%u", lastpkthwinfo.second); - dstate_setinfo("experimental.nhs.hw.alarmyear", "%u", lastpkthwinfo.alarmyear); - dstate_setinfo("experimental.nhs.hw.alarmmonth", "%u", lastpkthwinfo.alarmmonth); - dstate_setinfo("experimental.nhs.hw.alarmwday", "%u", lastpkthwinfo.alarmwday); - dstate_setinfo("experimental.nhs.hw.alarmday", "%u", lastpkthwinfo.alarmday); - dstate_setinfo("experimental.nhs.hw.alarmhour", "%u", lastpkthwinfo.alarmhour); - dstate_setinfo("experimental.nhs.hw.alarmminute", "%u", lastpkthwinfo.alarmminute); - dstate_setinfo("experimental.nhs.hw.alarmsecond", "%u", lastpkthwinfo.alarmsecond); - dstate_setinfo("experimental.nhs.hw.end_marker", "%u", lastpkthwinfo.end_marker); - - /* Data packet */ - dstate_setinfo("experimental.nhs.data.header", "%u", lastpktdata.header); - dstate_setinfo("experimental.nhs.data.length", "%u", lastpktdata.length); - dstate_setinfo("experimental.nhs.data.packet_type", "%c", lastpktdata.packet_type); - dstate_setinfo("experimental.nhs.data.vacinrms_high", "%u", lastpktdata.vacinrms_high); - dstate_setinfo("experimental.nhs.data.vacinrms_low", "%u", lastpktdata.vacinrms_low); - dstate_setinfo("experimental.nhs.data.vacinrms", "%0.2f", lastpktdata.vacinrms); - dstate_setinfo("experimental.nhs.data.vdcmed_high", "%u", lastpktdata.vdcmed_high); - dstate_setinfo("experimental.nhs.data.vdcmed_low", "%u", lastpktdata.vdcmed_low); - dstate_setinfo("experimental.nhs.data.vdcmed", "%0.2f", lastpktdata.vdcmed); - dstate_setinfo("experimental.nhs.data.vdcmed_real", "%0.2f", lastpktdata.vdcmed_real); - dstate_setinfo("experimental.nhs.data.potrms", "%u", lastpktdata.potrms); - dstate_setinfo("experimental.nhs.data.vacinrmsmin_high", "%u", lastpktdata.vacinrmsmin_high); - dstate_setinfo("experimental.nhs.data.vacinrmsmin_low", "%u", lastpktdata.vacinrmsmin_low); - dstate_setinfo("experimental.nhs.data.vacinrmsmin", "%0.2f", lastpktdata.vacinrmsmin); - dstate_setinfo("experimental.nhs.data.vacinrmsmax_high", "%u", lastpktdata.vacinrmsmax_high); - dstate_setinfo("experimental.nhs.data.vacinrmsmax_low", "%u", lastpktdata.vacinrmsmax_low); - dstate_setinfo("experimental.nhs.data.vacinrmsmax", "%0.2f", lastpktdata.vacinrmsmax); - dstate_setinfo("experimental.nhs.data.vacoutrms_high", "%u", lastpktdata.vacoutrms_high); - dstate_setinfo("experimental.nhs.data.vacoutrms_low", "%u", lastpktdata.vacoutrms_low); - dstate_setinfo("experimental.nhs.data.vacoutrms", "%0.2f", lastpktdata.vacoutrms); - dstate_setinfo("experimental.nhs.data.tempmed_high", "%u", lastpktdata.tempmed_high); - dstate_setinfo("experimental.nhs.data.tempmed_low", "%u", lastpktdata.tempmed_low); - dstate_setinfo("experimental.nhs.data.tempmed", "%0.2f", lastpktdata.tempmed); - dstate_setinfo("experimental.nhs.data.tempmed_real", "%0.2f", lastpktdata.tempmed_real); - dstate_setinfo("experimental.nhs.data.icarregrms", "%u", lastpktdata.icarregrms); - dstate_setinfo("experimental.nhs.data.icarregrms_real", "%u", lastpktdata.icarregrms_real); - dstate_setinfo("experimental.nhs.data.battery_tension", "%0.2f", lastpktdata.battery_tension); - dstate_setinfo("experimental.nhs.data.perc_output", "%u", lastpktdata.perc_output); - dstate_setinfo("experimental.nhs.data.statusval", "%u", lastpktdata.statusval); - for (i = 0; i < 8; i++) { - /* Reusing variable */ - snprintf(alarm, sizeof(alarm), "experimental.nhs.data.status_p%d", i); - dstate_setinfo(alarm, "%u", lastpktdata.status[i]); - } - dstate_setinfo("experimental.nhs.data.nominaltension", "%u", lastpktdata.nominaltension); - dstate_setinfo("experimental.nhs.data.timeremain", "%0.2f", lastpktdata.timeremain); - dstate_setinfo("experimental.nhs.data.s_battery_mode", "%s", lastpktdata.s_battery_mode ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_battery_low", "%s", lastpktdata.s_battery_low ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_network_failure", "%s", lastpktdata.s_network_failure ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_fast_network_failure", "%s", lastpktdata.s_fast_network_failure ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_220_in", "%s", lastpktdata.s_220_in ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_220_out", "%s", lastpktdata.s_220_out ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_bypass_on", "%s", lastpktdata.s_bypass_on ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.s_charger_on", "%s", lastpktdata.s_charger_on ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.checksum", "%u", lastpktdata.checksum); - dstate_setinfo("experimental.nhs.data.checksum_ok", "%s", lastpktdata.checksum_ok ? "true" : "false"); - dstate_setinfo("experimental.nhs.data.checksum_calc", "%u", lastpktdata.checksum_calc); - dstate_setinfo("experimental.nhs.data.end_marker", "%u", lastpktdata.end_marker); - dstate_setinfo("experimental.nhs.param.va", "%u", va); - dstate_setinfo("experimental.nhs.param.pf", "%0.2f", pf); - dstate_setinfo("experimental.nhs.param.ah", "%u", ah); - dstate_setinfo("experimental.nhs.param.vin_low_warn_perc", "%0.2f", get_vin_perc("vin_low_warn_perc")); - dstate_setinfo("experimental.nhs.param.vin_low_crit_perc", "%0.2f", get_vin_perc("vin_low_crit_perc")); - dstate_setinfo("experimental.nhs.param.vin_high_warn_perc", "%0.2f", get_vin_perc("vin_high_warn_perc")); - dstate_setinfo("experimental.nhs.param.vin_high_crit_perc", "%0.2f", get_vin_perc("vin_high_crit_perc")); - - dstate_dataok(); - } /* end if */ - else { - upsdebugx(4, "Checksum of pkt_hwinfo is corrupted or not initialized. Waiting for new request..."); - } - } /* end else */ - } /* end if lastpktdata.checksum_ok */ - /* Now the nobreak read buffer is empty. * We need a hw info packet to discover several variables, * like number of batteries, to calculate some data * FIXME: move (semi)static info discovery to upsdrv_initinfo() or so */ - if (lastpkthwinfo.checksum_ok) { - /* Refresh the healthy timer */ - dstate_dataok(); - } else { + if (!lastpkthwinfo.checksum_ok) { upsdebugx(4, "pkt_hwinfo loss -- Requesting"); /* If size == 0, packet maybe not initizated, * then send an initialization packet to obtain data. From 71029c32ca0b7a89b3c12b2d888cfe536de383f2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 9 Dec 2024 03:13:30 +0100 Subject: [PATCH 139/144] docs/nut-names.txt: stress that `DOMAIN.CONTEXT.SPEC` suffixes are not complete names Signed-off-by: Jim Klimov --- docs/nut-names.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/nut-names.txt b/docs/nut-names.txt index c37dde8f34..bca0c9295c 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -443,7 +443,8 @@ Valid SPECs NOTE: For cursory readers -- the following couple of tables lists just the short `SPEC` component of the larger `DOMAIN.CONTEXT.SPEC` naming scheme -for phase-aware values, as discussed in other sections of this chapter. +for phase-aware values, as discussed in other sections of this chapter just +above. These are NOT to be used verbatim as complete data-point names! Valid with/without context (i.e. per phase or aggregated/averaged) From 8e364080cedc22d9f806ed67b5122491812c1060 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 9 Dec 2024 03:35:05 +0100 Subject: [PATCH 140/144] drivers/nhs_ser.c: interpret_pkt_data(): cache ah, pf, va, numbat, vbat and min_input_power in static vars [#2692] Signed-off-by: Jim Klimov --- drivers/nhs_ser.c | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/drivers/nhs_ser.c b/drivers/nhs_ser.c index 8faeba6467..5318a1606c 100644 --- a/drivers/nhs_ser.c +++ b/drivers/nhs_ser.c @@ -1851,10 +1851,15 @@ static void interpret_pkt_data(void) { * Note that certain points from lastpkthwinfo do play a role * in decisions here (so maybe two parameters?) */ + static unsigned int va = 0; + static unsigned int ah = 0; + static unsigned int numbat = 0; + static unsigned int vbat = 0; + static int min_input_power = 0; + static float pf = 0; + int got_hwinfo = (lastpkthwinfo.checksum_ok && lastpkthwinfo.size > 0); char alarm[1024]; /* Also used as a general string buffer */ - unsigned int va = 0; - unsigned int ah = 0; unsigned int vin_underv = 0; unsigned int vin_overv = 0; unsigned int perc = 0; @@ -1866,11 +1871,7 @@ static void interpret_pkt_data(void) { float vin_high_warn = 0; float vin_high_crit = 0; float vpower = 0; - float pf = 0; long bcharge = 0; - int min_input_power = 0; - unsigned int numbat = 0; - unsigned int vbat = 0; float abat = 0; float actual_current = 0; @@ -1923,8 +1924,10 @@ static void interpret_pkt_data(void) { min_input_power = lastpkthwinfo.undervoltagein120V; } } else { - min_input_power = 96; - upsdebugx(4, "I'm on unknown input!. My undervoltage is default %d", min_input_power); + if (!min_input_power) { + min_input_power = 96; + upsdebugx(4, "I'm on unknown input!. My undervoltage is default %d", min_input_power); + } } if (lastpktdata.s_battery_mode) { @@ -1986,20 +1989,26 @@ static void interpret_pkt_data(void) { } /* end else */ /* TODO: Report in NUT datapoints (battery.packs etc.), - * cache in C vars to not re-getval on every loop. * Perhaps set in upsdrv_initinfo() and refresh in * interpret_pkt_hwinfo() if needed, but not here? + * NOTE: values are cached in static C vars so as to + * not re-getval on every loop. */ - vbat = get_vbat(); - ah = get_ah(); + if (!vbat) + vbat = get_vbat(); + if (!ah) + ah = get_ah(); if (va == 0 && got_hwinfo) va = get_va(lastpkthwinfo.model); - pf = get_pf(); - numbat = get_numbat(); - if (numbat == 0 && got_hwinfo) - numbat = lastpkthwinfo.numbatteries; - else - upsdebugx(4, "Number of batteries is set to %d", numbat); + if (!pf) + pf = get_pf(); + if (!numbat) { + numbat = get_numbat(); + if (numbat == 0 && got_hwinfo) + numbat = lastpkthwinfo.numbatteries; + else + upsdebugx(4, "Number of batteries is set to %d", numbat); + } /* Set all alarms possible */ alarm[0] = '\0'; From fb4b88ca473ff02b4e2770140402aba78a618937 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 9 Dec 2024 03:43:10 +0100 Subject: [PATCH 141/144] drivers/nhs_ser.c: pass buffer sizes as size_t not int [#2692] Signed-off-by: Jim Klimov --- drivers/nhs_ser.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/nhs_ser.c b/drivers/nhs_ser.c index 5318a1606c..a9bfaf3d8d 100644 --- a/drivers/nhs_ser.c +++ b/drivers/nhs_ser.c @@ -342,20 +342,20 @@ static int openfd(const char * portarg, int BAUDRATE); #if 0 static int write_serial(int fd, const char * dados, int size); #endif -static int write_serial_int(int fd, const unsigned int * data, int size); +static int write_serial_int(int fd, const unsigned int *data, size_t size); static void print_pkt_hwinfo(pkt_hwinfo data); static void print_pkt_data(pkt_data data); -static void pdatapacket(unsigned char * datapkt, int size); -static pkt_data mount_datapacket(unsigned char * datapkt, int size, double tempodecorrido, pkt_hwinfo pkt_upsinfo); -static pkt_hwinfo mount_hwinfo(unsigned char *datapkt, int size); +static void pdatapacket(unsigned char *datapkt, size_t size); +static pkt_data mount_datapacket(unsigned char *datapkt, size_t size, double tempodecorrido, pkt_hwinfo pkt_upsinfo); +static pkt_hwinfo mount_hwinfo(unsigned char *datapkt, size_t size); static upsinfo getupsinfo(unsigned int upscode); static unsigned int get_va(int equipment); static unsigned int get_vbat(void); static float get_pf(void); static unsigned int get_ah(void); -static float get_vin_perc(char * var); +static float get_vin_perc(char *var); static unsigned int get_numbat(void); @@ -623,8 +623,8 @@ static unsigned char calculate_checksum(unsigned char *pacote, int inicio, int f return (soma & 0xFF); } -static void pdatapacket(unsigned char * datapkt, int size) { - int i = 0; +static void pdatapacket(unsigned char *datapkt, size_t size) { + size_t i = 0; if (!debug_pkt_raw) return; @@ -634,7 +634,7 @@ static void pdatapacket(unsigned char * datapkt, int size) { upsdebugx(1, "%s: logging received data packet bytes at debug verbosity 5 or more", __func__); for (i = 0; i < size; i++) { - upsdebugx(5, "\tPosition %d -- 0x%02X -- Decimal %d -- Char %c", i, datapkt[i], datapkt[i], datapkt[i]); + upsdebugx(5, "\tPosition %" PRIuSIZE " -- 0x%02X -- Decimal %d -- Char %c", i, datapkt[i], datapkt[i], datapkt[i]); } } } @@ -655,8 +655,8 @@ static unsigned int get_vbat(void) { } } -static pkt_data mount_datapacket(unsigned char * datapkt, int size, double tempodecorrido, pkt_hwinfo pkt_upsinfo) { - int i = 0; +static pkt_data mount_datapacket(unsigned char *datapkt, size_t size, double tempodecorrido, pkt_hwinfo pkt_upsinfo) { + size_t i = 0; unsigned int vbat = 0; unsigned char checksum = 0x00; pkt_data pktdata = { @@ -789,8 +789,8 @@ static pkt_data mount_datapacket(unsigned char * datapkt, int size, double tempo return pktdata; } -static pkt_hwinfo mount_hwinfo(unsigned char *datapkt, int size) { - int i = 0; +static pkt_hwinfo mount_hwinfo(unsigned char *datapkt, size_t size) { + size_t i = 0; unsigned char checksum = 0x00; pkt_hwinfo pkthwinfo = { 0xFF, /* header */ @@ -913,7 +913,7 @@ static pkt_hwinfo mount_hwinfo(unsigned char *datapkt, int size) { } #if 0 -static int write_serial(int fd, const char * dados, int size) { +static int write_serial(int fd, const char *dados, size_t size) { if (fd > 0) { ssize_t bytes_written = write(fd, dados, size); if (bytes_written < 0) @@ -927,11 +927,11 @@ static int write_serial(int fd, const char * dados, int size) { } #endif -static int write_serial_int(int fd, const unsigned int * data, int size) { +static int write_serial_int(int fd, const unsigned int *data, size_t size) { if (fd > 0) { ssize_t bytes_written; uint8_t *message = NULL; - int i = 0; + size_t i = 0; message = xcalloc(size, sizeof(uint8_t)); for (i = 0; i < size; i++) { From 10448f0c1a59171d17422f242026d717aaef83b7 Mon Sep 17 00:00:00 2001 From: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> Date: Mon, 9 Dec 2024 18:25:15 +0200 Subject: [PATCH 142/144] typo for rerun CI Signed-off-by: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> --- data/cmdvartab | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/cmdvartab b/data/cmdvartab index 8fc4f48948..5c56727720 100644 --- a/data/cmdvartab +++ b/data/cmdvartab @@ -224,7 +224,7 @@ VARDESC driver.version "Driver version - NUT release" VARDESC driver.version.internal "Internal driver version" VARDESC driver.version.usb "USB library version" VARDESC driver.version.data "Version of the internal data mapping, for generic drivers" -VARDESC driver.state "Current state in driver's lifecycle" +VARDESC driver.state "Current state in driver's lifecycle" # FIXME: driver.parameter and driver.flag can have many possible members # From a171cde2ea979dd8d9f35b4500cf7e7576a52685 Mon Sep 17 00:00:00 2001 From: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> Date: Mon, 9 Dec 2024 18:25:57 +0200 Subject: [PATCH 143/144] remove typo Signed-off-by: DaRK AnGeL <28630321+masterwishx@users.noreply.github.com> --- data/cmdvartab | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/cmdvartab b/data/cmdvartab index 5c56727720..8fc4f48948 100644 --- a/data/cmdvartab +++ b/data/cmdvartab @@ -224,7 +224,7 @@ VARDESC driver.version "Driver version - NUT release" VARDESC driver.version.internal "Internal driver version" VARDESC driver.version.usb "USB library version" VARDESC driver.version.data "Version of the internal data mapping, for generic drivers" -VARDESC driver.state "Current state in driver's lifecycle" +VARDESC driver.state "Current state in driver's lifecycle" # FIXME: driver.parameter and driver.flag can have many possible members # From e9bc3dda1f052527fb917ecc80e862da0e5336fc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 9 Dec 2024 18:14:17 +0100 Subject: [PATCH 144/144] docs/nut.dict: add MGE "designator" [#2713] Signed-off-by: Jim Klimov --- docs/nut.dict | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/nut.dict b/docs/nut.dict index 18cbd25937..cd8ba0917f 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 3263 utf-8 +personal_ws-1.1 en 3264 utf-8 AAC AAS ABI @@ -1786,6 +1786,7 @@ desc deschis descr desde +designator dev devctl devd