networkupstools / nut

The Network UPS Tools repository. UPS management protocol Informational RFC 9271 published by IETF at https://www.rfc-editor.org/info/rfc9271 Please star NUT on GitHub, this helps with sponsorships!
https://networkupstools.org/
Other
2.09k stars 351 forks source link

Windows port #5

Open aquette opened 11 years ago

aquette commented 11 years ago

Complete port of NUT to MS Windows.

Alioth feature request: https://alioth.debian.org/tracker/index.php?func=detail&aid=302177&group_id=30602&atid=411545

aquette commented 11 years ago
clepple commented 11 years ago

I did a regression check on whether the code compiles for non-Windows systems, and it looks like there is an issue with this trunk merge to 2.6.5: 6e51ff2bd

The code still compiles, but the documentation does not: http://buildbot.networkupstools.org/public/nut/builders/Ubuntu-natty-x86/builds/322/steps/compile/logs/stdio

It looks like it is related to the change to the formatting of the INSTALL file, but it is possible that another related change did not get pulled in from the trunk.

clepple commented 11 years ago

I think the INSTALL file was overwritten by mistake during that merge with the standard GNU installation instructions:

http://trac.networkupstools.org/projects/nut/browser/branches/windows_port/INSTALL?rev=3714 versus http://trac.networkupstools.org/projects/nut/browser/trunk/INSTALL?rev=3857

I'm going to copy over the INSTALL from master, and apply this commit: http://trac.networkupstools.org/projects/nut/changeset/2958/ (but @bohe-eaton feel free to update the instructions if they are out of date)

intrudo commented 8 years ago

Hello!

A few weeks ago I have successfully built NUT in MinGW environment using Windows-v2.6.5-7 branch (you may find a manual I have posted to nut-upsdev mailing list).

I would like to help with Windows port development. I have examined the source code of several components (upsd, upsmon, nut) and I have got some ideas. I will be happy to share them with NUT team.

MAINTAINER UPDATE: Links to those mail posts in the archive:

aquette commented 8 years ago

hi @intrudo , thanks for jumping in. The best for now is probably to present your ideas here and / or to fork a branch of the Windows one and start applying these ideas.

see also ntUPSd - Network UPS Tools Services for Windows (https://github.com/6XGate/ntUPSd) from @6XGate

thx and cheers

intrudo commented 8 years ago

Hi @aquette ! Let’s start =)

First of all, I would like to say that I am not going to do something without acceptance of NUT Team (it would be great to coordinate high-level tasks at least). Please, feel free to say "no", if something is out of your vision/roadmap.

I sent a message with my ideas to nut-upsdev list and discussed some points with @clepple already. You may find there more details why I think so. So, here are just short descriptions.

  1. Produce cross-compiled source code to build *nix, x86 and x64 versions from single sources. Linux environment must be used only.
  2. Make a software abstraction layer (SAL) to hide all functions’ calls like "system", "syslog", etc. Windows version will have special implementation of them, nix will translate the calls to standard ones. For example, no one will call "system" directly, but "nut_system" only. "nut_system" will call standard "system" function for nix and "CreateProcess" for Windows. As result of such abstraction, we will have common program flow in source code without platform specific conditions like #ifdef.
  3. Improve logging for Windows version and use file-based log instead of Windows Event Log. Moreover, standard syslog should be supported in Windows version too (to have equal behavior with *nix version).
  4. Eliminate NUT service in Windows version (it provides "syslogd" and "initd" functions). Implement every daemons (upsmon, upsd) as Windows services. Here is open question what to do with drivers; it looks quite redundant to port every driver as service. I think to create additional Windows service (or extend upsdrvctrl program) to control them (start, stop, etc.), so, they can stay as console applications.

Sure, I see many minor tasks, but let us discuss them later.

The one thing I worry is branch. I was going to fork Windows-v2.6.5-7 branch and completing Windows port there. @clepple thinks that Windows-v2.6.5-7 should be rebased before. Unfortunately, I am not GIT expert and I am afraid that there will be many conflicts during rebase. Moreover, I think that my commits will be quite massive. What is the best way to synchronize master branch and my repository? I will be glad to get any hint.

I have looked into sources of ntUPSd project. They are complete different from NUT one, but maybe I will find something useful there.

Thanks!

clepple commented 8 years ago

The best for now is probably to present your ideas here and / or to fork a branch of the Windows one and start applying these ideas.

@aquette for reference, here is the previous nut-upsdev discussion: http://lists.alioth.debian.org/pipermail/nut-upsdev/2016-April/007172.html

