Hey folks, I run lots of machines on several UPSes (using sysutils/nut-* from pkgsrc). When mains fail and the UPS gets low on battery, the ups daemon shuts down the machine.
So far all fine. But there is a (tiny) race when mains return after the machine powered off but the UPS did not run out of battery. This is usually dealt with by telling the UPS to shutdown after a delay, which means: systems goes down, after running all shutdown hooks it tells the ups to restart in N seconds and then filesystems are unmounted and the machine shuts down. When the counter in the UPS reaches 0 it powers of its output and restarts. After restart it will only switch on its output if main power is back. This way the machine controlling the UPS will safely boot even if main power returns during the shutdown process. This is all fine, and documented best practices for many kind of UPSes. However, it is tricky to implement in NetBSD. I considered doing kernel/init tricks but then played around a bit and came to a *very* simple solution that works amazingly good. I also (ab)use it for a minor variant of above sheme in one setup: I have a machine that is alone on its UPS but has a few peripheral devices. Whenever I shut down that machine (manually or as described above via the nut ups daemon) I want the UPS to shut off all outputs (where the peripherals are connected). So here it is about all-power-off instead of safe-reboot when mains return. The trick is the small patch below that modifies /sbin/shutdown to pass an argument to /etc/rc.shutdown telling it about further actions: either halt, powerdown or reboot. On reboot or halt (goint single user) we don't want to trigger the UPS toggle countdown, or even power off all outputs. Besides this /sbin/shutdown patch we could enhance /etc/rc.shutdown to conditionally read an additional script that contains the local stuff, so the system provided script would not need to be modified for this kind of customizations. But that is trivial and a second step. So here the first step first. What do you think? Martin Index: shutdown.c =================================================================== RCS file: /cvsroot/src/sbin/shutdown/shutdown.c,v retrieving revision 1.58 diff -u -r1.58 shutdown.c --- shutdown.c 1 Jul 2022 16:45:12 -0000 1.58 +++ shutdown.c 28 Nov 2024 19:16:13 -0000 @@ -47,6 +47,7 @@ #include <sys/time.h> #include <sys/resource.h> #include <sys/syslog.h> +#include <sys/wait.h> #include <ctype.h> #include <err.h> @@ -518,10 +519,38 @@ static void dorcshutdown(void) { + const char *arg = NULL; + static char cmd[sizeof(_PATH_RCSHUTDOWN) + 64]; + int status; + + if (doreboot) + arg = "reboot"; + else if (dopowerdown) + arg = "powerdown"; + else if (dohalt) + arg = "halt"; + if (arg) { + snprintf(cmd, sizeof(cmd), "set -- %s && . " _PATH_RCSHUTDOWN, + arg); + arg = cmd; + } else { + arg = ". " _PATH_RCSHUTDOWN; + } + (void)printf("\r\nAbout to run shutdown hooks...\r\n"); #ifndef DEBUG (void)setuid(0); - (void)system(". " _PATH_RCSHUTDOWN); + status = system(arg); + if (status == -1) { + fprintf(stderr, "failed to run " _PATH_RCSHUTDOWN ", error %d\n", + errno); + exit(1); + } + if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { + fprintf(stderr, _PATH_RCSHUTDOWN " exited with status %d\n", + WEXITSTATUS(status)); + exit(1); + } #endif (void)sleep(5); /* Give operator a chance to abort this. */ (void)printf("\r\nDone running shutdown hooks.\r\n");