From e43d600ca30acbdfb0a5ef6503b0a6a488e7613a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 01:56:29 +0200 Subject: [PATCH 01/16] common/common.c: parsepid(): set errno in case of errors [#2567] Signed-off-by: Jim Klimov --- common/common.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/common.c b/common/common.c index 8d2e674661..47ae99f620 100644 --- a/common/common.c +++ b/common/common.c @@ -1412,8 +1412,10 @@ pid_t parsepid(const char *buf) pid_t pid = -1; intmax_t _pid; + errno = 0; if (!buf) { upsdebugx(6, "%s: called with NULL input", __func__); + errno = EINVAL; return pid; } @@ -1422,6 +1424,8 @@ pid_t parsepid(const char *buf) if (_pid <= get_max_pid_t()) { pid = (pid_t)_pid; } else { + errno = ERANGE; + if (nut_debug_level > 0 || nut_sendsignal_debug_level > 0) upslogx(LOG_NOTICE, "Received a pid number too big for a pid_t: %" From 8ed6a70f82273ffeef720cd79e650b3da283b11d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 04:29:07 +0200 Subject: [PATCH 02/16] docs/man/upsdrvctl.txt: add an example of "list" with hidden banner [#2567] Signed-off-by: Jim Klimov --- docs/man/upsdrvctl.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/man/upsdrvctl.txt b/docs/man/upsdrvctl.txt index 0ceb3b6a77..20f816e4e2 100644 --- a/docs/man/upsdrvctl.txt +++ b/docs/man/upsdrvctl.txt @@ -134,7 +134,12 @@ here and managed in other commands). NOTE: The tool name and NUT version banner line is also printed to `stdout` before any other processing. This can be suppressed by `NUT_QUIET_INIT_BANNER` -environment variable (exported by caller and empty or "true"). +environment variable (exported by caller and empty or "true"): + + :; NUT_QUIET_INIT_BANNER=true upsdrvctl list + dummy + UPS1 + UPS2 *status*:: Similar to `list`, but reports more information -- also the driver name, the From e592b3cf347b196a047098414fa9dae56ff45bad Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 04:32:55 +0200 Subject: [PATCH 03/16] drivers/upsdrvctl.c, docs/man/upsdrvctl.txt: "status": detect a RUNNING or not program using PID from *either* PID file or socket protocol [#2567] Signed-off-by: Jim Klimov --- docs/man/upsdrvctl.txt | 34 +++++++++++-------- drivers/upsdrvctl.c | 76 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 22 deletions(-) diff --git a/docs/man/upsdrvctl.txt b/docs/man/upsdrvctl.txt index 20f816e4e2..d4f2cf9438 100644 --- a/docs/man/upsdrvctl.txt +++ b/docs/man/upsdrvctl.txt @@ -149,30 +149,38 @@ If there is anything to print (at least one device is known), the first line of status report would be the heading with column names: :; NUT_QUIET_INIT_BANNER=true upsdrvctl status - UPSNAME UPSDRV PF_RUNNING PF_PID S_RESPONSIVE S_PID S_STATUS - dummy dummy-ups N/A -3 NOT_RESPONSIVE N/A - eco650 usbhid-ups RUNNING 3559207 RESPONSIVE 3559207 "OL" - UPS2 dummy-ups RUNNING 31455 RESPONSIVE 31455 "OL BOOST" + UPSNAME UPSDRV RUNNING PF_PID S_RESPONSIVE S_PID S_STATUS + dummy dummy-ups N/A -3 NOT_RESPONSIVE N/A + eco650 usbhid-ups RUNNING 3559207 RESPONSIVE 3559207 "OL" + UPS2 dummy-ups RUNNING 31455 RESPONSIVE 31455 "OL BOOST" -Values are TAB-separated. Any complex string values would be encased in -double-quotes. +Values are TAB-separated, but UPSNAME and UPSDRV may be padded by spaces +on the right and on the left respectively. Any complex string values would +be encased in double-quotes. Fields reported (`PF_*` = according to PID file, if any; `S_*` = via socket protocol): *UPSNAME*;; driver section configuration name *UPSDRV*;; driver program name per `ups.conf` - *PF_RUNNING*;; `RUNNING` if `PF_PID` is valid (PID file existed, - PID was parsed out of it and found running, program - name corresponds to driver, etc.); `STOPPED` if PID - was parsed but not running or has a wrong program - name; "N/A" if failed to parse it or no PID file + *RUNNING*;; `RUNNING` if `PF_PID` or `S_PID` is valid, + `STOPPED` if at least one PID value was parsed but + none was found running with a correct program name; + `N/A` if no PID file/socket reply or failed to parse. + First the PID file is consulted, but it may be absent + either due to command-line parameters of daemons, or + due to platform (WIN32). If no PID value was found and + confirmed this way, we fall back to checking the PID + reported via protocol (if available and different). *PF_PID*;; PID of driver according to PID file (if any), or some negative values upon errors (as defined in `common.c`) + including an absent PID file, invalid contents, or + unsupported platform for this mechanism (e.g. WIN32) *S_RESPONSIVE*;; `RESPONSIVE` if `PING`/`PONG` during socket protocol session setup succeeded; `NOT_RESPONSIVE` otherwise - *S_PID*;; PID of driver according to `GETPID` active query - *S_STATUS*;; Value of `ups.status` variable + *S_PID*;; PID of driver according to `GETPID` active query, + or `N/A` if the query failed + *S_STATUS*;; Quoted value of `ups.status` variable This mode does not discover drivers that are not in `ups.conf` (e.g. started manually for experiments with many `-x` CLI options). diff --git a/drivers/upsdrvctl.c b/drivers/upsdrvctl.c index 99fe674a89..c5b5ac0f8b 100644 --- a/drivers/upsdrvctl.c +++ b/drivers/upsdrvctl.c @@ -818,9 +818,16 @@ static void status_driver(const ups_t *ups) * (e.g. valid PID existence, data query via socket protocol...) */ static int headerShown = 0; - char pidfn[SMALLBUF], bufPid[LARGEBUF], *pidStrFromSocket = NULL, bufStatus[LARGEBUF], *statusStrFromSocket = NULL; - int cmdret = -1, qretPing = -1, qretPid = -1, qretStatus = -1, nudl = nut_upsdrvquery_debug_level, nsdl = nut_sendsignal_debug_level; - pid_t pidFromFile = -1; +#ifndef WIN32 + char pidfn[SMALLBUF]; +#endif + char bufPid[LARGEBUF], *pidStrFromSocket = NULL, + bufStatus[LARGEBUF], *statusStrFromSocket = NULL; + int cmdret = -1, + qretPing = -1, qretPid = -1, qretStatus = -1, + nudl = nut_upsdrvquery_debug_level, + nsdl = nut_sendsignal_debug_level; + pid_t pidFromFile = -1, pidFromSocket = -1; struct timeval tv; udq_pipe_conn_t *conn; @@ -837,9 +844,16 @@ static void status_driver(const ups_t *ups) if (pidFromFile >= 0) { /* this method actively reports errors, if any */ cmdret = sendsignalpid(pidFromFile, 0, ups->driver, 1); } + upsdebugx(4, "%s: pidfn=%s pidFromFile=%" PRIiMAX " cmdret=%d", + __func__, pidfn, (intmax_t)pidFromFile, cmdret); #else +/* // FIXME: We actually have no probing signals over pipe so far, + // and sending 0 (NULL here) is proven unsafe + // Instead we will try below with PID learned from pipe snprintf(pidfn, sizeof(pidfn), "%s-%s", ups->driver, ups->upsname); - cmdret = sendsignal(pidfn, 0, 1); + cmdret = sendsignal(pidfn, COMMAND_RELOAD, 1); + upsdebugx(4, "%s: pipe pidfn=%s cmdret=%d", __func__, pidfn, cmdret); + */ #endif /* Hush the fopen(socketfile) in upsdrvquery_connect_drvname_upsname() */ @@ -848,6 +862,7 @@ static void status_driver(const ups_t *ups) nut_upsdrvquery_debug_level = nudl; if (conn && VALID_FD(conn->sockfd)) { + upsdebugx(3, "%s: connected", __func__); /* Post the query and wait for reply */ /* FIXME: coordinate with pollfreq? */ tv.tv_sec = 3; @@ -857,6 +872,7 @@ static void status_driver(const ups_t *ups) /* Involves a PING/PONG check, and more; * returns -1 on error */ + upsdebugx(3, "%s: upsdrvquery_prepare", __func__); qretPing = upsdrvquery_prepare(conn, tv); if (qretPing >= 0) { @@ -865,17 +881,23 @@ static void status_driver(const ups_t *ups) /* No TRACKING in queries below */ memset(bufPid, 0, sizeof(bufPid)); + upsdebugx(3, "%s: upsdrvquery_write GETPID", __func__); if (upsdrvquery_write(conn, "GETPID\n") >= 0 && (qretPid = upsdrvquery_read_timeout(conn, tv)) >= 1 && (!strncmp(conn->buf, "PID ", 4)) ) { size_t l; + upsdebugx(4, "%s: upsdrvquery_read GETPID", __func__); snprintf(bufPid, sizeof(bufPid), "%s", conn->buf + 4); + upsdebugx(4, "%s: upsdrvquery_read GETPID 2", __func__); l = strlen(bufPid); pidStrFromSocket = bufPid; if (bufPid[l - 1] == '\n') bufPid[l - 1] = '\0'; qretPid = STAT_INSTCMD_HANDLED; + pidFromSocket = parsepid(pidStrFromSocket); + if (errno != 0) + pidFromSocket = -1; } else { /* query failed or returned not a PID */ qretPid = -1; @@ -884,15 +906,22 @@ static void status_driver(const ups_t *ups) if (qretPid == STAT_INSTCMD_HANDLED) { memset(bufStatus, 0, sizeof(bufStatus)); +#ifdef WIN32 + /* Allow a new read to happen later */ + conn->newread = 1; +#endif + upsdebugx(3, "%s: upsdrvquery_write DUMPSTATUS", __func__); if (upsdrvquery_write(conn, "DUMPSTATUS\n") >= 0 && (qretStatus = upsdrvquery_read_timeout(conn, tv)) >= 1 ) { char *buf; + upsdebugx(4, "%s: upsdrvquery_read DUMPSTATUS", __func__); /* save before strtok mangles it */ snprintf(bufStatus, sizeof(bufStatus), "%s", conn->buf); + upsdebugx(4, "%s: upsdrvquery_read DUMPSTATUS 2", __func__); for (buf = strtok(bufStatus, "\n"); buf; buf = strtok(NULL, "\n")) { if (statusStrFromSocket) { /* New loop, NUL byte was set if needed */ @@ -915,19 +944,46 @@ static void status_driver(const ups_t *ups) qretStatus = -1; } } + } else { + upsdebugx(3, "%s: not connected", __func__); } + upsdebugx(3, "%s: close socket", __func__); upsdrvquery_close(conn); if (conn) { + upsdebugx(4, "%s: free socket", __func__); free(conn); conn = NULL; } + if (pidFromFile < 0 && pidFromSocket >= 0) { + upsdebugx(3, "%s: PID was not available from a file, but was " + "from Socket Protocol; check if it is alive instead", + __func__); + + cmdret = checkprocname(pidFromSocket, ups->driver); + } else if (cmdret < 0 && pidFromSocket >= 0 && (pidFromFile != pidFromSocket)) { + upsdebugx(3, "%s: PID value was available from a file, but was " + "not found running or valid; another one is available " + "from Socket Protocol; check if it is alive instead", + __func__); + + cmdret = checkprocname(pidFromSocket, ups->driver); + upsdebugx(4, "%s: pidFromSocket=%" PRIiMAX " cmdret=%d", + __func__, (intmax_t)pidFromSocket, cmdret); + } + + /* Complete any cached (error) writes before the next lines, + * more so on WIN32 */ + fflush(stderr); + fflush(stdout); + usleep(1000); + if (!headerShown) { - printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", + printf("%-11s\t%11s\t%s\t%s\t%s\t%s\t%s\n", "UPSNAME", "UPSDRV", - "PF_RUNNING", + "RUNNING", "PF_PID", "S_RESPONSIVE", "S_PID", @@ -936,14 +992,16 @@ static void status_driver(const ups_t *ups) headerShown = 1; } - printf("%s\t%s\t%s\t%" PRIiMAX "\t%s\t%s\t%s\n", + printf("%-11s\t%11s\t%s\t%" PRIiMAX "\t%s\t%s\t%s\n", ups->upsname, ups->driver, - (pidFromFile < 0 ? "N/A" : (cmdret ? "STOPPED" : "RUNNING")), + ((pidFromFile < 0 && pidFromSocket < 0) || (cmdret < 0) + ? "N/A" : (cmdret > 0 ? "RUNNING" : "STOPPED")), (intmax_t)(pidFromFile), ((qretPing == STAT_INSTCMD_HANDLED) ? "RESPONSIVE" : "NOT_RESPONSIVE"), (pidStrFromSocket ? pidStrFromSocket : "N/A"), ((qretStatus == STAT_INSTCMD_HANDLED) ? NUT_STRARG(statusStrFromSocket) : "") ); + fflush(stdout); nut_sendsignal_debug_level = nsdl; } @@ -1160,7 +1218,7 @@ static void help(const char *arg_progname) printf(" shutdown only shutdown UPS \n"); printf(" status query status for all UPS drivers in ups.conf\n"); printf(" status only query status for driver for UPS \n"); - printf(" Fields: UPSNAME UPSDRV PF_RUNNING PF_PID S_RESPONSIVE S_PID S_STATUS\n"); + printf(" Fields: UPSNAME UPSDRV RUNNING PF_PID S_RESPONSIVE S_PID S_STATUS\n"); printf(" (PF_* = according to PID file, if any; S_* = via socket protocol)\n"); exit(EXIT_SUCCESS); From e2f8b44330a341bdac36131b54c31145b3f44900 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 04:34:14 +0200 Subject: [PATCH 04/16] drivers/dstate.c, docs/sock-protocol.txt: introduce a LOGOUT command [#2567] Now that the driver Unix socket / Windows named pipe connections can come and go (sibling drivers, `upsdrvctl status`...) it is not pretty to see logged messages about an unexpected connection loss. Having it coincide with a LOGOUT is better for troubleshooting. Signed-off-by: Jim Klimov --- docs/sock-protocol.txt | 19 +++++++++++++++++++ drivers/dstate.c | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/sock-protocol.txt b/docs/sock-protocol.txt index 4143e50155..4442dc2a28 100644 --- a/docs/sock-protocol.txt +++ b/docs/sock-protocol.txt @@ -162,6 +162,14 @@ This is sent in response to a PING from the server. It is only used as a sanity check to make sure that the driver has not gotten stuck somewhere. +OK +~~ + + OK Goodbye + +This is sent in response to a LOGOUT from the server (or more likely from +a sibling driver or `upsdrvctl` program). + DATAOK ~~~~~~ @@ -312,6 +320,17 @@ numeric argument. Note that initial default is to receive everything, so this command may be useful for connections that disabled broadcasts at some point. +LOGOUT +~~~~~~ + +Primarily used by communications between driver processes and/or `upsdrvctl`, +this command allows clients to gracefully close connection to the NUT driver +which acts as the server on the socket/pipe, avoiding noisy logs about sudden +disconnection. + + LOGOUT + OK Goodbye + Design notes ------------ diff --git a/drivers/dstate.c b/drivers/dstate.c index e2bafc89f9..8e4fb7506a 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -203,6 +203,7 @@ static TYPE_FD sock_open(const char *fn) static void sock_disconnect(conn_t *conn) { #ifndef WIN32 + upsdebugx(3, "%s: disconnecting socket %d", __func__, (int)conn->fd); close(conn->fd); #else /* FIXME not sure if this is the right way to close a connection */ @@ -210,11 +211,14 @@ static void sock_disconnect(conn_t *conn) CloseHandle(conn->read_overlapped.hEvent); conn->read_overlapped.hEvent = INVALID_HANDLE_VALUE; } + upsdebugx(3, "%s: disconnecting named pipe handle %p", __func__, conn->fd); DisconnectNamedPipe(conn->fd); #endif + upsdebugx(5, "%s: finishing parsing context", __func__); pconf_finish(&conn->ctx); + upsdebugx(5, "%s: relinking the chain of connections", __func__); if (conn->prev) { conn->prev->next = conn->next; } else { @@ -227,6 +231,7 @@ static void sock_disconnect(conn_t *conn) /* conntail = conn->prev; */ } + upsdebugx(5, "%s: freeing the conn object", __func__); free(conn); } @@ -701,6 +706,21 @@ static int sock_arg(conn_t *conn, size_t numarg, char **arg) return 0; } + if (!strcasecmp(arg[0], "LOGOUT")) { + send_to_one(conn, "OK Goodbye\n"); +#ifndef WIN32 + upsdebugx(2, "%s: received LOGOUT on socket %d, disconnecting", __func__, (int)conn->fd); +#else + upsdebugx(2, "%s: received LOGOUT on handle %p, disconnecting", __func__, conn->fd); +#endif + /* Let the system flush the reply somehow (or the other + * side to just see it) before we drop the pipe */ + usleep(1000000); + sock_disconnect(conn); + upsdebugx(4, "%s: LOGOUT processing finished", __func__); + return 2; + } + if (!strcasecmp(arg[0], "GETPID")) { send_to_one(conn, "PID %" PRIiMAX "\n", (intmax_t)getpid()); return 1; @@ -901,6 +921,7 @@ static int sock_arg(conn_t *conn, size_t numarg, char **arg) static void sock_read(conn_t *conn) { ssize_t ret, i; + int ret_arg = -1; #ifndef WIN32 char buf[SMALLBUF]; @@ -979,7 +1000,8 @@ static void sock_read(conn_t *conn) continue; case 1: /* try to use it, and complain about unknown commands */ - if (!sock_arg(conn, conn->ctx.numargs, conn->ctx.arglist)) { + ret_arg = sock_arg(conn, conn->ctx.numargs, conn->ctx.arglist); + if (!ret_arg) { size_t arg; upslogx(LOG_INFO, "Unknown command on socket: "); @@ -987,7 +1009,12 @@ static void sock_read(conn_t *conn) for (arg = 0; arg < conn->ctx.numargs && arg < INT_MAX; arg++) { upslogx(LOG_INFO, "arg %d: %s", (int)arg, conn->ctx.arglist[arg]); } + } else if (ret_arg == 2) { + /* closed by LOGOUT processing, conn is free()'d */ + upsdebugx(1, "%s: returning early, socket is not valid anymore", __func__); + return; } + continue; default: /* nothing parsed */ @@ -1154,6 +1181,8 @@ int dstate_poll_fds(struct timeval timeout, TYPE_FD arg_extrafd) if (FD_ISSET(conn->fd, &rfds)) { sock_read(conn); + /* Note: do not use "conn" after loop, + * it may be freed by LOGOUT */ } } @@ -1235,6 +1264,8 @@ int dstate_poll_fds(struct timeval timeout, TYPE_FD arg_extrafd) else { if (conn != NULL) { sock_read(conn); + /* Note: do not use "conn" after this, + * it may be freed by LOGOUT */ } } From b3287a11602c078743e4077cdb1f4d6a78c25ba3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 04:36:35 +0200 Subject: [PATCH 05/16] drivers/upsdrvquery.c: upsdrvquery_close(): take advantage of the new LOGOUT command [#2567] Signed-off-by: Jim Klimov --- drivers/upsdrvquery.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/upsdrvquery.c b/drivers/upsdrvquery.c index 4ba99181e6..e94f7b45cf 100644 --- a/drivers/upsdrvquery.c +++ b/drivers/upsdrvquery.c @@ -180,6 +180,20 @@ void upsdrvquery_close(udq_pipe_conn_t *conn) { if (!conn) return; + if (VALID_FD(conn->sockfd)) { + int nudl = nut_upsdrvquery_debug_level; + ssize_t ret; + upsdebugx(5, "%s: closing driver socket, try to say goodbye", __func__); + ret = upsdrvquery_write(conn, "LOGOUT\n"); + if (7 <= ret) { + upsdebugx(5, "%s: okay", __func__); + usleep(1000000); + } else { + upsdebugx(5, "%s: must have been closed on the other side", __func__); + } + nut_upsdrvquery_debug_level = nudl; + } + #ifndef WIN32 if (VALID_FD(conn->sockfd)) close(conn->sockfd); From caf59a4131dd956b234424e68955a5361920b3f7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 04:36:57 +0200 Subject: [PATCH 06/16] server/netmisc.c: update net_help() Signed-off-by: Jim Klimov --- server/netmisc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/netmisc.c b/server/netmisc.c index 199e6d719b..fbf8e0eb31 100644 --- a/server/netmisc.c +++ b/server/netmisc.c @@ -92,8 +92,9 @@ void net_help(nut_ctype_t *client, size_t numarg, const char **arg) return; } - sendback(client, "Commands: HELP VER GET LIST SET INSTCMD LOGIN LOGOUT" - " USERNAME PASSWORD STARTTLS\n"); + sendback(client, "Commands: HELP VER PROTVER GET LIST SET INSTCMD" + " LOGIN LOGOUT USERNAME PASSWORD STARTTLS\n"); + /* Not exposed: PRIMARY/MASTER FSD */ } void net_fsd(nut_ctype_t *client, size_t numarg, const char **arg) From eb6eaf4f32d30ffe676ba360b52e516f67ef3a17 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 04:47:43 +0200 Subject: [PATCH 07/16] drivers/dstate.{c,h}: err on the safe side with LOGOUT, and only mark the connection - close later [#2567] Signed-off-by: Jim Klimov --- drivers/dstate.c | 34 ++++++++++++++++++++++++---------- drivers/dstate.h | 1 + 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 8e4fb7506a..e170bfcb40 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -576,6 +576,7 @@ static void sock_connect(TYPE_FD sock) conn->nobroadcast = 0; conn->readzero = 0; + conn->closing = 0; pconf_init(&conn->ctx, NULL); if (connhead) { @@ -709,14 +710,15 @@ static int sock_arg(conn_t *conn, size_t numarg, char **arg) if (!strcasecmp(arg[0], "LOGOUT")) { send_to_one(conn, "OK Goodbye\n"); #ifndef WIN32 - upsdebugx(2, "%s: received LOGOUT on socket %d, disconnecting", __func__, (int)conn->fd); + upsdebugx(2, "%s: received LOGOUT on socket %d, will be disconnecting", __func__, (int)conn->fd); #else - upsdebugx(2, "%s: received LOGOUT on handle %p, disconnecting", __func__, conn->fd); + upsdebugx(2, "%s: received LOGOUT on handle %p, will be disconnecting", __func__, conn->fd); #endif /* Let the system flush the reply somehow (or the other * side to just see it) before we drop the pipe */ usleep(1000000); - sock_disconnect(conn); + /* err on the safe side, and actually close/free conn separately */ + conn->closing = 1; upsdebugx(4, "%s: LOGOUT processing finished", __func__); return 2; } @@ -1011,7 +1013,8 @@ static void sock_read(conn_t *conn) } } else if (ret_arg == 2) { /* closed by LOGOUT processing, conn is free()'d */ - upsdebugx(1, "%s: returning early, socket is not valid anymore", __func__); + if (i < ret) + upsdebugx(1, "%s: returning early, socket may be not valid anymore", __func__); return; } @@ -1105,13 +1108,12 @@ int dstate_poll_fds(struct timeval timeout, TYPE_FD arg_extrafd) { int maxfd = 0; /* Unidiomatic use vs. "sockfd" below, which is "int" on non-WIN32 */ int overrun = 0; - conn_t *conn; + conn_t *conn, *cnext; struct timeval now; #ifndef WIN32 int ret; fd_set rfds; - conn_t *cnext; FD_ZERO(&rfds); FD_SET(sockfd, &rfds); @@ -1181,8 +1183,14 @@ int dstate_poll_fds(struct timeval timeout, TYPE_FD arg_extrafd) if (FD_ISSET(conn->fd, &rfds)) { sock_read(conn); - /* Note: do not use "conn" after loop, - * it may be freed by LOGOUT */ + } + } + + for (conn = connhead; conn; conn = cnext) { + cnext = conn->next; + + if (conn->closing) { + sock_disconnect(conn); } } @@ -1264,8 +1272,14 @@ int dstate_poll_fds(struct timeval timeout, TYPE_FD arg_extrafd) else { if (conn != NULL) { sock_read(conn); - /* Note: do not use "conn" after this, - * it may be freed by LOGOUT */ + } + } + + for (conn = connhead; conn; conn = cnext) { + cnext = conn->next; + + if (conn->closing) { + sock_disconnect(conn); } } diff --git a/drivers/dstate.h b/drivers/dstate.h index fa2510d877..13b2957737 100644 --- a/drivers/dstate.h +++ b/drivers/dstate.h @@ -54,6 +54,7 @@ typedef struct conn_s { struct conn_s *next; int nobroadcast; /* connections can request to ignore send_to_all() updates */ int readzero; /* how many times in a row we had zero bytes read; see DSTATE_CONN_READZERO_THROTTLE_USEC and DSTATE_CONN_READZERO_THROTTLE_MAX */ + int closing; /* raised during LOGOUT processing, to close the socket when time is right */ } conn_t; /* sleep after read()ing zero bytes */ From 813ef171ed420d467dbc9a1f6624928aa13769ba Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 04:55:11 +0200 Subject: [PATCH 08/16] tools/gitlog2version.sh: comment why we repeat TRUNK candidate branches, and add master in the end too [#1949] Signed-off-by: Jim Klimov --- tools/gitlog2version.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/gitlog2version.sh b/tools/gitlog2version.sh index b2ebe50435..e7c3e1696c 100755 --- a/tools/gitlog2version.sh +++ b/tools/gitlog2version.sh @@ -111,8 +111,11 @@ getver_git() { # or "upstream/master", etc.) for resulting version discovery to make sense. if [ x"${NUT_VERSION_GIT_TRUNK-}" = x ] ; then # Find the newest info, it may be in a fetched branch - # not yet checked out locally (or long not updated) - for T in master `git branch -a 2>/dev/null | grep -E '^ *remotes/[^ ]*/master$'` origin/master upstream/master ; do + # not yet checked out locally (or long not updated). + # Currently we repeat the likely branch names in the + # end, so that if they exist and are still newest - + # those are the names to report. + for T in master `git branch -a 2>/dev/null | grep -E '^ *remotes/[^ ]*/master$'` origin/master upstream/master master ; do git log -1 "$T" 2>/dev/null >/dev/null || continue if [ x"${NUT_VERSION_GIT_TRUNK-}" = x ] ; then NUT_VERSION_GIT_TRUNK="$T" From 056d179c036bc71fc0879e438eae3dd33cc90024 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 05:30:48 +0200 Subject: [PATCH 09/16] drivers/upsdrvctl.c: status: revise return values of checkprocname() vs. sendsignalpid(), unite via pidAlive [#2567] Signed-off-by: Jim Klimov --- drivers/upsdrvctl.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/upsdrvctl.c b/drivers/upsdrvctl.c index c5b5ac0f8b..f7c48d94d1 100644 --- a/drivers/upsdrvctl.c +++ b/drivers/upsdrvctl.c @@ -820,10 +820,11 @@ static void status_driver(const ups_t *ups) static int headerShown = 0; #ifndef WIN32 char pidfn[SMALLBUF]; + int cmdret = -1; #endif char bufPid[LARGEBUF], *pidStrFromSocket = NULL, bufStatus[LARGEBUF], *statusStrFromSocket = NULL; - int cmdret = -1, + int pidAlive = -1, qretPing = -1, qretPid = -1, qretStatus = -1, nudl = nut_upsdrvquery_debug_level, nsdl = nut_sendsignal_debug_level; @@ -843,9 +844,12 @@ static void status_driver(const ups_t *ups) pidFromFile = parsepidfile(pidfn); if (pidFromFile >= 0) { /* this method actively reports errors, if any */ cmdret = sendsignalpid(pidFromFile, 0, ups->driver, 1); + /* returns zero for a successfully sent signal */ + if (cmdret == 0) + pidAlive = 1; } - upsdebugx(4, "%s: pidfn=%s pidFromFile=%" PRIiMAX " cmdret=%d", - __func__, pidfn, (intmax_t)pidFromFile, cmdret); + upsdebugx(4, "%s: pidfn=%s pidFromFile=%" PRIiMAX " cmdret=%d pidAlive=%d", + __func__, pidfn, (intmax_t)pidFromFile, cmdret, pidAlive); #else /* // FIXME: We actually have no probing signals over pipe so far, // and sending 0 (NULL here) is proven unsafe @@ -961,16 +965,18 @@ static void status_driver(const ups_t *ups) "from Socket Protocol; check if it is alive instead", __func__); - cmdret = checkprocname(pidFromSocket, ups->driver); - } else if (cmdret < 0 && pidFromSocket >= 0 && (pidFromFile != pidFromSocket)) { + pidAlive = checkprocname(pidFromSocket, ups->driver); + upsdebugx(4, "%s: pidFromSocket=%" PRIiMAX " pidAlive=%d", + __func__, (intmax_t)pidFromSocket, pidAlive); + } else if (pidAlive < 0 && pidFromSocket >= 0 && (pidFromFile != pidFromSocket)) { upsdebugx(3, "%s: PID value was available from a file, but was " "not found running or valid; another one is available " "from Socket Protocol; check if it is alive instead", __func__); - cmdret = checkprocname(pidFromSocket, ups->driver); - upsdebugx(4, "%s: pidFromSocket=%" PRIiMAX " cmdret=%d", - __func__, (intmax_t)pidFromSocket, cmdret); + pidAlive = checkprocname(pidFromSocket, ups->driver); + upsdebugx(4, "%s: pidFromSocket=%" PRIiMAX " pidAlive=%d", + __func__, (intmax_t)pidFromSocket, pidAlive); } /* Complete any cached (error) writes before the next lines, @@ -992,10 +998,18 @@ static void status_driver(const ups_t *ups) headerShown = 1; } + upsdebugx(2, "%s: raw values: pidAlive=%d " + "pidFromFile=%" PRIiMAX " pidFromSocket=%" PRIiMAX " " + "qretPing=%d qretPid=%d qretStatus=%d", + __func__, pidAlive, + (intmax_t)(pidFromFile), (intmax_t)(pidFromSocket), + qretPing, qretPid, qretStatus + ); + printf("%-11s\t%11s\t%s\t%" PRIiMAX "\t%s\t%s\t%s\n", ups->upsname, ups->driver, - ((pidFromFile < 0 && pidFromSocket < 0) || (cmdret < 0) - ? "N/A" : (cmdret > 0 ? "RUNNING" : "STOPPED")), + ((pidFromFile < 0 && pidFromSocket < 0) || (pidAlive < 0) + ? "N/A" : (pidAlive > 0 ? "RUNNING" : "STOPPED")), (intmax_t)(pidFromFile), ((qretPing == STAT_INSTCMD_HANDLED) ? "RESPONSIVE" : "NOT_RESPONSIVE"), (pidStrFromSocket ? pidStrFromSocket : "N/A"), From 521c72104ead7b764823aef972c27abdebb1abaf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 05:57:56 +0200 Subject: [PATCH 10/16] drivers/upsdrvquery.c: upsdrvquery_close(): track if we loggedOut and so not complain about disconnection faults [#2567] Signed-off-by: Jim Klimov --- drivers/upsdrvquery.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/upsdrvquery.c b/drivers/upsdrvquery.c index e94f7b45cf..31bb8e94e1 100644 --- a/drivers/upsdrvquery.c +++ b/drivers/upsdrvquery.c @@ -177,6 +177,10 @@ udq_pipe_conn_t *upsdrvquery_connect_drvname_upsname(const char *drvname, const } void upsdrvquery_close(udq_pipe_conn_t *conn) { +#ifdef WIN32 + int loggedOut = 0; +#endif /* WIN32 */ + if (!conn) return; @@ -187,6 +191,9 @@ void upsdrvquery_close(udq_pipe_conn_t *conn) { ret = upsdrvquery_write(conn, "LOGOUT\n"); if (7 <= ret) { upsdebugx(5, "%s: okay", __func__); +#ifdef WIN32 + loggedOut = 1; +#endif /* WIN32 */ usleep(1000000); } else { upsdebugx(5, "%s: must have been closed on the other side", __func__); @@ -204,7 +211,7 @@ void upsdrvquery_close(udq_pipe_conn_t *conn) { memset(&(conn->overlapped), 0, sizeof(conn->overlapped)); if (VALID_FD(conn->sockfd)) { - if (DisconnectNamedPipe(conn->sockfd) == 0) { + if (DisconnectNamedPipe(conn->sockfd) == 0 && !loggedOut) { if (nut_debug_level > 0 || nut_upsdrvquery_debug_level >= NUT_UPSDRVQUERY_DEBUG_LEVEL_CONNECT) upslogx(LOG_ERR, "DisconnectNamedPipe error : %d", From 717229874afc00f4df948ef082554e7914c15798 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 06:00:25 +0200 Subject: [PATCH 11/16] NEWS.adoc: new dstate LOGOUT command [#2567] Signed-off-by: Jim Klimov --- NEWS.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.adoc b/NEWS.adoc index 03f36a4dc3..7c30176bef 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -217,6 +217,8 @@ during a NUT build. and values (or macros) in the NUT codebase. [issue #1176, issue #31] * custom `distcheck-something` targets did not inherit `DISTCHECK_FLAGS` properly. [#2541] + * local socket/pipe protocol introduced a `LOGOUT` command for cleaner + disconnection handling. [#2572] - updated `docs/nut-names.txt` with items defined by 42ITy NUT fork. [#2339] From a50b800831ec755539c9b4c7db2cad698a9572db Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 10:15:02 +0200 Subject: [PATCH 12/16] scripts/upsdrvsvcctl/upsdrvsvcctl.in: make path to UPSDRVCTL a variable, like ENUMERATOR is used [#2567] Signed-off-by: Jim Klimov --- scripts/upsdrvsvcctl/upsdrvsvcctl.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/upsdrvsvcctl/upsdrvsvcctl.in b/scripts/upsdrvsvcctl/upsdrvsvcctl.in index 4878e6c6ec..3f05ffa0c9 100755 --- a/scripts/upsdrvsvcctl/upsdrvsvcctl.in +++ b/scripts/upsdrvsvcctl/upsdrvsvcctl.in @@ -43,7 +43,7 @@ case "$SERVICE_FRAMEWORK" in ;; *) echo "Unrecognized SERVICE_FRAMEWORK: $SERVICE_FRAMEWORK" >&2 ; exit ;; esac - +UPSDRVCTL='@SBINDIR@/upsdrvctl' usage() { # Note: version header differs from UPS_VERSION in binaries that @@ -148,7 +148,7 @@ while [ $# -gt 0 ]; do # TODO: Add info about service units echo "NOTE: Action '$1' is not implemented via services currently, will call upsdrvctl" >&2 RES=0 - @SBINDIR@/upsdrvctl status $2 || RES=$? + "${UPSDRVCTL}" status $2 || RES=$? exit $RES ;; start|stop) @@ -163,7 +163,7 @@ while [ $# -gt 0 ]; do echo "Stopping the driver service instance(s) to release exclusive resources, if any..." >&2 RES=0 $0 stop $2 - @SBINDIR@/upsdrvctl shutdown $2 || RES=$? + "${UPSDRVCTL}" shutdown $2 || RES=$? echo "Starting the driver service instance(s) so they can reconnect when the UPS returns..." >&2 $0 start $2 exit $RES From 5e8b340bb89df6099c659ff17063e03f56664eed Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 10:27:49 +0200 Subject: [PATCH 13/16] scripts/upsdrvsvcctl/upsdrvsvcctl.in, docs/man/upsdrvsvcctl.txt: add an option to display binary version (and fix the script one in help) [#2567] Signed-off-by: Jim Klimov --- docs/man/upsdrvsvcctl.txt | 8 +++++++- scripts/upsdrvsvcctl/upsdrvsvcctl.in | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/man/upsdrvsvcctl.txt b/docs/man/upsdrvsvcctl.txt index e75939b1bc..8cb56e7a7a 100644 --- a/docs/man/upsdrvsvcctl.txt +++ b/docs/man/upsdrvsvcctl.txt @@ -61,7 +61,13 @@ OPTIONS ------- *-h*:: -Display the help text. +Display the help text, including the built-in version of the script. + +*-V*:: +Display the version of NUT binaries (calling *upsdrvctl -V*), which +normally should not differ much from the built-in version of the script +shown in help. But with custom builds everything is possible, so it may +be useful to know. *-t*:: Enable testing mode. Testing mode makes upsdrvsvcctl display the actions diff --git a/scripts/upsdrvsvcctl/upsdrvsvcctl.in b/scripts/upsdrvsvcctl/upsdrvsvcctl.in index 3f05ffa0c9..1f0b0effb9 100755 --- a/scripts/upsdrvsvcctl/upsdrvsvcctl.in +++ b/scripts/upsdrvsvcctl/upsdrvsvcctl.in @@ -44,6 +44,7 @@ case "$SERVICE_FRAMEWORK" in *) echo "Unrecognized SERVICE_FRAMEWORK: $SERVICE_FRAMEWORK" >&2 ; exit ;; esac UPSDRVCTL='@SBINDIR@/upsdrvctl' +PACKAGE_VERSION='@PACKAGE_VERSION@' usage() { # Note: version header differs from UPS_VERSION in binaries that @@ -58,6 +59,8 @@ usage: $0 [OPTIONS] (start | stop | shutdown) [] Options: -h display this help + -V display upsdrvctl binary program version (should + not differ from script version above, but...) -t testing mode - prints actions without doing them -D raise debugging level --timeout-cmd service management calls will be time-limited @@ -190,6 +193,7 @@ while [ $# -gt 0 ]; do ;; -t) DRYRUN="echo" ;; -h) usage; exit 0 ;; + -V) "${UPSDRVCTL}" -V ; exit ;; -D) DEBUG="`expr $DEBUG + 1`" ;; -r|-u) echo "Option '$1 $2' is not implemented via services currently" >&2 ; shift;; *) echo "Unrecognized argument: $1" >&2 ; exit ;; From 8acb12dcadcb33dff594157399b737c705b7fa8a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 11:43:46 +0200 Subject: [PATCH 14/16] scripts/upsdrvsvcctl/upsdrvsvcctl.in, docs/man/upsdrvsvcctl.txt: complete the "upsdrvsvcctl status" feature [#2567] Signed-off-by: Jim Klimov --- docs/man/upsdrvsvcctl.txt | 9 ++- scripts/upsdrvsvcctl/upsdrvsvcctl.in | 97 ++++++++++++++++++++++++---- 2 files changed, 92 insertions(+), 14 deletions(-) diff --git a/docs/man/upsdrvsvcctl.txt b/docs/man/upsdrvsvcctl.txt index 8cb56e7a7a..475d005462 100644 --- a/docs/man/upsdrvsvcctl.txt +++ b/docs/man/upsdrvsvcctl.txt @@ -11,7 +11,7 @@ SYNOPSIS *upsdrvsvcctl* -h -*upsdrvsvcctl* ['OPTIONS'] {start | stop } ['ups'] +*upsdrvsvcctl* ['OPTIONS'] {start | stop | status} ['ups'] DESCRIPTION ----------- @@ -135,8 +135,11 @@ Stop the UPS driver(s). *status*:: Query run-time status of all configured devices (or one specified device). -Currently defers work to linkman:upsdrvctl[8], and does not report service -unit information. +Currently defers work to linkman:upsdrvctl[8], to list known device +configurations and their driver daemon details (PID, responsiveness, +`ups.status`) and to linkman:nut-driver-enumerator[8] to map device +names to service unit instances to report their names and states in +the service management framework. *upsdrvsvcctl* also supports further operations for troubleshooting the mapping of NUT driver section names to the service instance names (which diff --git a/scripts/upsdrvsvcctl/upsdrvsvcctl.in b/scripts/upsdrvsvcctl/upsdrvsvcctl.in index 1f0b0effb9..40c2709725 100755 --- a/scripts/upsdrvsvcctl/upsdrvsvcctl.in +++ b/scripts/upsdrvsvcctl/upsdrvsvcctl.in @@ -33,12 +33,17 @@ fi VERB="" CMD="" CMDARG="" +# Note: some frameworks separate the commands for acting and for reporting +CMDREPORT="" +CMDREPORTARG="" ENUMERATOR="" case "$SERVICE_FRAMEWORK" in smf) CMD="/usr/sbin/svcadm" + CMDREPORT="/usr/bin/svcs" ENUMERATOR="@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" ;; systemd) CMD="/bin/systemctl" + CMDREPORT="${CMD}" ENUMERATOR="@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" ;; *) echo "Unrecognized SERVICE_FRAMEWORK: $SERVICE_FRAMEWORK" >&2 ; exit ;; @@ -97,19 +102,18 @@ usage: $0 [OPTIONS] list [] usage: $0 [OPTIONS] status [] status report run-time status for each driver in ups.conf + and call $ENUMERATOR + to list the mapping of service instances to device sections + and report run-time status for each one that was matched status (optionally return the status info only for one device) - Fields: UPSNAME UPSDRV PF_RUNNING PF_PID S_RESPONSIVE S_PID S_STATUS - (PF_* = according to PID file, if any; S_* = via socket protocol) + Fields: SVC_NAME SVC_STATE UPSNAME UPSDRV RUNNING PF_PID S_RESPONSIVE S_PID S_STATUS + (SVC_* according to service management framework; and + PF_* = according to PID file, if any; S_* = via socket + protocol -- as defined by upsdrvctl) usage: $0 [OPTIONS] show-config [] show-config output config section from ups.conf for device show-config ...or all devices if no argument was passed EOF - -# TODO: -# status call $ENUMERATOR -# to list the mapping of service instances to device sections -# and report run-time status for each one - } ACTION="" @@ -148,10 +152,81 @@ while [ $# -gt 0 ]; do fi ;; status) - # TODO: Add info about service units - echo "NOTE: Action '$1' is not implemented via services currently, will call upsdrvctl" >&2 RES=0 - "${UPSDRVCTL}" status $2 || RES=$? + STATUSES="`NUT_QUIET_INIT_BANNER=true "${UPSDRVCTL}" status $2`" || RES=$? + if [ x"${STATUSES}" = x ]; then + exit $RES + fi + + SVCINSTS="`$0 list $2`" || RES=$? + # Use a common approach below, even if we only got one instance name from list() + if [ -n "$2" ] && [ -n "${SVCINSTS}" ] ; then SVCINSTS="${SVCINSTS} $2" ; fi + + case "$SERVICE_FRAMEWORK" in + smf) SVC_STATE_WIDTH="11" ;; + systemd) SVC_STATE_WIDTH="23" ;; + esac + + echo "$STATUSES" | ( COUNT=0; while IFS='' read LINE ; do + COUNT="`expr $COUNT + 1`" + if [ "$COUNT" = 1 ] ; then + # Header line + printf '%-30s\t%'"${SVC_STATE_WIDTH}"'s\t%s\n' "SVC_NAME" "SVC_STATE" "$LINE" + continue + fi + DEV_NAME="`echo "$LINE" | awk '{print $1}'`" + SVC_NAME="`echo "$SVCINSTS" | awk '($NF == "'"${DEV_NAME}"'") {print $1}'`" && [ -n "$SVC_NAME" ] || SVC_NAME="N/A" + if [ x"$SVC_NAME" = "N/A" ]; then + SVC_STATE="N/A" + else + case "$SERVICE_FRAMEWORK" in + smf) SVC_STATE="`$CMDREPORT -Hostate ${SVC_NAME} 2>/dev/null`" \ + && [ -n "$SVC_STATE" ] && [ 1 = `echo "$SVC_STATE" | wc -l` ] \ + || SVC_STATE="N/A" + ;; + systemd) SVC_STATE="" + CMDRES=0 + OUT="`$CMDREPORT is-enabled "${SVC_NAME}" 2>/dev/null`" || CMDRES=$? + if [ x"$OUT" != x ] ; then SVC_STATE="${OUT}" ; else + [ 0 = "$CMDRES" ] && SVC_STATE="enabled" || SVC_STATE="disabled" + fi + # At this point we should have some value, so others below + # may be or not be suffixed with a comma. + + # The masked unit state has no separate query in some (all?) + # systemd versions and can be reported as part of the above + CMDRES=0 + OUT="`$CMDREPORT is-masked "${SVC_NAME}" 2>/dev/null`" || CMDRES=$? + if [ x"$OUT" != x ] ; then + if [ x"${SVC_STATE}" != xmasked ] && [ x"${SVC_STATE}" != x"${OUT}" ] ; then + SVC_STATE="${SVC_STATE},${OUT}" + fi + else + [ 0 = "$CMDRES" ] && SVC_STATE="${SVC_STATE},masked" || true + fi + + CMDRES=0 + OUT="`$CMDREPORT is-active "${SVC_NAME}" 2>/dev/null`" || CMDRES=$? + if [ x"$OUT" != x ] ; then + SVC_STATE="${SVC_STATE},${OUT}" + else + [ 0 = "$CMDRES" ] && SVC_STATE="${SVC_STATE},active" || true + fi + + CMDRES=0 + OUT="`$CMDREPORT is-failed "${SVC_NAME}" 2>/dev/null`" || CMDRES=$? + if [ x"$OUT" != x ] ; then + if [ x"${OUT}" != xactive ] && [ x"${OUT}" != xinactive ] ; then + SVC_STATE="${SVC_STATE},${OUT}" + fi + else + [ 0 = "$CMDRES" ] && SVC_STATE="${SVC_STATE},failed" || true + fi + ;; + esac + fi + printf '%-30s\t%'"${SVC_STATE_WIDTH}"'s\t%s\n' "$SVC_NAME" "$SVC_STATE" "$LINE" + done ) exit $RES ;; start|stop) From 30cec0d76b1ab41ac3c8c5ce3c405aff6b6dd051 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 12:16:00 +0200 Subject: [PATCH 15/16] scripts/upsdrvsvcctl/upsdrvsvcctl.in: allow caller to customize ENUMERATOR and UPSDRVCTL when testing [#2567] Signed-off-by: Jim Klimov --- scripts/upsdrvsvcctl/upsdrvsvcctl.in | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/upsdrvsvcctl/upsdrvsvcctl.in b/scripts/upsdrvsvcctl/upsdrvsvcctl.in index 40c2709725..8a70fe34d2 100755 --- a/scripts/upsdrvsvcctl/upsdrvsvcctl.in +++ b/scripts/upsdrvsvcctl/upsdrvsvcctl.in @@ -36,19 +36,18 @@ CMDARG="" # Note: some frameworks separate the commands for acting and for reporting CMDREPORT="" CMDREPORTARG="" -ENUMERATOR="" case "$SERVICE_FRAMEWORK" in smf) CMD="/usr/sbin/svcadm" CMDREPORT="/usr/bin/svcs" - ENUMERATOR="@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" + [ -n "${ENUMERATOR-}" ] && export ENUMERATOR || ENUMERATOR="@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" ;; systemd) CMD="/bin/systemctl" CMDREPORT="${CMD}" - ENUMERATOR="@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" + [ -n "${ENUMERATOR-}" ] && export ENUMERATOR || ENUMERATOR="@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" ;; *) echo "Unrecognized SERVICE_FRAMEWORK: $SERVICE_FRAMEWORK" >&2 ; exit ;; esac -UPSDRVCTL='@SBINDIR@/upsdrvctl' +[ -n "${UPSDRVCTL-}" ] && export UPSDRVCTL || UPSDRVCTL='@SBINDIR@/upsdrvctl' PACKAGE_VERSION='@PACKAGE_VERSION@' usage() { From 5d4338b309b1a88d9efd4fc1703b04424973c948 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 1 Aug 2024 12:16:33 +0200 Subject: [PATCH 16/16] scripts/upsdrvsvcctl/upsdrvsvcctl.in: adjust SVC_NAME_WIDTH to framework type too [#2567] Signed-off-by: Jim Klimov --- scripts/upsdrvsvcctl/upsdrvsvcctl.in | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/upsdrvsvcctl/upsdrvsvcctl.in b/scripts/upsdrvsvcctl/upsdrvsvcctl.in index 8a70fe34d2..5079b764a7 100755 --- a/scripts/upsdrvsvcctl/upsdrvsvcctl.in +++ b/scripts/upsdrvsvcctl/upsdrvsvcctl.in @@ -161,16 +161,18 @@ while [ $# -gt 0 ]; do # Use a common approach below, even if we only got one instance name from list() if [ -n "$2" ] && [ -n "${SVCINSTS}" ] ; then SVCINSTS="${SVCINSTS} $2" ; fi + # Accomodate svc:/system/power/nut-driver:dummy (SMF) + # or nut-driver@dummy.service (systemd) case "$SERVICE_FRAMEWORK" in - smf) SVC_STATE_WIDTH="11" ;; - systemd) SVC_STATE_WIDTH="23" ;; + smf) SVC_NAME_WIDTH="39" ; SVC_STATE_WIDTH="11" ;; + systemd) SVC_NAME_WIDTH="30" ; SVC_STATE_WIDTH="23" ;; esac echo "$STATUSES" | ( COUNT=0; while IFS='' read LINE ; do COUNT="`expr $COUNT + 1`" if [ "$COUNT" = 1 ] ; then # Header line - printf '%-30s\t%'"${SVC_STATE_WIDTH}"'s\t%s\n' "SVC_NAME" "SVC_STATE" "$LINE" + printf '%-'"${SVC_NAME_WIDTH}"'s\t%'"${SVC_STATE_WIDTH}"'s\t%s\n' "SVC_NAME" "SVC_STATE" "$LINE" continue fi DEV_NAME="`echo "$LINE" | awk '{print $1}'`" @@ -224,7 +226,7 @@ while [ $# -gt 0 ]; do ;; esac fi - printf '%-30s\t%'"${SVC_STATE_WIDTH}"'s\t%s\n' "$SVC_NAME" "$SVC_STATE" "$LINE" + printf '%-'"${SVC_NAME_WIDTH}"'s\t%'"${SVC_STATE_WIDTH}"'s\t%s\n' "$SVC_NAME" "$SVC_STATE" "$LINE" done ) exit $RES ;;