Let me know if you have further questions about why I don't think a major Windows development effort should be based on that branch. The architectural changes that @intrudo proposed are probably best done by using Windows-v2.6.5-7 as a guide. For instance, there are a lot of #ifdef WIN32 blocks that will get even messier if 64-bit #ifdefs are needed. Also, a lot of drivers have been added or changed substantially since 2.6.x, and I think it would be best to re-port them using the proposed APIs and coding style.

intrudo commented 8 years ago

Well, if you do not mind, I will fork Windows-v2.6.5-7 branch to implement the SAL. After it is completed, we could get back to the question of proper branching and future merge.

clepple commented 8 years ago

Well, if you do not mind, I will fork Windows-v2.6.5-7 branch to implement the SAL. After it is completed, we could get back to the question of proper branching and future merge.

Agreed. For prototyping, I do not mind development on that branch (or a branch of the branch, given that branches are cheap in Git). I just want to make sure that everyone understands that merging (in the Git sense of the word, where two lines of history connect back to each other) any of the old Windows-* branches back into master will be a train wreck due to the leftover translated SVN cruft, plus the rebasing issues. (We cleaned up most of the SVN cruft in master with reposurgeon, and spent a lot of time verifying correctness against old tarballs, but we did not do the same to the Windows branches.)

I think a lot of the Windows-specific files from the branch could be copied over to a new Windows branch that starts from either v2.7.4 or master, though I think that they would change a bit for the proposed reduction of #ifdef WIN32 blocks.

mrudat commented 8 years ago

Depending on which library is being used for the windows port, would it not already either include a system and syslog function, or not include either, and thus allow us to write a replacement function also removing the need for different code for each platform.

aquette commented 7 years ago

Adding a user provided doc on "How to build NUT Windows Port": http://lists.alioth.debian.org/pipermail/nut-upsdev/2016-April/007171.html

jimklimov commented 2 years ago

After a few years' general hiatus on this subect, I tried to revive the changes from Windows-2.6.5-7 branch against current NUT master and came up with https://github.com/networkupstools/nut/tree/Windows-v2.8.0-1 which then got fixed (of accumulated typos and other merge conflicts) into passing builds on Linux (non-regression, testing more with CI farm now) but would need a Windows build env setup to see if the actual ifdef Win32 codebase still works.

The "just for fun" bit turned into half-a-week-long git rebase spree, trying to not distract... fat chance :)

jimklimov commented 2 years ago

Building the NUT 2.8.0 based branch natively on Windows proved problematic, mainly due to the hassle of sourcing a reliable MinGW environment (there are many variants and packagings, including CygWin and MSYS2) as well as lack of MinGW for Linux on a box I tried. But also because it is much more strict in default compiler settings, and notable differences between the original NUT-for-Windows effort and current codebases over time.

Now I tried to take a step back to and expected-working solution, to build the 2.6.5-based codebase according to its instructions in scripts/Windows/README file, and results are quite promising.

I found that Ubuntu 21.10 does ship a usable mingw environment, so built much of NUT and its dependencies for MinGW in an LXC container and rsync'ed files to a Windows 7 machine to test.

The resulting updated code and instructions are at https://github.com/networkupstools/nut/tree/Windows-v2.6.5-8 branch (specific state that "works for me" is tagged as https://github.com/networkupstools/nut/releases/tag/Windows-v2.6.5-8 now).

Early starts were with 32-bit MinGW (i686) and seemed to compile but I did not test them. Later attempts involved 64-bit target for dependencies (libregex, libusb-1.0 and libusb-0.1-compat for the old codebase, libnetsnmp...) and NUT.

I did successfully test an Eaton-ECO USB UPS as far as seeing its identification; but the state was bogus (OB all the time) and not much more data was exposed. Currently I did not care to test much if that is a NUT 2.6.5 issue (old codebase), or Windows porting issue, or even if the UPS (with no load) was just asleep. Replugging the wall cord did not help at least ;)

