From ad1e78a22dc91b70bee871425cd5f74bb3c34c9f Mon Sep 17 00:00:00 2001 From: Laurent Bigonville Date: Thu, 21 Nov 2013 17:52:16 +0100 Subject: [PATCH] Provide retry options for upsdrvctl and driver(s) As recently seen in Debian (bugs #694717 and #677143), it may be required to have upsdrvctl retrying to start the driver in case of failure. More specifically, a mix of init system (V and systemd), udev and USB device(s) can result in the /dev entry not being available at driver startup, thus resulting in a general failure to start NUT. This commit provides at least a way to overcome this issue. A more suitable solution will require more work on NUT design. This patch if based on Arnaud Quette proposal --- docs/man/ups.conf.txt | 15 +++++++++++++++ docs/man/upsdrvctl.txt | 3 ++- drivers/upsdrvctl.c | 37 ++++++++++++++++++++++++++++++++++--- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/docs/man/ups.conf.txt b/docs/man/ups.conf.txt index 8a72a83..4877852 100644 --- a/docs/man/ups.conf.txt +++ b/docs/man/ups.conf.txt @@ -58,6 +58,21 @@ directory, which is often /usr/local/ups/bin. Optional. Same as the UPS field of the same name, but this is the default for UPSes that don't have the field. +*maxretry*:: +Optional. Specify the number of attempts to start the driver(s), in case of +failure, before giving up. A delay of 'retrydelay' is inserted between each +attempt. Caution should be taken when using this option, since it can +impact the time taken by your system to start. ++ +The default is 1 attempt. + +*retrydelay*:: +Optional. Specify the delay between each restart attempt of the driver(s), +as specified by 'maxretry'. Caution should be taken when using this option, +since it can impact the time taken by your system to start. ++ +The default is 5 seconds. + *pollinterval*:: Optional. The status of the UPS will be refreshed after a maximum diff --git a/docs/man/upsdrvctl.txt b/docs/man/upsdrvctl.txt index f19fd77..52e5509 100644 --- a/docs/man/upsdrvctl.txt +++ b/docs/man/upsdrvctl.txt @@ -63,7 +63,8 @@ Without that argument, they operate on every UPS that is currently configured. *start*:: -Start the UPS driver(s). +Start the UPS driver(s). In case of failure, further attempts may be executed +by using the 'maxretry' and 'retrydelay' options - see linkman:ups.conf[5]. *stop*:: Stop the UPS driver(s). diff --git a/drivers/upsdrvctl.c b/drivers/upsdrvctl.c index 623f753..0cdaa2c 100644 --- a/drivers/upsdrvctl.c +++ b/drivers/upsdrvctl.c @@ -45,6 +45,12 @@ /* timer - keeps us from getting stuck if a driver hangs */ static int maxstartdelay = 45; + /* counter - retry that many time(s) to start the driver if it fails to */ +static int maxretry = 1; + + /* timer - delay between each restart attempt of the driver(s) */ +static int retrydelay = 5; + /* Directory where driver executables live */ static char *driverpath = NULL; @@ -65,6 +71,12 @@ void do_upsconf_args(char *upsname, char *var, char *val) driverpath = xstrdup(val); } + if (!strcmp(var, "maxretry")) + maxretry = atoi(val); + + if (!strcmp(var, "retrydelay")) + retrydelay = atoi(val); + /* ignore anything else - it's probably for main */ return; @@ -248,6 +260,7 @@ static void start_driver(const ups_t *ups) char *argv[8]; char dfn[SMALLBUF]; int ret, arg = 0; + int initial_exec_error = exec_error, drv_maxretry = maxretry; struct stat fs; upsdebugx(1, "Starting UPS: %s", ups->upsname); @@ -276,10 +289,28 @@ static void start_driver(const ups_t *ups) /* tie it off */ argv[arg++] = NULL; - debugcmdline(2, "exec: ", argv); - if (!testmode) { - forkexec(argv, ups); + while (drv_maxretry > 0) { + int cur_exec_error = exec_error; + + upsdebugx(2, "%i remaining attempts", drv_maxretry); + debugcmdline(2, "exec: ", argv); + drv_maxretry--; + + if (!testmode) { + forkexec(argv, ups); + } + + /* driver command succeeded */ + if (cur_exec_error == exec_error) { + drv_maxretry = 0; + exec_error = initial_exec_error; + } + else { + /* otherwise, retry if still needed */ + if (drv_maxretry > 0) + sleep (retrydelay); + } } } -- 1.8.4