Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows port #5

Open
aquette opened this issue Feb 14, 2013 · 39 comments
Open

Windows port #5

aquette opened this issue Feb 14, 2013 · 39 comments

Comments

@aquette
Copy link
Member

aquette commented Feb 14, 2013

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
Copy link
Member Author

aquette commented Feb 14, 2013

@clepple
Copy link
Member

clepple commented Feb 18, 2013

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: 6e51ff2

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
Copy link
Member

clepple commented Feb 20, 2013

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)

aquette added a commit that referenced this issue Feb 23, 2013
Document that, at least, this Makefile.am is broken by design
(patch #5 from Greg A. Woods)

Fossil-ID: SVN r3572
@intrudo
Copy link

intrudo commented Apr 12, 2016

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
Copy link
Member Author

aquette commented Apr 28, 2016

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
Copy link

intrudo commented Apr 30, 2016

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
Copy link
Member

clepple commented Apr 30, 2016

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
Copy link

intrudo commented May 5, 2016

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
Copy link
Member

clepple commented May 7, 2016

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
Copy link

mrudat commented Sep 18, 2016

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.

jimklimov referenced this issue in 42ity/nut Jan 28, 2017
…/BUILD

* commit 'dfdf588dac92d3be175dac30a875e0b19551aefe': (22 commits)
  snmp-ups.c : a little bit of input checking does not hurt, especially when it fixes a segfault
  Remove unimplement nut-scanner option
  Update nut-scanner manual page
  Fix c++ style comments
  upsstats.html: Conform to HTML 4.01 Strict
  upsstats-single.html: Conform to HTML 4.01 Strict
  Fix c++ style comments
  Fix exit code for --list
  common.c/.h : simplify the upsdebugx() enhancement (no need to fight with warnings/pragmas)
  common.h : rearranged the ENABLE_WARNING/DISABLE_WARNING macro definitions so they are only called for supported compilers and generate empty macros (no-op) for others
  common.c : move anti-warning macros out of the function body, some compilers do not like that
  common.c : in upsdebugx() added "[Dx]" prefix : do not add it for level 0
  configure.ac : do not fail configuration if a doctype picked with "auto" can not be built on this system - just skip it
  Added and documented an snmp-ups argument to list currently loaded MIB-to-NUT mappings (typo fix)
  Added and documented an snmp-ups argument to list currently loaded MIB-to-NUT mappings
  snmp-ups.c : small typo fix
  Mention tripplite mapping in snmp-ups manpage; updated some comments about it in ietf-mib.c
  configure.ac : when verifying ability to build docs, consider out-of-tree builds too
  configure.ac : verify that we can actually build a simple or chunked HTML document, beside having nominal versions of software
  configure.ac : verify that we can actually build a manpage document, beside having nominal versions of software
  ...
jimklimov referenced this issue in jimklimov/nut Mar 1, 2017
Complete the deprecation of MGE-XML convert_deci()
@aquette
Copy link
Member Author

aquette commented Mar 22, 2017

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
Copy link
Member

jimklimov commented May 17, 2022

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
Copy link
Member

jimklimov commented Jun 9, 2022

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
Copy link

DorCoMaNdO commented Jun 9, 2022

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
Copy link
Member

jimklimov commented Jun 10, 2022

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
Copy link
Member

jimklimov commented Aug 19, 2022

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
Copy link
Member

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
Copy link

chocmake commented Mar 4, 2024

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
Copy link
Member

jimklimov commented Mar 4, 2024

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
Copy link

chocmake commented Mar 4, 2024

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
Copy link
Member

OpenSCManager failed (5)

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

@chocmake
Copy link

chocmake commented Mar 4, 2024

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 👍

@chocmake
Copy link

chocmake commented Mar 4, 2024

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
Copy link
Member

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
Copy link

chocmake commented Mar 5, 2024

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
Copy link
Member

jimklimov commented Mar 5, 2024

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
Copy link

chocmake commented Mar 5, 2024

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
Copy link

chocmake commented Mar 8, 2024

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:

  • SHUTDOWNCMD "calc.exe" = works
  • SHUTDOWNCMD "cmd /c \"C:\\Portable\\NUT\\etc\\script.bat\"" = works
  • SHUTDOWNCMD "\"C:\\Portable\\NUT\\etc\\script.bat\"" = works
  • SHUTDOWNCMD "C:\Portable\NUT\etc\script.bat" = fails
  • SHUTDOWNCMD 'C:\Portable\NUT\etc\script.bat' (single quotes) = fails (read online this should work in Cygwin)
  • SHUTDOWNCMD "cmd.exe /c "C:\Portable\NUT\etc\custompoweroff.bat"" = fails
  • SHUTDOWNCMD "/etc/script.bat" = fails

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
Copy link
Member

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
Copy link

chocmake commented Mar 8, 2024

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
Copy link

chocmake commented Mar 9, 2024

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
  1. 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:

  • Runs per-user instead of user-agnostic like a Windows service is. So if multiple users on system then each would require their own running copy, which also means multiple instances of whatever command/script is set for SHUTDOWNCMD will trigger simultaneously if both users logged in when FSD signal received (may or may not be an issue).
  • No Event Viewer logs added afaict (though I personally don't care).
  • In my limited testing nothing launched by the hidden upsmon.exe will be visible either, including batch scripts. So any commands used should be functional rather than say launching some visible window (unless it's just some passed argument to an already running program).

Miscellaneous:

  • If upsmon.conf security permissions are changed to be read only for the current user (ie: to make modification a privileged action) upsmon.exe will fail to run, saying it can't access the file.
  • If using hibernation instead of shutdown upsmon.exe will need to be restarted following the restoration of the hibernation state (since it's not a Windows startup it won't auto launch otherwise). This can be accomplished by attaching a Task Scheduler task to the Power-Troubleshooter event with the Event ID of 1. See this answer for the correct path syntax to get VBS scripts to auto run without erroring.

Other things explored prior to this that had no effect:

  • Editing NUT service Properties>Recovery settings to 'Run program' when service 'failure' detected. Apparently a service auto stopping doesn't count as a failure so this had no effect.
  • Running NUT service as current user via Properties>Log on and entering current user's Windows credentials.
  • Can't attach a Task Scheduler task to an Event Viewer log item in NUT's case since all events are logged as event ID 1, whether they're an error/warning/normal event. Also Exiting event is logged as normal item (I think that's the last event that occurs when FSD detected?).

jimklimov added a commit that referenced this issue Jun 10, 2024
…sage [#2463]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Todo
Development

No branches or pull requests

7 participants