This particular deployment began throwing libusb interrupts errors soon after driver init, and the "pollonly" driver option helped avoid that (in hindsight, I'm not sure if the info updates are really happening though, or it was stuck with what it saw at init time).

The nut-scanner succeeded to find the USB UPS, as well as scan the network for a NUT server and report the devices configured on it - so confirming that libltdl magic is also usable. It however relies on suffixed DLL names like libusb-0-1-4 (without the .dll extension) which for now are hardcoded in the WIN32-specific source code section. I suppose installing those libs with standard names (per respective libname string variable) might work and would be more simple then.

The net-snmp sources refused to build a DLL, so it is statically linked into nut-scanner and snmp-ups drivers. No idea how well it works beyond that, as I have no devices to test against at the moment. In particular, nut-scanner seems to have frozen when scanning even one IP address (-s and -e with same value). Maybe that is about timeouts waiting for reply (should be 30 sec IIRC, so a much longer freeze is strange) or due to "non-LTDL shim" to use the library methods (just assigning our pointers to method names, without lt_dlsym in a WIN32 build).

I did not yet look at the Installer or DriverInstaller directories, just hacked into the build-mingw-nut.sh a few lines to copy a few dependency DLLs (custom built prereqs and winpthread) into the nut_install/bin directory which I rsync'ed to the Windows machine.

FWIW, binaries produced for this experiment are archived at https://github.com/networkupstools/nut/releases/download/Windows-v2.6.5-8/nut_install-2.6.5-8-gd1b8d14d1.zip

DorCoMaNdO commented 2 years ago

Funnily enough I starred and watched this repo yesterday while doing my own NUT on Windows testing, after discovering the latest msi installer wouldn't properly finish installation, I looked it up and came across similar reports, and a workarounds here and here

I actually did manage to get it working in standalone mode, and I did also have libusb interrupt errors for which I enabled pollonly to dismiss them given that information was still being updated, and yes I did confirm that the UPS state was being updated, at least as far as usage and estimated time on battery goes (edit: I forgot the input/output voltage and frequency also updated within the +/-1 variance), I had pollinterval at 1 and pollfreq as the default 30. I did end up scrapping NUT and going back to the default MGE UPS driver as, aside from the issues, NUT was lacking a few features I was hoping to utilize, specifically performing shutdown based on battery percentage and/or estimated runtime (whichever happens to be lower) but apparently my UPS (Eaton 5E 2000i USB) does not support those features ("low" stats)? So it would probably require a client to consume the data produced by NUT to offer those features. Ideally the Windows NUT driver would also be identified as a battery, just like the default MGE driver, to offer at least some of those features, using the default driver and the Windows battery settings in "power options" (performance profiles) I've set different battery percentage thresholds, performance throttling and more, however it still lacks the option to shutdown/hibernate based on runtime (which given usage, might be only a couple of minutes), which would be useful when leaving the system in some high-usage state such as 3D rendering overnight. Perhaps this could be implemented with a "dummy" driver that consumes information from UPS(es) and aggregates percentage/runtime, it could perform similar assumptions to DEADTIME among other things such as maintaining last known and estimating battery percentage loss to trigger Windows battery power options such as performance throttling, low/critical thresholds warning prompts prior to shutting down.

My reason for considering NUT on Windows is because I'm currently using it on an unRaid system with a similar UPS (Eaton 5E 1500i USB), for which the plugin does allow the system to shutdown based on estimated runtime remaining, and because the default MGE driver has a pretty annoying issue, it randomly disconnects momentarily for a second or two at random periods (usually no more than twice a day, and as little as once every few days), while the disconnection issue alone is not a huge problem, the events that it produces do, as Windows triggers multiple events when it stops recognizing a battery and recognizes one again, which leads to a brief (roughly 2-3 seconds) system hang/unresponsiveness. Since I'm not sure whether I can trust my NUT configuration to shut down my system in the event of a power loss, I was not able to test whether the UPS disconnects with the libusb/NUT driver (I had a stale data issue with unRaid at the default pollinterval of 2, but I came across a suggestion to change it to 1 and it's been fine for over a week now, so I'd expect no less on Windows either).

jimklimov commented 2 years ago

Thanks, lots of good ideas here though not sure who and when would follow up on these :)

Representing NUT in same way as a laptop battery for different systems (dbus, hal, windows, linux, mac....) does sound reasonable and did for years :) but for some reason nobody published such PRs. Given NUT's layered architecture, and the fact that the UPS may be connected elsewhere, it might be a client similar to upsmon hooking deep into a system. Especially if we get to consider multiple PSUs fed by numerous UPSes in general redundant case; not sure if OS battery facilities even have a concept for that across the board.

Finally note that existing NUT for Windows packages are based on 2.6.5 release codebase (and some later merges from trunk development) so evolution and bug fixes there fizzled out roughly in 2013. Lack of features and data points may just be due to that. I'm (slowly... but knowing now that the machinery is viable is a big boon to enthusiasm) struggling to get modern code to just build in similar manner, let alone function and be tested/testable :)

jimklimov commented 2 years ago

Regarding hibernation, power profile switching, etc. you might consider setting up some batch/powershell scripts as Task Scheduler event handlers, and then use upsmon NOTIFYCMD or eventually SHUTDOWNCMD to pull their strings. Not sure if upssched works on Windows but it might be useful for complicated logic too.

DorCoMaNdO commented 2 years ago

I do have SHUTDOWNCMD configured to a hibernation command, the issue is that I have absolutely no idea when it would trigger, since my UPS does not report and does not support configuring "low" battery percentage or runtime remaining, I don't know when NUT would decide to shut down the system, and when my system is high usage (100% GPU utilization + high CPU utilization, typically when rendering overnight or gaming) the UPS itself estimates up to 3 minutes of runtime as opposed to the lightweight/idle estimates of 15 to 25 minutes (based on HWiNFO64 readings from the MGE UPS driver), that's a problem because I have experienced periods where hibernation actually takes a few minutes to finalize, so unless the system knows to throttle and hibernate (nearly immediately in the high usage scenarios) there's potential for the UPS to run out of battery before the system can shut down in time, and that's especially true if NUT would trigger the shutdown command late (ex: 10~20% battery remaining), that's what I meant when I previously said "I'm not sure whether I can trust my NUT configuration", because I have no idea when/if it will send the shutdown command, or what it would do in the meantime before the shutdown command while the system is on battery.

jimklimov commented 2 years ago

FWIW, just now got the 2.8.0-based variant to build with mingw on Ubuntu! Pushed the branch so CI rolls it around other platforms and targets.

If anyone wants to give it a spin, routine should be like this:

:; git clone -b Windows-v2.8.0-1 https://github.com/networkupstools/nut nut-win
:; cd nut-win
:; ./autogen.sh && (cd scripts/Windows/ && ./bu*sh all64)

Note that prerequisites like libusb may be needed - requiring you build them for mingw - for a NUT build to be useful (more details in scripts/Windows/README). Resulting installed proto-tree would be in scripts/Windows/nut_install/ -- rsync that to a Windows box and should be sufficient (was with 2.6.5 based builds).

jimklimov commented 2 years ago

By now the 2.8.0-based NUT for Windows branch seems on par with its 2.6.5-based "origin" in terms of what and how I can build, and how it (mis-)behaves in a greenhouse build and operational environment - e.g. usbhid-ups and nut-scanner now see a connected device again, yay! but the driver crashes in cleanup routine after ctrl+c.

Not quite production ready for general availability, but still - a solid foundation again to research, tinker and improve (able hands welcome; I won't progress on it much further - especially not soon), so really a big achievement.

jimklimov commented 2 years ago

With recent push, build also works in MSYS2 (which also ships mingw) straight on Windows. Slooowly, but it passes, and lets see and fix a different set of bugs :) Notably, a different set of dependencies is easily available, e.g. "out of the box" can build for neon (xml), modbus and cgi but not for snmp.

The cat is off to vacations, mice can play :)

Announced on the mailing lists as:

jimklimov commented 2 years ago

@clepple : in discussions here and elsewhere a few years ago you mentioned that existing Windows branch should not be merged to master, but should rather be the basis/inspiration for separate work to provide NUT for Windows. The mailing-list discussion unfortunately fizzled, but it did provide a lot of interesting ideas and practical info (for build environment preparation).

My recent experiments were in fact started from that older branch, updating and fixing it for modern NUT codebase as well as CI. Do you still think it should not be merged "as is" (when CI works for all platforms, there is some effort needed still) so people can tinker with current NUT master on Windows to make it actually usable and packageable?

clepple commented 2 years ago

@jimklimov I admit I haven't followed your recent Windows work, but if you are truly proposing a merge of the old SVN-era Windows branch with no rebasing to remove all of the SVN "merge" issues (since SVN did not support actual merges at the time, we had a lot of regressions introduced on that branch by improper SVN usage), then I still think merging is a bad idea. We spent a lot of time carefully translating SVN trunk to Git master via reposurgeon, but that was production code, and many people have kicked the tires on Git master (and 2.7.x) since then. The same cannot be said for the original SVN Windows branch.

If I were more actively involved with NUT development these days, I would really not want to sift through all of the Windows experiments when triaging non-Windows bugs. It sounds like you are familiar enough with both the current POSIX code and the Windows code, so if you are up to the task, I don't want to get in your way. But this is another round of changes that makes me less confident in looking over PRs and approving them (at least without testing on real hardware, which I really don't have time to do).

jimklimov commented 2 years ago

Well, as far as codebase text merges go, I did sift through the changes keeping in mind the past two years of "fightwarn" and other improvements, so most of those Windows-related commits made some sense to me and/or were adapted to be "sort of rebased" (old metadata, somewhat new content after conflict resolution). Technically the effort was a cherry-pick of a few hundred commits over recent post-2.8.0 master codebase, I think (I had several starts addressing it all the while changing workstations, so not sure OTOH which attempt survived) :)

One thing of note, the discussion from 2016 mentioned that there can be a benefit to managing daemons and drivers as separate Windows services; the Windows-branch codebase did add and later deprecate such ability along the way. I am not sure about the reasons, but at least there is "existing" source code in history to revive, if someone wants to address that.

I hope that those compiler warnings and CI running many scenarios (and lots of iterations until all is green) would give a measure of confidence about non-regression at least for currently well-supported targets, and would allow the Windows related effort to not bit-rot again. Hopefully someone would walk the next mile to also shape it into a prime-time product.

Thinking of it, just comparing the current state of two Git branch tips to ignore blocks hidden by WIN32 macro, so looking at differences in POSIX code (if any) should also help weed out bugs and PR the improvements, if any, or confirm that there are no expected differences for Unix/Linux behavior.

jimklimov commented 2 years ago

With recent PRs merged, the Windows branch now includes master (and exceeds with almost no changes to POSIX part of the codebase), and beside our usual NUT CI and CircleCI multi-platform testing, it builds every commit with semi-natively (Windows MSYS2) on AppVeyor and makes a tarball so people can try it out:

Note that all envs (MSYS2, linux+mingw 32-bit and 64-bit) are all differently capable in terms of 3rd party deps provided. But at least it feels great to have explored so many viable options! ;)

As noted in the https://github.com/orgs/networkupstools/projects/2/views/1 project, there are still quite a few caveats and commented-away incomplete features which make Windows builds not "prime-time" for each and every use, but at least as built, it should already cover many of the typical bases like USB, ModBus, (maybe less so for Serial ports), NetXML and SNMP... Volunteers to code the missing bits and especially to test against real HW are quite needed at this point.

jimklimov commented 2 years ago

Posted the PR above (#1611) which I hope will take care of merging the codebases, so existing NUT for Windows effort becomes part of "master" branch. This does not mean the end of the journey, as there are at least the existing disabled and questionable parts of code (see project referenced above) to take care of, for the added platform to be "on par" with the POSIX builds feature-wise, and packaging which was not revived yet but is something valuable to end-users on that platform. And the multitude of supported build environments currently gives a choice of "either these or those" third-party libraries and so NUT capabilities, something to level out as well.

chocmake commented 8 months ago

Just wondering with these builds, if set up as a NUT client, what is meant to be run for it to monitor the server in the background?

On Linux when the NUT packages are installed it auto starts a upsmon background process that keeps track of the server, and from my brief check of the old (~2013) official Windows installer it sets up a background service that presumably does the same thing.

I notice there's a upsmon binary in the sbin directory that picks up on the config files in etc but unsure if the idea is that is meant to be left open in a CMD window or what the proper binary to leave open would actually be (if there isn't any binary that can be opened as a background process).


Edit: tested a forced shutdown via the server and indeed the upsmon window left open detected it and shutdown the system (had previously edited the SHUTDOWNCMD to be the Windows equivalent of shutdown /s).

Hopefully in the future there could be a version that could be started as a window-less background process (or if there's a way to do this currently I'd be interested).

jimklimov commented 8 months ago

Thanks for the confirmation it works at least so :)

Looking at the scripts/Windows/Installer sources (should be of same provenance as what made the 2013 packages), there seems to be StartService.bat and StopService.bat mentioned all over the place in the XML file. Haven't made much more sense of it yet, and nobody else contributed so far, so the pretty installation in Windows is in limbo :\

Those files just go net start "Network UPS Tools" or net stop "Network UPS Tools" so apparently the service is registered somewhere along the way :)

There seems to be a services/Windows/wininit.c which wraps the starting of daemons and installation of the service in the later iterations. As I was importing the old code cherry-pick by cherry-pick, similar abilities appeared and disappeared in daemons themselves - so apparently predecessors on that fork deemed it better at some time to wrap a single unit for "everything NUT" rather than separately upsd (+ drivers?) and upsmon.

According to the Makefile.am there, this program should get built as nut.exe. And also nearby, a halt.c => halt.exe to drive the poweroff via Windows API.

With the above in mind, in the XML file for Windows packaging I found this block:

        <!-- To Add Service in Service Table-->
        <!--Register NUT Service -->
        <CustomAction Id="RegisterNUTService" FileKey="nut.exe" ExeCommand="-I" Execute="deferred" Return="asyncNoWait" HideTarget="no" Impersonate="no" />
        <!--Stop Service-->
        <CustomAction Id="StopService" FileKey="StopService.bat" ExeCommand="" Execute="deferred" Return="asyncNoWait" HideTarget="no" Impersonate="no" />
        <!--UnRegister NUT Service -->
        <CustomAction Id="DeRegisterNUTService" FileKey="nut.exe" ExeCommand="-U" Execute="deferred" Return="asyncNoWait" HideTarget="no" Impersonate="no" />

and the SVCNAME mentioned in wininit.c (aka "Network UPS Tools" mentioned in those batch files) is tucked away in common.h so was not an easy git grep hit.

chocmake commented 8 months ago

Thanks for the confirmation it works at least so :)

:) Tbh I'm not familiar with it all know how I'd be able to modify the source but I did try nut.exe -I (seen in the XML above) to see if that would register the service but it produced the error:

OpenSCManager failed (5)

Apart from registered Windows services I know there are some executables that run as UI-less processes. One such is display-switch (a clever pseudo KVM) which when opened by itself just remains open in the background until terminated, but when opened via console window will print out config info.

Though given there are already some code basis for the Windows service approach I suppose that makes more sense to get functional.

jimklimov commented 8 months ago

OpenSCManager failed (5)

Just in case: did you do it from a "Run as Administrator" sort of CMD session?

chocmake commented 8 months ago

Oh wow, I feel silly. Running the console as admin made it successfully install. Nice!

It auto detected my forced shutdown signal from the server and shutdown as expected.

Great to have this native port, since I tried WinNUT-Client first but it failed to detect FSD signals sent from the server and had various disconnects that wouldn't auto re-connect. Thanks for the work you've put into it :+1:

chocmake commented 8 months ago

Just for clarification, is SHUTDOWNCMD on the Windows build meant to support arbitrary CLI commands? As I've been attempting to point it to a batch script but so far no luck.

Also tried as one of the tests just SHUTDOWNCMD "notepad" to see if it'd launch Notepad but it didn't, which leads me to think it's expecting a regular shutdown value (since shutdown /s does work). Wondering if the halt.exe mentioned above is what the value is actually parsed and sent to.

Reason being is since I have my server configured to always issue a FSD signal when on battery due to the unorthodox behaviour of my UPS and afaict from the docs an FSD basically directly triggers SHUTDOWNCMD following the FINALDELAY.

jimklimov commented 8 months ago

No firm idea, but I think as a service it has no easy access to a desktop session, so probably notepad won't work. Batch files... maybe? By full pathname with extension and possible double-slash (or Unix slash) magic?..

chocmake commented 8 months ago

Hmm, perhaps I should make it a separate issue but here are various things I've tried with no effect:

SHUTDOWNCMD "shutdown /l" [no effect, this is the logout argument on Windows] SHUTDOWNCMD "notepad.exe" [to see if it could execute any Windows CLI paths] SHUTDOWNCMD "cmd.exe /c "C:\Portable\NUT\etc\script.bat"" SHUTDOWNCMD "/etc/script.bat"

Also tried instead using upssched-cmd but I think the issue is everything is being interpreted as Linux-native commands and there's no way I'm aware of to 'break out' of the Linux interpreter to execute native Windows commands.

shutdown /s which works must just be being interpreted coincidentally as the Linux equivalent (not sure what the /s argument would be interpreted as since only familiar with -h and -t in Linux).

Goal with a script is I need to run commands to close any open VMs then hibernate my system when the FSD signal is detected, since a vanilla shutdown wouldn't handle all scenarios.

jimklimov commented 8 months ago

My guess would be that Windows slashes must be doubled (so as to be escaped), and if quotes are deemed needed - also escaped. So purely parser-wise these would be more correct:

SHUTDOWNCMD "cmd.exe /c \"C:\\Portable\\NUT\\etc\\script.bat\""
SHUTDOWNCMD "cmd.exe /c C:\\Portable\\NUT\\etc\\script.bat"

Maybe explicit cmd is not required (should be the OS-registered handler for batch files)... coupling with Unix-style slash support (in modern Windows), maybe this would work:

SHUTDOWNCMD "C:/Portable/NUT/etc/script.bat"

Edit: tested a forced shutdown via the server and indeed the upsmon window left open detected it and shutdown the system (had previously edited the SHUTDOWNCMD to be the Windows equivalent of shutdown /s).

I was under the impression that you did nail the syntax for it? Or did it only work for "interactive" upsmon in a window?

...not sure what the /s argument would be interpreted as...

Check shutdown /? on your system, its set of options varied over editions; e.g. from what I found:

Usage: shutdown [/i | /l | /s | /sg | /r | /g | /a | /p | /h | /e | /o] [/hybrid] [/soft] [/fw] [/f]
    [/m \\computer][/t xxx][/d [p|u:]xx:yy [/c "comment"]]

    No args    Display help. This is the same as typing /?.
    /?         Display help. This is the same as not typing any options.
    /i         Display the graphical user interface (GUI).
               This must be the first option.
    /l         Log off. This cannot be used with /m or /d options.
    /s         Shutdown the computer.
    /sg        Shutdown the computer. On the next boot, if Automatic Restart Sign-On
               is enabled, automatically sign in and lock last interactive user.
               After sign in, restart any registered applications.
    /r         Full shutdown and restart the computer.
    /g         Full shutdown and restart the computer. After the system is rebooted,
               if Automatic Restart Sign-On is enabled, automatically sign in and
               lock last interactive user.
               After sign in, restart any registered applications.
    /a         Abort a system shutdown.
               This can only be used during the time-out period.
               Combine with /fw to clear any pending boots to firmware.
    /p         Turn off the local computer with no time-out or warning.
               Can be used with /d and /f options.
    /h         Hibernate the local computer.
               Can be used with the /f option.
    /hybrid    Performs a shutdown of the computer and prepares it for fast startup.
               Must be used with /s option.
    /fw        Combine with a shutdown option to cause the next boot to go to the
               firmware user interface.
    /e         Document the reason for an unexpected shutdown of a computer.
    /o         Go to the advanced boot options menu and restart the computer.
               Must be used with /r option.
    /m \\computer Specify the target computer.
    /t xxx     Set the time-out period before shutdown to xxx seconds.
               The valid range is 0-315360000 (10 years), with a default of 30.
               If the timeout period is greater than 0, the /f parameter is
               implied.
    /c "comment" Comment on the reason for the restart or shutdown.
               Maximum of 512 characters allowed.
    /f         Force running applications to close without forewarning users.
               The /f parameter is implied when a value greater than 0 is
               specified for the /t parameter.
    /d [p|u:]xx:yy  Provide the reason for the restart or shutdown.
               p indicates that the restart or shutdown is planned.
               u indicates that the reason is user defined.
               If neither p nor u is specified the restart or shutdown is
               unplanned.
               xx is the major reason number (positive integer less than 256).
               yy is the minor reason number (positive integer less than 65536).

Reasons on this computer:
(E = Expected U = Unexpected P = planned, C = customer defined)
Type    Major   Minor   Title

 U      0       0       Other (Unplanned)
E       0       0       Other (Unplanned)
E P     0       0       Other (Planned)
 U      0       5       Other Failure: System Unresponsive
E       1       1       Hardware: Maintenance (Unplanned)
E P     1       1       Hardware: Maintenance (Planned)
E       1       2       Hardware: Installation (Unplanned)
E P     1       2       Hardware: Installation (Planned)
E       2       2       Operating System: Recovery (Unplanned)
E P     2       2       Operating System: Recovery (Planned)
  P     2       3       Operating System: Upgrade (Planned)
E       2       4       Operating System: Reconfiguration (Unplanned)
E P     2       4       Operating System: Reconfiguration (Planned)
  P     2       16      Operating System: Service pack (Planned)
        2       17      Operating System: Hot fix (Unplanned)
  P     2       17      Operating System: Hot fix (Planned)
        2       18      Operating System: Security fix (Unplanned)
  P     2       18      Operating System: Security fix (Planned)
E       4       1       Application: Maintenance (Unplanned)
E P     4       1       Application: Maintenance (Planned)
E P     4       2       Application: Installation (Planned)
E       4       5       Application: Unresponsive
E       4       6       Application: Unstable
 U      5       15      System Failure: Stop error
 U      5       19      Security issue (Unplanned)
E       5       19      Security issue (Unplanned)
E P     5       19      Security issue (Planned)
E       5       20      Loss of network connectivity (Unplanned)
 U      6       11      Power Failure: Cord Unplugged
 U      6       12      Power Failure: Environment
  P     7       0       Legacy API shutdown
chocmake commented 8 months ago

My guess would be that Windows slashes must be doubled (so as to be escaped), and if quotes are deemed needed - also escaped. So purely parser-wise these would be more correct:

So tested these three (edited upsmon.conf, restarted NUT service in case it needed to be to detect the config changes, then sent FSD) but none had any effect (technically the one effect the FSD signal always has for the NUT client is stopping the Windows NUT service, since I suppose it assumes a shutdown has commenced and it no longer needs to be running).

I was under the impression that you did nail the syntax for it? Or did it only work for "interactive" upsmon in a window?

Only shutdown /s works, both when I originally launched upsmon or when using the background service that I later installed. It just happened to be the first command I tried since it was the Windows equivalent. Whereas other Windows arguments I've tried since like shutdown /l have no effect (nor other commands like those listed in my prior post).

Check shutdown /? on your system, its set of options varied over editions; e.g. from what I found:

Mmm, yeah for Windows I'm familiar, just not what it's being translated to (that is, if my later assumption is correct that everything gets interpreted as Linux commands). Linux man page for shutdown doesn't list any -s so was puzzled why only /s works.


Currently testing in a Windows 10 VM via VirtualBox, to test feasibility (and for ease of restarting) before adding to my host Windows system.

chocmake commented 8 months ago

Some more testing. Decided to stop the NUT service and launch upsmon.exe instead since an earlier post mentioned:

I think as a service it has no easy access to a desktop session

Also since it I noticed references in the source code to Cygwin, which supports executing Windows commands alongside Linux ones with the right syntax. Not sure if that's actually what is used as the interpreter but anyway.

Results:

So at least I know now using upsmon.exe instead is feasible for launching commands other than shutdown /s.


If there was a way that the service could execute files when at least given a full path it'd be useful. This SO page suggests that for example CMD.exe can be utilized with arguments via a service, so long as it doesn't require any UI/user interaction. Idk if this is relevant to how NUT handles it.

jimklimov commented 8 months ago

I read there is a common trick using Windows Task Scheduler to set up execution of complex commands (with privileges needed, ensuring stuff only runs once at a time, etc.) and scripts or other less privileged programs calling that via schtasks /Run "name".

I suppose here it is overkill since the NUT service should already be quite privileged, but still a useful trick in the automation toolkit.

You can probably try setting a higher debug_min in upsmon.conf (see wiki) and check if something appears in event log about programs it is trying to run, maybe failing (and with what hints then).

Keep in mind that there is a NUT config parser involved first and foremost (converting string markup in text files into a char array in memory), so the outer double-quotes which concatenate space-separated tokens like shutdown and /s are important and are not passed as such to the OS. Notably, you should not need extra quotes to wrap a single token. The back-slashes are parsed in C style, so a single one means an escape symbol (like \n meaning end of line; you need \\ to escape and present a single backslash). The rest is passed to OS-dependent API for process creation (usually fork()+exec() or native CreateProcess() on Windows; in case of upsmon.c I see a generic system() for both, however) which is where embedded single/double quotes and path resolution (/etc/...) might matter.

chocmake commented 8 months ago

Checked Event log messages when DEBUG_MIN 2 set.

upsmon - [D2] pollups: ups@<ip>
upsmon - [D2] parse_status: [FSD OL]
upsmon - [D1] Critical UPS: ups@<ip>
upsmon - [D2] do_notify: ntype 0x0006 (SHUTDOWN)
upsmon - Failed to create power down flag!
Exiting

Then checked when set to DEBUG_MIN 3. Didn't increase verbosity but curiously the errors weren't logged (maybe because it was between polling checks?). So I guess 2 is the max verbosity (or 1, didn't try that).

upsmon - [D2] pollups: ups@<ip>
upsmon - [D2] parse_status: [OL]
upsmon - [D2] pollups: ups@<ip>
Exiting

Might try the method of setting my custom script to execute when the service is detected as stopped (which afaict is possible with it via the Service properties). Edit: oh, I see what you meant re Task Scheduler, didn't click at first.


Edit 2: tested creating a Task Scheduler task and using SHUTDOWNCMD "schtasks /run /tn "Task name here"" to trigger it in upsmon.conf.

First tested that the task would launch the custom script when manually run via Task Scheduler and gave it Administrators user group privileges and enabled 'Run with highest privileges' option. It wouldn't run without admin privileges I found (edit: turns out there is but you have to disable the option to run when logged out, so tested setting task to run under current user only but similarly nothing triggered on FSD).

However unfortunately the NUT service failed to launch it automatically when the FSD signal was received. If I'm missing something let me know.

chocmake commented 8 months ago

Some success :) Found a way to make upsmon.exe launch entirely hidden, so that it can be run in the background instead of the NUT service for correctly launching arbitrary SHUTDOWNCMD values.

It uses VBScript to launch it to begin with, which can be added to the Windows startup/etc.

  1. Create a VBS file in the sbin directory, eg: upsmon-hidden.vbs.
  2. Fill it with the following lines (the 0 tells VBScript to launch it window-less):
    Set wShell = CreateObject ("Wscript.Shell") 
    wShell.Run "upsmon.exe", 0
  3. Launch it and nothing will appear, however it will be listed in Task Manager's Details tab, along with a conhost item.

Downsides to this approach:

Miscellaneous:


Other things explored prior to this that had no effect: