[PATCH 0/2] Add apcupsd

  • Done
  • quality assurance status badge
Details
2 participants
  • Maxim Cournoyer
  • Tomas Volf
Owner
unassigned
Submitted by
Tomas Volf
Severity
normal

Debbugs page

Tomas Volf wrote 2 months ago
(address . guix-patches@gnu.org)(name . Tomas Volf)(address . ~@wolfsden.cz)
cover.1736722765.git.~@wolfsden.cz
For machines running with an uninterruptible power supply (UPS), it is
necessary to be able to react to events from it. That allows to, for example,
safely shutdown the system when battery is getting low or get a notification
that battery needs to be replaced. This series adds package and service type
for apcupsd, daemon to manage UPS devices made by American Power Conversion
Corporation (APC).

Tomas Volf (2):
gnu: Add apcupsd.
services: Add power.

doc/guix.texi | 374 +++++++++++++++++++++-
gnu/local.mk | 2 +
gnu/packages/power.scm | 125 ++++++++
gnu/services/power.scm | 690 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 1188 insertions(+), 3 deletions(-)
create mode 100644 gnu/packages/power.scm
create mode 100644 gnu/services/power.scm

--
2.47.1
Tomas Volf wrote 2 months ago
[PATCH 1/2] gnu: Add apcupsd.
(address . 75528@debbugs.gnu.org)(name . Tomas Volf)(address . ~@wolfsden.cz)
51cba7679af5c4aa9cf0e6d70453e369ff0909d6.1736722765.git.~@wolfsden.cz
* gnu/packages/power.scm (apcupsd): New variable.

Change-Id: I74f59cd1fa2a13954117ff1683a10a84576cc839
---
gnu/local.mk | 1 +
gnu/packages/power.scm | 125 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 126 insertions(+)
create mode 100644 gnu/packages/power.scm

Toggle diff (145 lines)
diff --git a/gnu/local.mk b/gnu/local.mk
index 855f2acccc..6ca7bf68ac 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -557,6 +557,7 @@ GNU_SYSTEM_MODULES = \
%D%/packages/polkit.scm \
%D%/packages/popt.scm \
%D%/packages/potassco.scm \
+ %D%/packages/power.scm \
%D%/packages/printers.scm \
%D%/packages/profiling.scm \
%D%/packages/prolog.scm \
diff --git a/gnu/packages/power.scm b/gnu/packages/power.scm
new file mode 100644
index 0000000000..98dc98c6e4
--- /dev/null
+++ b/gnu/packages/power.scm
@@ -0,0 +1,125 @@
+;;; Copyright (C) 2025 Tomas Volf <~@wolfsden.cz>
+;;; Copyright (C) 2023 Raven Hallsby <karl@hallsby.com>
+
+;;;; Commentary:
+
+;;; Power-related packages.
+
+;;;; Code:
+
+(define-module (gnu packages power)
+ #:use-module (gnu)
+ #:use-module (gnu packages libusb)
+ #:use-module (gnu packages linux)
+ #:use-module (gnu packages pkg-config)
+ #:use-module (gnu packages python-xyz)
+ #:use-module (guix build-system gnu)
+ #:use-module (guix download)
+ #:use-module (guix gexp)
+ #:use-module ((guix licenses) #:prefix license:)
+ #:use-module (guix packages))
+
+(define-public apcupsd
+ (package
+ (name "apcupsd")
+ (version "3.14.14")
+ (source (origin
+ (method url-fetch)
+ (uri
+ (string-append
+ "mirror://sourceforge/" name "/" name " - Stable/" version
+ "/" name "-" version ".tar.gz"))
+ (sha256
+ (base32
+ "0rwqiyzlg9p0szf3x6q1ppvrw6f6dbpn2rc5z623fk3bkdalhxyv"))))
+ (native-inputs
+ (list pkg-config python-docutils))
+ (inputs
+ (list libusb libusb-compat))
+ (outputs '("out" "doc"))
+ (build-system gnu-build-system)
+ (arguments
+ (list
+ #:configure-flags
+ #~(list
+ ;; The configure script ignores --prefix for most of the paths.
+ (string-append "--exec-prefix=" #$output)
+ (string-append "--mandir=" #$output "/share/man")
+ (string-append "--sbindir=" #$output "/sbin")
+ (string-append "--sysconfdir=" #$output "/etc/apcupsd")
+ (string-append "--with-halpolicydir=" #$output "/share/halpolicy")
+
+ ;; Put us into the version string.
+ "--with-distname=GNU/Guix"
+ "--disable-install-distdir"
+
+ ;; State directories.
+ "--localstatedir=/var"
+ "--with-log-dir=/var/log"
+ "--with-pid-dir=/var/run"
+ "--with-lock-dir=/var/run/apcupsd/lock"
+ "--with-nologin=/var/run/apcupsd"
+ "--with-pwrfail-dir=/var/run/apcupsd"
+
+ ;; Configure requires these, but we do not use the genenerated
+ ;; apcupsd.conf, so in order to reduce dependencies of the package,
+ ;; provide fake values.
+ (string-append "ac_cv_path_SHUTDOWN=/nope")
+ (string-append "ac_cv_path_APCUPSD_MAIL=/nope")
+ ;; While `wall' is not expanded anywhere, it still is searched for.
+ (string-append "ac_cv_path_WALL=/nope")
+
+ ;; Enable additional drivers.
+ "--enable-test"
+ "--enable-usb"
+ "--enable-modbus-usb")
+ #:tests? #f ; There are no tests.
+ #:modules (cons '(ice-9 ftw) %default-gnu-modules)
+ #:phases
+ #~(modify-phases %standard-phases
+ ;; These are not installed, so trick Make into thinking they were
+ ;; already generated. Allows us not to depend on mandoc, util-linux.
+ (add-before 'configure 'touch-txt-docs
+ (lambda _
+ (for-each (lambda (f)
+ (call-with-output-file f close-port))
+ '("doc/apcupsd.man.txt"
+ "doc/apcaccess.man.txt"
+ "doc/apctest.man.txt"
+ "doc/apccontrol.man.txt"
+ "doc/apcupsd.conf.man.txt"))))
+ (add-after 'build 'build-manual
+ (lambda _
+ (invoke "make" "-C" "doc/manual" "manual.html")))
+ (add-after 'install-license-files 'move-doc
+ (lambda _
+ (let ((target (string-append #$output:doc
+ "/share/doc/"
+ (strip-store-file-name #$output))))
+ (mkdir-p target)
+ (for-each (lambda (f)
+ (copy-file (string-append "doc/manual/" f)
+ (string-append target "/" f)))
+ (scandir "doc/manual"
+ (lambda (f)
+ (or (string-suffix? ".png" f)
+ (string-suffix? ".html" f))))))))
+ ;; If sending mails is required, use proper mail program.
+ (add-after 'install 'remove-smtp
+ (lambda _
+ (delete-file (string-append #$output "/sbin/smtp"))))
+ ;; The configuration files and scripts are not really suitable for
+ ;; Guix, and our service provides its own version anyway. So nuke
+ ;; these to make sure `apcupsd' and `apctest' executed without any
+ ;; arguments fail. `apctest' actually segfaults, but only after
+ ;; printing an error ¯\_(ツ)_/¯.
+ (add-after 'install 'remove-etc-apcupsd
+ (lambda _
+ (delete-file-recursively (string-append #$output "/etc/apcupsd")))))))
+ (home-page "https://www.apcupsd.org")
+ (synopsis "A daemon for controlling APC UPSes")
+ (description "Apcupsd can be used for power mangement and controlling most
+of APC’s UPS models on Unix and Windows machines. Apcupsd works with most of
+APC’s Smart-UPS models as well as most simple signalling models such a Back-UPS,
+and BackUPS-Office.")
+ (license license:gpl2)))
--
2.47.1
Tomas Volf wrote 2 months ago
[PATCH 2/2] services: Add power.
(address . 75528@debbugs.gnu.org)(name . Tomas Volf)(address . ~@wolfsden.cz)
05ba17f2babc772a26072dca72c2e6e6f852e0ad.1736722765.git.~@wolfsden.cz
* gnu/services/power.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (Power Management Services): Document service and data types.

Change-Id: If205d19bea1d20a99309626e28521a2d6fe6702f
---
doc/guix.texi | 374 +++++++++++++++++++++-
gnu/local.mk | 1 +
gnu/services/power.scm | 690 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 1062 insertions(+), 3 deletions(-)
create mode 100644 gnu/services/power.scm

Toggle diff (543 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index d6e17c74cd..6cb11200cd 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -123,7 +123,7 @@
Copyright @copyright{} 2023 Thomas Ieong@*
Copyright @copyright{} 2023 Saku Laesvuori@*
Copyright @copyright{} 2023 Graham James Addis@*
-Copyright @copyright{} 2023, 2024 Tomas Volf@*
+Copyright @copyright{} 2023-2025 Tomas Volf@*
Copyright @copyright{} 2024 Herman Rimm@*
Copyright @copyright{} 2024 Matthew Trzcinski@*
Copyright @copyright{} 2024 Richard Sent@*
@@ -420,7 +420,7 @@ Top
* Network File System:: NFS related services.
* Samba Services:: Samba services.
* Continuous Integration:: Cuirass and Laminar services.
-* Power Management Services:: Extending battery life.
+* Power Management Services:: Extending battery life, etc.
* Audio Services:: The MPD.
* Virtualization Services:: Virtualization services.
* Version Control Services:: Providing remote access to Git repositories.
@@ -19255,7 +19255,7 @@ Services
* Network File System:: NFS related services.
* Samba Services:: Samba services.
* Continuous Integration:: Cuirass and Laminar services.
-* Power Management Services:: Extending battery life.
+* Power Management Services:: Extending battery life, etc.
* Audio Services:: The MPD.
* Virtualization Services:: Virtualization services.
* Version Control Services:: Providing remote access to Git repositories.
@@ -36263,6 +36263,374 @@ Power Management Services
@end table
@end deftp
+The @code{(gnu services power)} module provides a service definition for
+@uref{http://www.apcupsd.org/, apcupsd}, a utility to interact with APC
+UPSes. Apcupsd also works with some OEM-branded products manufactured
+by APC.
+
+@defvar apcupsd-service-type
+The service type for apcupsd. For USB UPSes no configuration is
+necessary, however tweaking some fields to better suit your needs might
+be desirable. The defaults are taken from the upstream configuration
+and they are not very conservative (@code{battery-level} of 5% is too
+low in my opinion).
+
+The default event handlers do send emails, read more in
+@ref{apcupsd-event-handlers}.
+
+@lisp
+(service apcupsd-service-type)
+@end lisp
+@end defvar
+
+@deftp {Data Type} apcupsd-configuration
+
+Available @code{apcupsd-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{apcupsd}) (type: package)
+Package to use.
+
+@item @code{shepherd-base-name} (default: @code{apcupsd}) (type: symbol)
+Base name of the shepherd service. You can add the service multiple
+times with different prefix to manage multiple UPSes.
+
+@item @code{auto-start?} (default: @code{#t}) (type: boolean)
+Should the shepherd service auto-start?
+
+@item @code{pid-file} (default: @code{"/var/run/apcupsd.pid"}) (type: string)
+Path to the pid file.
+
+@item @code{debug-level} (default: @code{0}) (type: integer)
+Logging verbosity. Bigger number means more logs. In the source code I
+saw up to @code{300}, so for all logs, @code{999} seems reasonable.
+
+@item @code{run-dir} (default: @code{"/var/run/apcupsd"}) (type: string)
+Directory containing runtime information. You need to change this if
+you desire to run multiple instances of the daemon.
+
+@item @code{name} (type: maybe-string)
+Use this to give your UPS a name in log files and such. This is
+particularly useful if you have multiple UPSes. This does not set the
+EEPROM. It should be 8 characters or less.
+
+@item @code{cable} (default: @code{usb}) (type: enum-cable)
+The type of cable connecting the UPS to your computer. Possible generic
+choices are @code{'simple}, @code{'smart}, @code{'ether} and
+@code{'usb}. Or a specific cable model number may be used:
+@code{'940-0119A}, @code{'940-0127A}, @code{'940-0128A},
+@code{'940-0020B}, @code{'940-0020C}, @code{'940-0023A},
+@code{'940-0024B}, @code{'940-0024C}, @code{'940-1524C},
+@code{'940-0024G}, @code{'940-0095A}, @code{'940-0095B},
+@code{'940-0095C}, @code{'940-0625A}, @code{'M-04-02-2000}.
+
+@item @code{type} (default: @code{usb}) (type: enum-type)
+Type of the UPS you have.
+
+@table @code
+@item apcsmart
+Newer serial character device, appropriate for SmartUPS models using a
+serial cable (not USB).
+
+@item usb
+Most new UPSes are USB.
+
+@item net
+Network link to a master apcupsd through apcupsd's Network Information
+Server. This is used if the UPS powering your computer is connected to
+a different computer for monitoring.
+
+@item snmp
+SNMP network link to an SNMP-enabled UPS device.
+
+@item netsnmp
+Same as SNMP above but requires use of the net-snmp library. Unless you
+have a specific need for this old driver, you should use @code{'snmp}
+instead.
+
+@item dumb
+Old serial character device for use with simple-signaling UPSes.
+
+@item pcnet
+PowerChute Network Shutdown protocol which can be used as an alternative
+to SNMP with the AP9617 family of smart slot cards.
+
+@item modbus
+Serial device for use with newest SmartUPS models supporting the MODBUS
+protocol.
+
+@end table
+
+@item @code{device} (default: @code{""}) (type: string)
+For USB UPSes, usually you want to set this to an empty string (the
+default). For other UPS types, you must specify an appropriate port or
+address.
+
+@table @code
+@item apcsmart
+Set to the appropriate @file{/dev/tty**} device.
+
+@item usb
+A null string setting enables autodetection, which is the best choice
+for most installations.
+
+@item net
+Set to @code{@var{hostname}:@var{port}}.
+
+@item snmp
+Set to @code{@var{hostname}:@var{port}:@var{vendor}:@var{community}}.
+@var{hostname} is the ip address or hostname of the UPS on the network.
+@var{vendor} can be can be "APC" or "APC_NOTRAP". "APC_NOTRAP" will
+disable SNMP trap catching; you usually want "APC". @var{port} is
+usually 161. @var{community} is usually "private".
+
+@item netsnmp
+Same as @code{'snmp}.
+
+@item dumb
+Set to the appropriate @file{/dev/tty**} device.
+
+@item pcnet
+Set to @code{@var{ipaddr}:@var{username}:@var{passphrase}:@var{port}}.
+@var{ipaddr} is the IP address of the UPS management card.
+@var{username} and @var{passphrase} are the credentials for which the
+card has been configured. @var{port} is the port number on which to
+listen for messages from the UPS, normally 3052. If this parameter is
+empty or missing, the default of 3052 will be used.
+
+@item modbus
+Set to the appropriate @file{/dev/tty**} device. You can also leave it
+empty for MODBUS over USB or set to the serial number of the UPS.
+
+@end table
+
+@item @code{poll-time} (default: @code{60}) (type: integer)
+Interval (in seconds) at which apcupsd polls the UPS for status. This
+setting applies both to directly-attached UPSes (apcsmart, usb, dumb)
+and networked UPSes (net, snmp). Lowering this setting will improve
+apcupsd's responsiveness to certain events at the cost of higher CPU
+utilization.
+
+@item @code{on-batery-delay} (default: @code{6}) (type: integer)
+The the time in seconds from when a power failure is detected until we
+react to it with an onbattery event. The @code{'powerout} event will be
+triggered immediately when a power failure is detected. However, the
+@code{'onbattery} event will be trigger only after this delay.
+
+@item @code{battery-level} (default: @code{5}) (type: integer)
+If during a power failure, the remaining battery percentage (as reported
+by the UPS) is below or equal to this value, apcupsd will initiate a
+system shutdown.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{remaining-minutes} (default: @code{3}) (type: integer)
+If during a power failure, the remaining runtime in minutes (as
+calculated internally by the UPS) is below or equal to this value,
+apcupsd will initiate a system shutdown.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{timeout} (default: @code{0}) (type: integer)
+If during a power failure, the UPS has run on batteries for this many
+seconds or longer, apcupsd will initiate a system shutdown. A value of
+0 disables this timer.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{annoy-interval} (default: @code{300}) (type: integer)
+Time in seconds between annoying users (via the @code{'annoyme} event)
+to sign off prior to system shutdown. 0 disables.
+
+@item @code{annoy-delay} (default: @code{60}) (type: integer)
+Initial delay in seconds after power failure before warning users to get
+off the system.
+
+@item @code{no-logon} (default: @code{disable}) (type: enum-no-logon)
+The condition which determines when users are prevented from logging in
+during a power failure.
+
+@item @code{kill-delay} (default: @code{0}) (type: integer)
+If this is non-zero, apcupsd will continue running after a shutdown has
+been requested, and after the specified time in seconds attempt to kill
+the power. This is for use on systems where apcupsd cannot regain
+control after a shutdown.
+
+@item @code{net-server} (default: @code{#f}) (type: boolean)
+If enabled, a network information server process will be started.
+
+@item @code{net-server-ip} (default: @code{"127.0.0.1"}) (type: string)
+IP address on which NIS server will listen for incoming connections.
+
+@item @code{net-server-port} (default: @code{3551}) (type: integer)
+IP port on which NIS server will listen for incoming connections.
+
+@item @code{net-server-events-file} (type: maybe-string)
+If you want the last few EVENTS to be available over the network by the
+network information server, you must set this to a file patch.
+
+@item @code{net-server-events-file-max-size} (default: @code{10}) (type: integer)
+Maximum size of the events file in kilobytes.
+
+@item @code{class} (default: @code{standalone}) (type: enum-class)
+Normally standalone unless you share an UPS using an APC ShareUPS card.
+
+@item @code{mode} (default: @code{disable}) (type: enum-mode)
+Normally disable unless you share an UPS using an APC ShareUPS card.
+
+@item @code{stat-time} (default: @code{0}) (type: integer)
+Time interval in seconds between writing the status file, 0 disables.
+
+@item @code{log-stats} (default: @code{#f}) (type: boolean)
+Also write the stats as a logs. This generates a lot of output.
+
+@item @code{data-time} (default: @code{0}) (type: integer)
+Time interval in seconds between writing the data records to the log
+file, 0 disables.
+
+@item @code{facility} (type: maybe-string)
+The logging facility for the syslog.
+
+@item @code{event-handlers} (type: apcupsd-event-handlers)
+Handlers for events produced by apcupsd.
+
+@end table
+
+@end deftp
+
+@anchor{apcupsd-event-handlers}
+@deftp {Data Type} apcupsd-event-handlers
+
+For description of the events please refer to the @command{apcupsd}'s
+manual, which can be found in the @samp{apcupsd-doc} package.
+
+Each handler shall be a gexp. It is spliced into the control script for
+the daemon. In addition to the standard Guile programming environment,
+these procedures and variables are also available.
+
+@table @code
+@item conf
+Variable containing path to the configuration file.
+
+@item powerfail-file
+Variable containing path to the powerfail file.
+
+@item cmd
+The event currently being handled.
+
+@item name
+The name of the UPS as specified in the configuration file.
+
+@item connected
+Is @code{"1"} if apcupsd is connected to the UPS via a serial port (or a
+USB port). In most configurations, this will be the case. In the case
+of a Slave machine where apcupsd is not directly connected to the UPS,
+this value will be @code{"0"}.
+
+@item powered
+Is @code{"1"} if the computer on which @command{apcupsd} is running is
+powered by the UPS and @code{"0"} if not. At the moment, this value is
+unimplemented and always @code{"0"}.
+
+@item (err @var{fmt} @var{args...})
+Wrapper around @code{format} outputting to @code{(current-error-port)}.
+
+@item (wall @var{fmt} @var{args...})
+Wrapper around @code{format} outputting via @command{wall}.
+
+@item (apcupsd @var{args...})
+Call @command{apcupsd} while passing the correct configuration file and
+all the arguments.
+
+@item (mail-to-root @var{subject} @var{body})
+Send an email to the local administrator. This procedure assumes the
+@command{sendmail} is located at @command{/run/privileged/bin/sendmail}
+(as would be the case with @code{opensmtpd-service-type}).
+
+@end table
+
+Available @code{apcupsd-event-handlers} fields are:
+
+@table @asis
+@item @code{killpower} (type: gexp)
+Handler for killpower event.
+
+@item @code{commfailure} (type: gexp)
+Handler for commfailure event.
+
+@item @code{commok} (type: gexp)
+Handler for commfailure event.
+
+@item @code{powerout} (type: gexp)
+Handler for powerout event.
+
+@item @code{onbattery} (type: gexp)
+Handler for onbattery event.
+
+@item @code{offbattery} (type: gexp)
+Handler for offbattery event.
+
+@item @code{mainsback} (type: gexp)
+Handler for mainsback event.
+
+@item @code{failing} (type: gexp)
+Handler for failing event.
+
+@item @code{timeout} (type: gexp)
+Handler for timeout event.
+
+@item @code{loadlimit} (type: gexp)
+Handler for loadlimit event.
+
+@item @code{runlimit} (type: gexp)
+Handler for runlimit event.
+
+@item @code{doreboot} (type: gexp)
+Handler for doreboot event.
+
+@item @code{doshutdown} (type: gexp)
+Handler for doshutdown event.
+
+@item @code{annoyme} (type: gexp)
+Handler for annoyme event.
+
+@item @code{emergency} (type: gexp)
+Handler for emergency event.
+
+@item @code{changeme} (type: gexp)
+Handler for changeme event.
+
+@item @code{remotedown} (type: gexp)
+Handler for remotedown event.
+
+@item @code{startselftest} (type: gexp)
+Handler for startselftest event.
+
+@item @code{endselftest} (type: gexp)
+Handler for endselftest event.
+
+@item @code{battdetach} (type: gexp)
+Handler for battdetach event.
+
+@item @code{battattach} (type: gexp)
+Handler for battattach event.
+
+@end table
+
+@end deftp
+
@node Audio Services
@subsection Audio Services
diff --git a/gnu/local.mk b/gnu/local.mk
index 6ca7bf68ac..dea0e43fe6 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -758,6 +758,7 @@ GNU_SYSTEM_MODULES = \
%D%/services/nix.scm \
%D%/services/nfs.scm \
%D%/services/pam-mount.scm \
+ %D%/services/power.scm \
%D%/services/science.scm \
%D%/services/security.scm \
%D%/services/security-token.scm \
diff --git a/gnu/services/power.scm b/gnu/services/power.scm
new file mode 100644
index 0000000000..a6f9259eb3
--- /dev/null
+++ b/gnu/services/power.scm
@@ -0,0 +1,690 @@
+;;; Copyright (C) 2025 Tomas Volf <~@wolfsden.cz>
+
+;;;; Commentary:
+
+;;; Power-related services.
+
+;;;; Code:
+
+(define-module (gnu services power)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
+ #:use-module (ice-9 match)
+ #:use-module (gnu)
+ #:use-module (gnu packages admin)
+ #:use-module (gnu packages linux)
+ #:use-module (gnu packages power)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services shepherd)
+ #:use-module (guix packages)
+ #:use-module (guix records)
+ #:export (apcupsd-service-type
+
+ apcupsd-configuration
+ apcupsd-configuration-package
+ apcupsd-configuration-shepherd-base-name
+ apcupsd-configuration-auto-start?
+ apcupsd-configuration-pid-file
+ apcupsd-configuration-debug-level
+ apcupsd-configuration-run-dir
+ apcupsd-configuration-name
+ apcupsd-configuration-cable
+ apcupsd-configuration-type
+ apcupsd-configuration-device
+ apcupsd-configuration-poll-time
+ apcupsd-configuration-on-batery-delay
+ apcupsd-configuration-battery-level
+ apcupsd-configuration-remaining-minutes
+ apcupsd-configuration-timeout
+ apcupsd-configuration-annoy-interval
+ apcupsd-configuration-annoy-delay
+ apcupsd-configuration-no-logon
+ apcupsd-configuration-kill-delay
+ apcupsd-configuration-net-server
+ apcupsd-configuration-net-server-ip
+ apcupsd-configuration-net-server-port
+ apcupsd-configuration-net-server-events-file
+ apcupsd-configuration-net-server-events-file-max-size
+ apcupsd-configuration-class
+ apcupsd-configuration-mode
+ apcupsd-configuration-stat-time
+ apcupsd-configuration-log-stats
+ apcupsd-configuration-data-time
+ apcupsd-configuration-facility
+ apcupsd-configuration-event-handlers
+
+ apcupsd-event-handlers-annoyme
+ apcupsd-event-handlers-battattach
+ apcupsd-event-handlers-battdetach
+ apcupsd-event-handlers-changeme
+ apcupsd-event-handlers-commfailure
+ apcupsd-event-handlers-commok
+ apcupsd-event-handlers-doreboot
+ apcupsd-event-handlers-doshutdown
+ apcupsd-event-handlers-emergency
+ apcupsd-event-handlers-endselftest
+ apcupsd-event-handlers-failing
+ apcupsd-event-handlers-killpower
+ apcupsd-event-handlers-loadlimit
+ apcupsd-event-handlers-mainsback
+ apcupsd-event-handlers-offbattery
+ apcupsd-event-handlers-onbattery
+ apcupsd-event-handlers-powerout
+ apcupsd-event-handlers-remotedown
+ apcupsd-event-handlers-runlimit
+ apcupsd-event-handlers-startselftest
+ apcupsd-event-handlers-timeout))
+
+(define-configuration/no-serialization apcupsd-event-handlers
+ (killpower
+ (gexp
+ #~((wall "Apccontrol doing: apcupsd --killpower on UPS ~a" name)
+ (sleep 10)
+ (apcupsd "--killpower")
+ (wall "Apccontrol has done: apcupsd --killpower on UPS ~a" name)))
+ "Handler for killpower event.")
+ (commfailure
+ (gexp
+ #~((let ((msg (format #f "~a Communications with UPS ~a lost."
+ (gethostname) name)))
+ (mail-to-root msg msg))
+ (wall "Warning: communications lost with UPS ~a" name)))
+ "Handler for commfailure event.")
+ (commok
+ (gexp
+ #~((let ((msg (format #f "~a Communications with UPS ~a restored."
+ (gethostname) name)))
+ (mail-to-root msg msg))
+ (wall "Communications restored with UPS ~a" name)))
+ "Handler for commfailure event.")
+ (powerout
+ (gexp
+ #~(#t))
+ "Handler for powerout event.")
+ (onbattery
+ (gexp
+ #~((let ((msg (format #f "~a UPS ~a Power Failure !!!"
+ (gethostname) name)))
+ (mail-to-root msg msg))
+ (wall "Power failure on UPS ~a. Running on batteries." name)))
+ "Handler for onbattery event.")
+ (offbattery
+ (gexp
+ #~((let ((msg (format #f "~a UPS ~a Power has returned."
+ (gethostname) name)))
+ (mail-to-root msg msg))
+ (wall "Power has returned on UPS ~a..." name)))
+ "Handler for offbattery event.")
+ (mainsba
This message was truncated. Download the full message here.
Maxim Cournoyer wrote 1 months ago
Re: [bug#75528] [PATCH 1/2] gnu: Add apcupsd.
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)
87jza498sp.fsf@gmail.com
Hi Tomas,

Tomas Volf <~@wolfsden.cz> writes:

Toggle quote (2 lines)
> * gnu/packages/power.scm (apcupsd): New variable.

'guix lint' says:

Toggle snippet (4 lines)
gnu/packages/power.scm:120:14: apcupsd@3.14.14: no article allowed at the beginning of the synopsis
gnu/packages/power.scm:119:15: apcupsd@3.14.14: TLS certificate error: X.509 server certificate for 'www.apcupsd.org' does not match: CN=sf.net

Please fix these.

Toggle quote (17 lines)
> Change-Id: I74f59cd1fa2a13954117ff1683a10a84576cc839
> ---
> gnu/local.mk | 1 +
> gnu/packages/power.scm | 125 +++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 126 insertions(+)
> create mode 100644 gnu/packages/power.scm
>
> diff --git a/gnu/local.mk b/gnu/local.mk
> index 855f2acccc..6ca7bf68ac 100644
> --- a/gnu/local.mk
> +++ b/gnu/local.mk
> @@ -557,6 +557,7 @@ GNU_SYSTEM_MODULES = \
> %D%/packages/polkit.scm \
> %D%/packages/popt.scm \
> %D%/packages/potassco.scm \
> + %D%/packages/power.scm \

This change should be mentioned in the change log as well, e.g.:

* gnu/local.mk (GNU_SYSTEM_MODULES): Register it.

[...]

Toggle quote (18 lines)
> +(define-public apcupsd
> + (package
> + (name "apcupsd")
> + (version "3.14.14")
> + (source (origin
> + (method url-fetch)
> + (uri
> + (string-append
> + "mirror://sourceforge/" name "/" name " - Stable/" version
> + "/" name "-" version ".tar.gz"))
> + (sha256
> + (base32
> + "0rwqiyzlg9p0szf3x6q1ppvrw6f6dbpn2rc5z623fk3bkdalhxyv"))))
> + (native-inputs
> + (list pkg-config python-docutils))
> + (inputs
> + (list libusb libusb-compat))

nitpick: If there are less than 5 and they fit on the same line, I
prefer to list them on the same line as the field name itself. That's
how 'guix style' does it, although it has a tendency to protrude past
our max column width of 80 guideline in other places (that's bug#62303).

Also, we conventionally list the input fields below the arguments field
of a package, although technically it's unimportant.

Toggle quote (8 lines)
> + (outputs '("out" "doc"))
> + (build-system gnu-build-system)
> + (arguments
> + (list
> + #:configure-flags
> + #~(list
> + ;; The configure script ignores --prefix for most of the paths.

Well done with the comment.

Toggle quote (9 lines)
> + (string-append "--exec-prefix=" #$output)
> + (string-append "--mandir=" #$output "/share/man")
> + (string-append "--sbindir=" #$output "/sbin")
> + (string-append "--sysconfdir=" #$output "/etc/apcupsd")
> + (string-append "--with-halpolicydir=" #$output "/share/halpolicy")
> +
> + ;; Put us into the version string.
> + "--with-distname=GNU/Guix"

The name is 'GNU Guix'.

Toggle quote (10 lines)
> + "--disable-install-distdir"
> +
> + ;; State directories.
> + "--localstatedir=/var"
> + "--with-log-dir=/var/log"
> + "--with-pid-dir=/var/run"
> + "--with-lock-dir=/var/run/apcupsd/lock"
> + "--with-nologin=/var/run/apcupsd"
> + "--with-pwrfail-dir=/var/run/apcupsd"

I think /var/run is deprecated in favor of just /run, so we should
configure the package to use this, I think.

Toggle quote (5 lines)
> + (string-append "ac_cv_path_SHUTDOWN=/nope")
> + (string-append "ac_cv_path_APCUPSD_MAIL=/nope")
> + ;; While `wall' is not expanded anywhere, it still is searched for.
> + (string-append "ac_cv_path_WALL=/nope")

I'm not sure if this package is actively developed, but that last issue
should ideally be reported (then cross-referenced here).

Toggle quote (3 lines)
> + ;; Enable additional drivers.
> + "--enable-test"

Is '--enable-test' useful? What does it do?

Toggle quote (18 lines)
> + "--enable-usb"
> + "--enable-modbus-usb")
> + #:tests? #f ; There are no tests.
> + #:modules (cons '(ice-9 ftw) %default-gnu-modules)
> + #:phases
> + #~(modify-phases %standard-phases
> + ;; These are not installed, so trick Make into thinking they were
> + ;; already generated. Allows us not to depend on mandoc, util-linux.
> + (add-before 'configure 'touch-txt-docs
> + (lambda _
> + (for-each (lambda (f)
> + (call-with-output-file f close-port))
> + '("doc/apcupsd.man.txt"
> + "doc/apcaccess.man.txt"
> + "doc/apctest.man.txt"
> + "doc/apccontrol.man.txt"
> + "doc/apcupsd.conf.man.txt"))))

I think I'd rather depend on these than introduce hacks like below.
These are only needed as native inputs anyway, right?

Toggle quote (23 lines)
> + (add-after 'build 'build-manual
> + (lambda _
> + (invoke "make" "-C" "doc/manual" "manual.html")))
> + (add-after 'install-license-files 'move-doc
> + (lambda _
> + (let ((target (string-append #$output:doc
> + "/share/doc/"
> + (strip-store-file-name #$output))))
> + (mkdir-p target)
> + (for-each (lambda (f)
> + (copy-file (string-append "doc/manual/" f)
> + (string-append target "/" f)))
> + (scandir "doc/manual"
> + (lambda (f)
> + (or (string-suffix? ".png" f)
> + (string-suffix? ".html" f))))))))
> + ;; If sending mails is required, use proper mail program.
> + (add-after 'install 'remove-smtp
> + (lambda _
> + (delete-file (string-append #$output "/sbin/smtp"))))
> + ;; The configuration files and scripts are not really suitable for
> + ;; Guix, and our service provides its own version anyway. So nuke

I'd use the more peaceful 'remove' or 'delete' instead of 'nuke'.

Toggle quote (4 lines)
> + ;; these to make sure `apcupsd' and `apctest' executed without any
> + ;; arguments fail. `apctest' actually segfaults, but only after
> + ;; printing an error ¯\_(ツ)_/¯.

Please don't embed emojis in the source :-).

Toggle quote (4 lines)
> + (add-after 'install 'remove-etc-apcupsd
> + (lambda _
> + (delete-file-recursively (string-append #$output "/etc/apcupsd")))))))

Break this long line so it fits under 80 columns (our code style
guideline).

Toggle quote (2 lines)
> + (home-page "https://www.apcupsd.org")

I'd use 'http', since their TLS cert is now invalid.

Toggle quote (2 lines)
> + (synopsis "A daemon for controlling APC UPSes")

s/A // (as hinted by 'guix lint').

Toggle quote (2 lines)
> + (description "Apcupsd can be used for power mangement and controlling most

s/mangement/management/

Toggle quote (5 lines)
> +of APC’s UPS models on Unix and Windows machines. Apcupsd works with most of
> +APC’s Smart-UPS models as well as most simple signalling models such a Back-UPS,
> +and BackUPS-Office.")
> + (license license:gpl2)))

I think it's actually license:gpl2+, according to
apcupsd-3.14.14/COPYING, which says:

Toggle snippet (9 lines)
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

In this case for most file the last sentence applies, some other
explicitly mention 'or any later version'. That's also supported by the
debian/copyright file.

Another thing found: the doc output doesn't build reproducibly, as
shown by:

Toggle snippet (4 lines)
$ ./pre-inst-env guix build --no-grafts --check -K
guix build: error: derivation `/gnu/store/r62j72bd3an8k2fbmaiil5hma32syxdy-apcupsd-3.14.14.drv' may not be deterministic: output `/gnu/store/bhwkrgkqahgs5h8crcfyacvnwp7rj390-apcupsd-3.14.14-doc' differs from `/gnu/store/bhwkrgkqahgs5h8crcfyacvnwp7rj390-apcupsd-3.14.14-doc-check'

Let's see why:

$ diff -r /gnu/store/bhwkrgkqahgs5h8crcfyacvnwp7rj390-apcupsd-3.14.14-doc{,-check}
diff -r /gnu/store/bhwkrgkqahgs5h8crcfyacvnwp7rj390-apcupsd-3.14.14-doc/share/doc/apcupsd-3.14.14/manual.html /gnu/store/bhwkrgkqahgs5h8crcfyacvnwp7rj390-apcupsd-3.14.14-doc-check/share/doc/apcupsd-3.14.14/manual.html
376c376
< <div class="line">February 5, 2025 06:54:22</div>
---
Toggle quote (2 lines)
> <div class="line">February 5, 2025 06:54:50</div>

Ah, the classic date time stamp. You'll want to neuter it in the source
or in the built html file (with a preference for the former).

Could you please send a v2?

--
Thanks,
Maxim
Maxim Cournoyer wrote 1 months ago
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)
87frks98fm.fsf@gmail.com
Hi,

Tomas Volf <~@wolfsden.cz> writes:

Toggle quote (6 lines)
> --- /dev/null
> +++ b/gnu/packages/power.scm
> @@ -0,0 +1,125 @@
> +;;; Copyright (C) 2025 Tomas Volf <~@wolfsden.cz>
> +;;; Copyright (C) 2023 Raven Hallsby <karl@hallsby.com>

One last thing; I don't see Raven listed in a 'Co-authored-by:' git
trailer in the commit message; should they? Or otherwise mention
plainly this work was based on their previous work, made available
'$where'.

--
Thanks,
Maxim
Maxim Cournoyer wrote 1 months ago
Re: [bug#75528] [PATCH 2/2] services: Add power.
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
878qqk8q96.fsf@gmail.com
Hi Tomas,

Tomas Volf <~@wolfsden.cz> writes:

Toggle quote (2 lines)
> * gnu/services/power.scm: New file.

I see there's already a gnu/services/pm.scm file; it seems we don't need
another one? Or was it a conscious decision?

[...]

Toggle quote (5 lines)
> +The @code{(gnu services power)} module provides a service definition for
> +@uref{http://www.apcupsd.org/, apcupsd}, a utility to interact with APC
> +UPSes. Apcupsd also works with some OEM-branded products manufactured
> +by APC.

I'd introduce a few @acronym here to help readers, e.g. for OEM and UPS.

Toggle quote (7 lines)
> +@defvar apcupsd-service-type
> +The service type for apcupsd. For USB UPSes no configuration is
> +necessary, however tweaking some fields to better suit your needs might
> +be desirable. The defaults are taken from the upstream configuration
> +and they are not very conservative (@code{battery-level} of 5% is too
> +low in my opinion).

I'd say, (for example, the default @code{battery-level} of 5% may be
considered too low by some), to keep it neutral.

Toggle quote (16 lines)
> +The default event handlers do send emails, read more in
> +@ref{apcupsd-event-handlers}.
> +
> +@lisp
> +(service apcupsd-service-type)
> +@end lisp
> +@end defvar
> +
> +@deftp {Data Type} apcupsd-configuration
> +
> +Available @code{apcupsd-configuration} fields are:
> +
> +@table @asis
> +@item @code{package} (default: @code{apcupsd}) (type: package)
> +Package to use.

That's more conventionally named with the same of the package,
e.g. 'acpupsd', not 'package'. I'd word the description as 'The
@code{apcupsd} package to use'.

Toggle quote (4 lines)
> +@item @code{shepherd-base-name} (default: @code{apcupsd}) (type: symbol)
> +Base name of the shepherd service. You can add the service multiple
> +times with different prefix to manage multiple UPSes.

s/prefix/prefixes/
aaa
Toggle quote (7 lines)
> +
> +@item @code{auto-start?} (default: @code{#t}) (type: boolean)
> +Should the shepherd service auto-start?
> +
> +@item @code{pid-file} (default: @code{"/var/run/apcupsd.pid"}) (type: string)
> +Path to the pid file.

All file names becoming /run/ prefixed instead of /var/run, based on a
previous comment.

We don't use path in GNU to denote file names, we use 'file names'.
That's mentioned in info '(standards) GNU Manuals':

Please do not use the term "pathname" that is used in Unix
documentation; use "file name" (two words) instead. We use the term
"path" only for search paths, which are lists of directory names.

Toggle quote (5 lines)
> +
> +@item @code{debug-level} (default: @code{0}) (type: integer)
> +Logging verbosity. Bigger number means more logs. In the source code I
> +saw up to @code{300}, so for all logs, @code{999} seems reasonable.

I think it's best to not use first person pronoun in the manual to keep
it a bit more formal. So instead of 'I saw', this could be reworded to:
'The source code uses up to @code{300} as debug level value, so a value
of @code{999} seems reasonable to enable all logs.' or similar.

Toggle quote (15 lines)
> +
> +@item @code{run-dir} (default: @code{"/var/run/apcupsd"}) (type: string)
> +Directory containing runtime information. You need to change this if
> +you desire to run multiple instances of the daemon.
> +
> +@item @code{name} (type: maybe-string)
> +Use this to give your UPS a name in log files and such. This is
> +particularly useful if you have multiple UPSes. This does not set the
> +EEPROM. It should be 8 characters or less.
> +
> +@item @code{cable} (default: @code{usb}) (type: enum-cable)
> +The type of cable connecting the UPS to your computer. Possible generic
> +choices are @code{'simple}, @code{'smart}, @code{'ether} and
> +@code{'usb}. Or a specific cable model number may be used:

nitpick: I'd reword to 'Alternatively, a specific [...]', which reads
better to me.

[...]

Toggle quote (4 lines)
> +@item usb
> +A null string setting enables autodetection, which is the best choice
> +for most installations.

nitpick: My dictionary (aspell) prefers auto-detection.

[...]

Toggle quote (6 lines)
> +@anchor{apcupsd-event-handlers}
> +@deftp {Data Type} apcupsd-event-handlers
> +
> +For description of the events please refer to the @command{apcupsd}'s
> +manual, which can be found in the @samp{apcupsd-doc} package.

I think you also meant it is in the @samp{doc} output, not in a
different package, right? In any case, I'd refer them to the man page
of apccontrol, which details the events. Also, to avoid the odd
rendering as "the ‘apcupsd’’s manual" in info, I'd instead rephrase to:

For a description of the events please refer to @samp{man 8 apccontrol}.

Toggle quote (5 lines)
> +
> +Each handler shall be a gexp. It is spliced into the control script for
> +the daemon. In addition to the standard Guile programming environment,
> +these procedures and variables are also available.

s/these/the following/ [...] s/available./available:/
Since these are spliced in the control script and thus not at the top
level, users won't be able to import Guile modules they may want to use,
so it seems there should be a field to define which Guile modules should
be imported and used by default in the configuration.

Toggle quote (5 lines)
> +
> +@table @code
> +@item conf
> +Variable containing path to the configuration file.

s/path/file name/, anywhere this appears.

Toggle quote (4 lines)
> +
> +@item powerfail-file
> +Variable containing path to the powerfail file.

Ditto.

Toggle quote (9 lines)
> +@item cmd
> +The event currently being handled.
> +
> +@item name
> +The name of the UPS as specified in the configuration file.
> +
> +@item connected
> +Is @code{"1"} if apcupsd is connected to the UPS via a serial port (or a

@command{apcupsd}

Also, any reason why these integer values are expressed as strings?

Toggle quote (3 lines)
> +USB port). In most configurations, this will be the case. In the case
> +of a Slave machine where apcupsd is not directly connected to the UPS,

s/Slave/slave/ ? Could be nicer to use a neutral alternative like
'follower machine'.

Toggle quote (94 lines)
> +this value will be @code{"0"}.
> +
> +@item powered
> +Is @code{"1"} if the computer on which @command{apcupsd} is running is
> +powered by the UPS and @code{"0"} if not. At the moment, this value is
> +unimplemented and always @code{"0"}.
> +
> +@item (err @var{fmt} @var{args...})
> +Wrapper around @code{format} outputting to @code{(current-error-port)}.
> +
> +@item (wall @var{fmt} @var{args...})
> +Wrapper around @code{format} outputting via @command{wall}.
> +
> +@item (apcupsd @var{args...})
> +Call @command{apcupsd} while passing the correct configuration file and
> +all the arguments.
> +
> +@item (mail-to-root @var{subject} @var{body})
> +Send an email to the local administrator. This procedure assumes the
> +@command{sendmail} is located at @command{/run/privileged/bin/sendmail}
> +(as would be the case with @code{opensmtpd-service-type}).
> +
> +@end table
> +
> +Available @code{apcupsd-event-handlers} fields are:
> +
> +@table @asis
> +@item @code{killpower} (type: gexp)
> +Handler for killpower event.
> +
> +@item @code{commfailure} (type: gexp)
> +Handler for commfailure event.
> +
> +@item @code{commok} (type: gexp)
> +Handler for commfailure event.
> +
> +@item @code{powerout} (type: gexp)
> +Handler for powerout event.
> +
> +@item @code{onbattery} (type: gexp)
> +Handler for onbattery event.
> +
> +@item @code{offbattery} (type: gexp)
> +Handler for offbattery event.
> +
> +@item @code{mainsback} (type: gexp)
> +Handler for mainsback event.
> +
> +@item @code{failing} (type: gexp)
> +Handler for failing event.
> +
> +@item @code{timeout} (type: gexp)
> +Handler for timeout event.
> +
> +@item @code{loadlimit} (type: gexp)
> +Handler for loadlimit event.
> +
> +@item @code{runlimit} (type: gexp)
> +Handler for runlimit event.
> +
> +@item @code{doreboot} (type: gexp)
> +Handler for doreboot event.
> +
> +@item @code{doshutdown} (type: gexp)
> +Handler for doshutdown event.
> +
> +@item @code{annoyme} (type: gexp)
> +Handler for annoyme event.
> +
> +@item @code{emergency} (type: gexp)
> +Handler for emergency event.
> +
> +@item @code{changeme} (type: gexp)
> +Handler for changeme event.
> +
> +@item @code{remotedown} (type: gexp)
> +Handler for remotedown event.
> +
> +@item @code{startselftest} (type: gexp)
> +Handler for startselftest event.
> +
> +@item @code{endselftest} (type: gexp)
> +Handler for endselftest event.
> +
> +@item @code{battdetach} (type: gexp)
> +Handler for battdetach event.
> +
> +@item @code{battattach} (type: gexp)
> +Handler for battattach event.
> +
> +@end table
> +
> +@end deftp

nitpick: I'd remove the newline between the two @end above (I know, it
gets generated this way...).

[...]

Toggle quote (2 lines)
> +;;; Copyright (C) 2025 Tomas Volf <~@wolfsden.cz>

We use the unicode symbol (©) for copyright throughout Guix; please use
it too, for uniformity.

Toggle quote (21 lines)
> +
> +;;;; Commentary:
> +
> +;;; Power-related services.
> +
> +;;;; Code:
> +
> +(define-module (gnu services power)
> + #:use-module (srfi srfi-1)
> + #:use-module (srfi srfi-26)
> + #:use-module (ice-9 match)
> + #:use-module (gnu)
> + #:use-module (gnu packages admin)
> + #:use-module (gnu packages linux)
> + #:use-module (gnu packages power)
> + #:use-module (gnu services)
> + #:use-module (gnu services configuration)
> + #:use-module (gnu services shepherd)
> + #:use-module (guix packages)
> + #:use-module (guix records)

Please order lexicographically (that is, alphabetically but on things
which are not necessarily words :-)).

Toggle quote (66 lines)
> + #:export (apcupsd-service-type
> +
> + apcupsd-configuration
> + apcupsd-configuration-package
> + apcupsd-configuration-shepherd-base-name
> + apcupsd-configuration-auto-start?
> + apcupsd-configuration-pid-file
> + apcupsd-configuration-debug-level
> + apcupsd-configuration-run-dir
> + apcupsd-configuration-name
> + apcupsd-configuration-cable
> + apcupsd-configuration-type
> + apcupsd-configuration-device
> + apcupsd-configuration-poll-time
> + apcupsd-configuration-on-batery-delay
> + apcupsd-configuration-battery-level
> + apcupsd-configuration-remaining-minutes
> + apcupsd-configuration-timeout
> + apcupsd-configuration-annoy-interval
> + apcupsd-configuration-annoy-delay
> + apcupsd-configuration-no-logon
> + apcupsd-configuration-kill-delay
> + apcupsd-configuration-net-server
> + apcupsd-configuration-net-server-ip
> + apcupsd-configuration-net-server-port
> + apcupsd-configuration-net-server-events-file
> + apcupsd-configuration-net-server-events-file-max-size
> + apcupsd-configuration-class
> + apcupsd-configuration-mode
> + apcupsd-configuration-stat-time
> + apcupsd-configuration-log-stats
> + apcupsd-configuration-data-time
> + apcupsd-configuration-facility
> + apcupsd-configuration-event-handlers
> +
> + apcupsd-event-handlers-annoyme
> + apcupsd-event-handlers-battattach
> + apcupsd-event-handlers-battdetach
> + apcupsd-event-handlers-changeme
> + apcupsd-event-handlers-commfailure
> + apcupsd-event-handlers-commok
> + apcupsd-event-handlers-doreboot
> + apcupsd-event-handlers-doshutdown
> + apcupsd-event-handlers-emergency
> + apcupsd-event-handlers-endselftest
> + apcupsd-event-handlers-failing
> + apcupsd-event-handlers-killpower
> + apcupsd-event-handlers-loadlimit
> + apcupsd-event-handlers-mainsback
> + apcupsd-event-handlers-offbattery
> + apcupsd-event-handlers-onbattery
> + apcupsd-event-handlers-powerout
> + apcupsd-event-handlers-remotedown
> + apcupsd-event-handlers-runlimit
> + apcupsd-event-handlers-startselftest
> + apcupsd-event-handlers-timeout))
> +
> +(define-configuration/no-serialization apcupsd-event-handlers
> + (killpower
> + (gexp
> + #~((wall "Apccontrol doing: apcupsd --killpower on UPS ~a" name)
> + (sleep 10)
> + (apcupsd "--killpower")
> + (wall "Apccontrol has done: apcupsd --killpower on UPS ~a" name)))
> + "Handler for killpower event.")

In many places in your writing, I find that it skips adding 'the'
(definite article), which I've learned should be use when describing
something specific, such as here with the specific handlers:

"Handler for the @code{killpower} event."

In case it helps, I've found this page seems useful in briefly
explaining when to use 'the' [0].


Toggle quote (99 lines)
> + (commfailure
> + (gexp
> + #~((let ((msg (format #f "~a Communications with UPS ~a lost."
> + (gethostname) name)))
> + (mail-to-root msg msg))
> + (wall "Warning: communications lost with UPS ~a" name)))
> + "Handler for commfailure event.")
> + (commok
> + (gexp
> + #~((let ((msg (format #f "~a Communications with UPS ~a restored."
> + (gethostname) name)))
> + (mail-to-root msg msg))
> + (wall "Communications restored with UPS ~a" name)))
> + "Handler for commfailure event.")
> + (powerout
> + (gexp
> + #~(#t))
> + "Handler for powerout event.")
> + (onbattery
> + (gexp
> + #~((let ((msg (format #f "~a UPS ~a Power Failure !!!"
> + (gethostname) name)))
> + (mail-to-root msg msg))
> + (wall "Power failure on UPS ~a. Running on batteries." name)))
> + "Handler for onbattery event.")
> + (offbattery
> + (gexp
> + #~((let ((msg (format #f "~a UPS ~a Power has returned."
> + (gethostname) name)))
> + (mail-to-root msg msg))
> + (wall "Power has returned on UPS ~a..." name)))
> + "Handler for offbattery event.")
> + (mainsback
> + (gexp
> + #~((when (file-exists? powerfail-file)
> + (wall "Continuing with shutdown."))))
> + "Handler for mainsback event.")
> + (failing
> + (gexp
> + #~((wall "Battery power exhausted on UPS ~a. Doing shutdown." name)))
> + "Handler for failing event.")
> + (timeout
> + (gexp
> + #~((wall "Battery time limit exceeded on UPS ~a. Doing shutdown." name)))
> + "Handler for timeout event.")
> + (loadlimit
> + (gexp
> + #~((wall "Remaining battery charge below limit on UPS ~a. Doing shutdown." name)))
> + "Handler for loadlimit event.")
> + (runlimit
> + (gexp
> + #~((wall "Remaining battery runtime below limit on UPS ~a. Doing shutdown." name)))
> + "Handler for runlimit event.")
> + (doreboot
> + (gexp
> + #~((wall "UPS ~a initiating Reboot Sequence" name)
> + (system* #$(file-append shepherd "/sbin/reboot"))))
> + "Handler for doreboot event.")
> + (doshutdown
> + (gexp
> + #~((wall "UPS ~a initiated Shutdown Sequence" name)
> + (system* #$(file-append shepherd "/sbin/halt"))))
> + "Handler for doshutdown event.")
> + (annoyme
> + (gexp
> + #~((wall "Power problems with UPS ~a. Please logoff." name)))
> + "Handler for annoyme event.")
> + (emergency
> + (gexp
> + #~((wall "Emergency Shutdown. Possible battery failure on UPS ~a." name)))
> + "Handler for emergency event.")
> + (changeme
> + (gexp
> + #~((let ((msg (format #f "~a UPS ~a battery needs changing NOW."
> + (gethostname) name)))
> + (mail-to-root msg msg))
> + (wall "Emergency! Batteries have failed on UPS ~a. Change them NOW." name)))
> + "Handler for changeme event.")
> + (remotedown
> + (gexp
> + #~((wall "Remote Shutdown. Beginning Shutdown Sequence.")))
> + "Handler for remotedown event.")
> + (startselftest
> + (gexp
> + #~(#t))
> + "Handler for startselftest event.")
> + (endselftest
> + (gexp
> + #~(#t))
> + "Handler for endselftest event.")
> + (battdetach
> + (gexp
> + #~(#t))
> + "Handler for battdetach event.")
> + (battattach
> + (gexp
> + #~(#t))
> + "Handler for battattach event."))

Sounds fun :-). I was wondering if the handlers could be of the
maybe-gexp type, and when not provided the handler would not be called
at all? Or does the design of apcupsd mandates that all handlers be
defined?

Toggle quote (10 lines)
> +(define-syntax define-enum
> + (lambda (x)
> + (syntax-case x ()
> + ((_ name values)
> + (let* ((d/n (syntax->datum #'name))
> + (d/predicate (string->symbol
> + (format #f "enum-~a?" d/n)))
> + (d/serialize (string->symbol
> + (format #f "serialize-enum-~a" d/n))))

Even for internal bindings, better variable names would help reading the
code.

Toggle quote (8 lines)
> + (with-syntax
> + ((predicate (datum->syntax x d/predicate))
> + (serialize (datum->syntax x d/serialize)))
> + #'(begin
> + (define (predicate value)
> + (memq value values))
> + (define serialize serialize-symbol))))))))

That is a nice minimal enum for using with define-configuration. Note
that Guile has some kind of very limited enum from (rnrs enums), but I
don't think they'd be too useful here. See for example
'make-enumeration' from (rnrs enums), which I used in for the
ntp-server-types in (gnu services networking).

Toggle quote (38 lines)
> +(define mangle-field-name
> + (match-lambda
> + ('name "UPSNAME")
> + ('cable "UPSCABLE")
> + ('type "UPSTYPE")
> + ('device "DEVICE")
> + ('poll-time "POLLTIME")
> + ('lock-dir "LOCKFILE")
> + ('power-fail-dir "PWRFAILDIR")
> + ('no-login-dir "NOLOGINDIR")
> + ('on-batery-delay "ONBATTERYDELAY")
> + ('battery-level "BATTERYLEVEL")
> + ('remaining-minutes "MINUTES")
> + ('timeout "TIMEOUT")
> + ('annoy-interval "ANNOY")
> + ('annoy-delay "ANNOYDELAY")
> + ('no-logon "NOLOGON")
> + ('kill-delay "KILLDELAY")
> + ('net-server "NETSERVER")
> + ('net-server-ip "NISIP")
> + ('net-server-port "NISPORT")
> + ('net-server-events-file "EVENTSFILE")
> + ('net-server-events-file-max-size "EVENTSFILEMAX")
> + ('class "UPSCLASS")
> + ('mode "UPSMODE")
> + ('stat-time "STATTIME")
> + ('stat-file "STATFILE")
> + ('log-stats "LOGSTATS")
> + ('data-time "DATATIME")
> + ('facility "FACILITY")))
> +
> +(define (serialize-string field-name value)
> + #~(format #f "~a ~a\n" #$(mangle-field-name field-name) '#$value))
> +(define serialize-symbol serialize-string)
> +(define serialize-integer serialize-string)
> +(define (serialize-boolean field-name value)
> + #~(format #f "~a ~a\n" #$(mangle-field-name field-name) #$(if value "on" "off")))

You'll want to break the above line.

Toggle quote (15 lines)
> +
> +(define-maybe string)
> +
> +(define-enum cable '( simple smart ether usb
> + 940-0119A 940-0127A 940-0128A 940-0020B 940-0020C
> + 940-0023A 940-0024B 940-0024C 940-1524C 940-0024G
> + 940-0095A 940-0095B 940-0095C 940-0625A MAM-04-02-2000))
> +(define-enum type '(apcsmart usb net snmp netsnmp dumb pcnet modbus test))
> +(define-enum no-logon '(disable timeout percent minutes always))
> +(define-enum class '(standalone shareslave sharemaster))
> +(define-enum mode '(disable share))
> +
> +(define-configuration apcupsd-configuration
> + (package (package apcupsd) "Package to use.")

Please stick to the convention where the name of the package is the name
of the fi
This message was truncated. Download the full message here.
Tomas Volf wrote 1 months ago
Re: [bug#75528] [PATCH 1/2] gnu: Add apcupsd.
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 75528@debbugs.gnu.org)
87pljrf69g.fsf@wolfsden.cz
Hello Maxim,

thank you for the review, replies below.

Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (14 lines)
> Hi Tomas,
>
> Tomas Volf <~@wolfsden.cz> writes:
>
>> * gnu/packages/power.scm (apcupsd): New variable.
>
> 'guix lint' says:
>
> gnu/packages/power.scm:120:14: apcupsd@3.14.14: no article allowed at the beginning of the synopsis
> gnu/packages/power.scm:119:15: apcupsd@3.14.14: TLS certificate error: X.509 server certificate for 'www.apcupsd.org' does not match: CN=sf.net
>
>
> Please fix these.

Both fixed.

Toggle quote (24 lines)
>
>> Change-Id: I74f59cd1fa2a13954117ff1683a10a84576cc839
>> ---
>> gnu/local.mk | 1 +
>> gnu/packages/power.scm | 125 +++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 126 insertions(+)
>> create mode 100644 gnu/packages/power.scm
>>
>> diff --git a/gnu/local.mk b/gnu/local.mk
>> index 855f2acccc..6ca7bf68ac 100644
>> --- a/gnu/local.mk
>> +++ b/gnu/local.mk
>> @@ -557,6 +557,7 @@ GNU_SYSTEM_MODULES = \
>> %D%/packages/polkit.scm \
>> %D%/packages/popt.scm \
>> %D%/packages/potassco.scm \
>> + %D%/packages/power.scm \
>
> This change should be mentioned in the change log as well, e.g.:
>
> * gnu/local.mk (GNU_SYSTEM_MODULES): Register it.
>
> [...]

I used slightly different message, since the previous line mentions "new
variable", but the object being registered is whole file.

Toggle quote (25 lines)
>
>> +(define-public apcupsd
>> + (package
>> + (name "apcupsd")
>> + (version "3.14.14")
>> + (source (origin
>> + (method url-fetch)
>> + (uri
>> + (string-append
>> + "mirror://sourceforge/" name "/" name " - Stable/" version
>> + "/" name "-" version ".tar.gz"))
>> + (sha256
>> + (base32
>> + "0rwqiyzlg9p0szf3x6q1ppvrw6f6dbpn2rc5z623fk3bkdalhxyv"))))
>> + (native-inputs
>> + (list pkg-config python-docutils))
>> + (inputs
>> + (list libusb libusb-compat))
>
> nitpick: If there are less than 5 and they fit on the same line, I
> prefer to list them on the same line as the field name itself. That's
> how 'guix style' does it, although it has a tendency to protrude past
> our max column width of 80 guideline in other places (that's
> bug#62303).

Fair enough. I do not have strong preference, so I have made it into a
single line.

I have to admit I do not run `guix style' (yes, yes, I know contributing
part of the manual mandates it), since the code produced is often subpar
compared to the hand-crafted variant.

Toggle quote (4 lines)
>
> Also, we conventionally list the input fields below the arguments field
> of a package, although technically it's unimportant.

Moved.

Toggle quote (22 lines)
>
>> + (outputs '("out" "doc"))
>> + (build-system gnu-build-system)
>> + (arguments
>> + (list
>> + #:configure-flags
>> + #~(list
>> + ;; The configure script ignores --prefix for most of the paths.
>
> Well done with the comment.
>
>> + (string-append "--exec-prefix=" #$output)
>> + (string-append "--mandir=" #$output "/share/man")
>> + (string-append "--sbindir=" #$output "/sbin")
>> + (string-append "--sysconfdir=" #$output "/etc/apcupsd")
>> + (string-append "--with-halpolicydir=" #$output "/share/halpolicy")
>> +
>> + ;; Put us into the version string.
>> + "--with-distname=GNU/Guix"
>
> The name is 'GNU Guix'.

I was afraid of the space. :) But seems to work fine, changed.

Toggle quote (14 lines)
>
>> + "--disable-install-distdir"
>> +
>> + ;; State directories.
>> + "--localstatedir=/var"
>> + "--with-log-dir=/var/log"
>> + "--with-pid-dir=/var/run"
>> + "--with-lock-dir=/var/run/apcupsd/lock"
>> + "--with-nologin=/var/run/apcupsd"
>> + "--with-pwrfail-dir=/var/run/apcupsd"
>
> I think /var/run is deprecated in favor of just /run, so we should
> configure the package to use this, I think.

It seems pretty much all programs on my system are using /var/run,
including Shepherd. Only programs that store information in /run seem
to be elogind and dbus.

Is deprecation of /var/run listed somewhere in the manual? I am
surprised Shepherd (since it is actively maintained and core component)
is still in /var/run. I looked and found 42 references to /var/run, but
none with the deprecation notice.

Assuming you will insist on moving it to /run, is there some structure
to follow in that directory? Or just s~/var/run~/run~? What about /var
and /var/log? Should those be moved somewhere as well?

Toggle quote (9 lines)
>
>> + (string-append "ac_cv_path_SHUTDOWN=/nope")
>> + (string-append "ac_cv_path_APCUPSD_MAIL=/nope")
>> + ;; While `wall' is not expanded anywhere, it still is searched for.
>> + (string-append "ac_cv_path_WALL=/nope")
>
> I'm not sure if this package is actively developed, but that last issue
> should ideally be reported (then cross-referenced here).

I did not found a way to report a bug. Nothing is listed in the manual,
and their apcupsd-users mailing list requires JavaScript to sign up (and
does not tell me the email address otherwise).

I did try to blindly send an email, will see if it will be accepted
without being subscribed. If that happens, I will add the link here.

Toggle quote (6 lines)
>
>> + ;; Enable additional drivers.
>> + "--enable-test"
>
> Is '--enable-test' useful? What does it do?

According to the manual

Toggle snippet (3 lines)
This turns on a test driver that is used only for debugging.

Since there does not seem to be any harm in having it on, I enabled it,
since Guix seems to aim for feature complete packages (I assume that is
the reason for everything being so large). But I have no use for it, so
I can turn it off if you would prefer.

Toggle quote (21 lines)
>
>> + "--enable-usb"
>> + "--enable-modbus-usb")
>> + #:tests? #f ; There are no tests.
>> + #:modules (cons '(ice-9 ftw) %default-gnu-modules)
>> + #:phases
>> + #~(modify-phases %standard-phases
>> + ;; These are not installed, so trick Make into thinking they were
>> + ;; already generated. Allows us not to depend on mandoc, util-linux.
>> + (add-before 'configure 'touch-txt-docs
>> + (lambda _
>> + (for-each (lambda (f)
>> + (call-with-output-file f close-port))
>> + '("doc/apcupsd.man.txt"
>> + "doc/apcaccess.man.txt"
>> + "doc/apctest.man.txt"
>> + "doc/apccontrol.man.txt"
>> + "doc/apcupsd.conf.man.txt"))))
>
> I think I'd rather depend on these than introduce hacks like below.

The "hacks" *below* would still be required. The HTML manual is not
built nor installed by default, and there is no target to invoke to
install them. So both 'build-manual and 'move-doc phases would still be
required.

We could get rid of the "hack" *above* ('touch-txt-docs), true. It is
worth those two additional inputs?

Toggle quote (2 lines)
> These are only needed as native inputs anyway, right?

Yes.

Toggle quote (26 lines)
>
>> + (add-after 'build 'build-manual
>> + (lambda _
>> + (invoke "make" "-C" "doc/manual" "manual.html")))
>> + (add-after 'install-license-files 'move-doc
>> + (lambda _
>> + (let ((target (string-append #$output:doc
>> + "/share/doc/"
>> + (strip-store-file-name #$output))))
>> + (mkdir-p target)
>> + (for-each (lambda (f)
>> + (copy-file (string-append "doc/manual/" f)
>> + (string-append target "/" f)))
>> + (scandir "doc/manual"
>> + (lambda (f)
>> + (or (string-suffix? ".png" f)
>> + (string-suffix? ".html" f))))))))
>> + ;; If sending mails is required, use proper mail program.
>> + (add-after 'install 'remove-smtp
>> + (lambda _
>> + (delete-file (string-append #$output "/sbin/smtp"))))
>> + ;; The configuration files and scripts are not really suitable for
>> + ;; Guix, and our service provides its own version anyway. So nuke
>
> I'd use the more peaceful 'remove' or 'delete' instead of 'nuke'.

Uh, sure. Done.

Toggle quote (7 lines)
>
>> + ;; these to make sure `apcupsd' and `apctest' executed without any
>> + ;; arguments fail. `apctest' actually segfaults, but only after
>> + ;; printing an error ¯\_(ツ)_/¯.
>
> Please don't embed emojis in the source :-).

Technically kaomoji (顔文字), not emoji (絵文字), but point taken. :)

Toggle quote (8 lines)
>
>> + (add-after 'install 'remove-etc-apcupsd
>> + (lambda _
>> + (delete-file-recursively (string-append #$output "/etc/apcupsd")))))))
>
> Break this long line so it fits under 80 columns (our code style
> guideline).

I thought (well, still do), that going 4 characters over the limit was
worth the increase in readability. Nevertheless, I have split it into
two lines.

Toggle quote (5 lines)
>
>> + (home-page "https://www.apcupsd.org")
>
> I'd use 'http', since their TLS cert is now invalid.

Done as part of resolving the ling warning.

Toggle quote (5 lines)
>
>> + (synopsis "A daemon for controlling APC UPSes")
>
> s/A // (as hinted by 'guix lint').

Same.

Toggle quote (5 lines)
>
>> + (description "Apcupsd can be used for power mangement and controlling most
>
> s/mangement/management/

Nice catch, fixed.

Toggle quote (22 lines)
>
>> +of APC’s UPS models on Unix and Windows machines. Apcupsd works with most of
>> +APC’s Smart-UPS models as well as most simple signalling models such a Back-UPS,
>> +and BackUPS-Office.")
>> + (license license:gpl2)))
>
> I think it's actually license:gpl2+, according to
> apcupsd-3.14.14/COPYING, which says:
>
> Each version is given a distinguishing version number. If the Program
> specifies a version number of this License which applies to it and "any
> later version", you have the option of following the terms and conditions
> either of that version or of any later version published by the Free
> Software Foundation. If the Program does not specify a version number of
> this License, you may choose any version ever published by the Free Software
> Foundation.
>
>
> In this case for most file the last sentence applies, some other
> explicitly mention 'or any later version'. That's also supported by the
> debian/copyright file.

I do not think this is correct. When I check
apcupsd-3.14.14/src/apcupsd.c file, I see this in its header:

Toggle snippet (17 lines)
* Copyright (C) 1999-2005 Kern Sibbald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General
* Public License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1335, USA.

There is no mention of "any later version". Does that not make the
combined work GPL-2.0-only, even if other files would be under
GPL-2.0-or-later (I did no check whether they are)?

I did not find debian/copyright file in the source code.

Toggle quote (11 lines)
>
> Another thing found: the doc output doesn't build reproducibly, as
> shown by:
>
> $ ./pre-inst-env guix build --no-grafts --check -K
> guix build: error: derivation `/gnu/store/r62j72bd3an8k2fbmaiil5hma32syxdy-apcupsd-3.14.14.drv' may not be deterministic: output `/gnu/store/bhwkrgkqahgs5h8crcfyacvnwp7rj390-apcupsd-3.14.14-doc' differs from `/gnu/store/bhwkrgkqahgs5h8crcfyacvnwp7rj390-apcupsd-3.14.14-doc-check'
>
> Let's see why:
>
> $ diff -r /gnu/store/bhwkrgkqahgs5h8crcfyacvnwp7rj390-apcupsd-3.14.14-doc{,-check}

This is great, I did not know that it names the store item -check. Very
useful, thank you.

Toggle quote (11 lines)
> diff -r
> /gnu/store/bhwkrgkqahgs5h8crcfyacvnwp7rj390-apcupsd-3.14.14-doc/share/doc/apcupsd-3.14.14/manual.html
> /gnu/store/bhwkrgkqahgs5h8crcfyacvnwp7rj390-apcupsd-3.14.14-doc-check/share/doc/apcupsd-3.14.14/manual.html
> 376c376
> < <div class="line">February 5, 2025 06:54:22</div>
> ---
>> <div class="line">February 5, 2025 06:54:50</div>
>
> Ah, the classic date time stamp. You'll want to neuter it in the source
> or in the built html file (with a preference for the former).

Nice find, I have missed this. I have patched the source code of the
manual to not embed the date and time, seems to work now.

Toggle quote (3 lines)
>
> Could you please send a v2?

Definitely, I will go through your review for the service file as well,
and once I have all the answers (both here and there), will send v2.

Once more thanks for the review,
Tomas

--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
-----BEGIN PGP SIGNATURE-----

iQJCBAEBCgAsFiEEt4NJs4wUfTYpiGikL7/ufbZ/wakFAmeo04sOHH5Ad29sZnNk
ZW4uY3oACgkQL7/ufbZ/wakexA/9FXapBD7LTzTio9FuXl/LZfl0plqmid/KZEtn
vGP4MBKSCb/IpQaYi1XQuXQEtBzoLMDprJraelLfMVck5I5v0Sqfsi5pZSSUHcHr
ZzN2/flboVWyaol/Wee5zTFJUcAM8H2Pc61lNemwR8360Gm0qLIWXgIEiVw/k2I+
ViBbPXDo8zJJjTYT6L2GMFwVu2Rn6Ww9khd4U1mUy0IXxcu7rVgzoAJsA3He7Q1f
TY7Sx4j5ggVZFcoTAc6VIYxRDHb78eiTCTObbPnSCODE2cCsmrLvuoW7qy+GhMKP
ZkU/SLKoxx+H7xQ0Wp6QKPshUsAj8mKkK5LWtq0uT6LuLsY06HxxEihIynNSvwjc
8KA+zW7RzUqSEfne4V3xi6YXpyR396A7QmkT44Vy5Knd1zKaK2TMtJw38gYzVR/Y
ACLmO0MylPthoHQgyKsdWBQT4Ynmstetw5jAbLGgqvxMhW9Otwhzo4auSbX0+qXI
+EeXCQN8IzEIFWUFcKDm/+QqHyPtTDMcg/FjjYlbH16bJQzT0YX2pWWUt7xuyLlD
FtSsvw80F4mdCaN0upKWRasFoqLarqmwGc+TRfEBZST4CBQBOZ8g5WsI+0/dZuAV
FpJ2wcmoUhNkzeFA3uSUfsNCNsIEE9SjhIgI+IB8HaBtWv7l+4uumMyt3DOw/fPf
OZSCClI=
=ft5W
-----END PGP SIGNATURE-----

Tomas Volf wrote 1 months ago
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 75528@debbugs.gnu.org)
87lduff5jn.fsf@wolfsden.cz
Hi,

Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (15 lines)
> Hi,
>
> Tomas Volf <~@wolfsden.cz> writes:
>
>> --- /dev/null
>> +++ b/gnu/packages/power.scm
>> @@ -0,0 +1,125 @@
>> +;;; Copyright (C) 2025 Tomas Volf <~@wolfsden.cz>
>> +;;; Copyright (C) 2023 Raven Hallsby <karl@hallsby.com>
>
> One last thing; I don't see Raven listed in a 'Co-authored-by:' git
> trailer in the commit message; should they? Or otherwise mention
> plainly this work was based on their previous work, made available
> '$where'.

I did not know about co-authored trailer. Should it be used even in
situations where I just took a look at his version online without any
active involvement? Currently I have modified the commit message to:

Toggle snippet (14 lines)
gnu: Add apcupsd.

Some parts were taken or inspired by the work of Raven Hallsby available
here[0]. For that reason I have added his copyright as well (I asked for the
permission).

0: https://raw.githubusercontent.com/KarlJoad/guix/9013b5ac3fadb48fad2e7ef1fbfaa4848dcb922a/gnu/packages/power.scm

* gnu/packages/power.scm (apcupsd): New variable.
* gnu/local.mk (GNU_SYSTEM_MODULES): Register the new file.

Change-Id: I0e4b2f50c8adf0f96d140e2be0f79e3740f4955c

Is that sufficient?

Tomas

--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
-----BEGIN PGP SIGNATURE-----

iQJCBAEBCgAsFiEEt4NJs4wUfTYpiGikL7/ufbZ/wakFAmeo1ywOHH5Ad29sZnNk
ZW4uY3oACgkQL7/ufbZ/wakakQ/+Icqd2dgQ/CeDbt0YUQW5ZOoGvxusDcC37Cir
RyMl6sw3w/G+g9Om9KVRHFAu6yDgrrukGpEuHXv3bD7YgwwYUkbiyUD26qeMFZt2
oj23ujzH/gPoyDJP01N2nuFDZx2x4c5E6K4K9Hx3Eoh8B3Ot5KKHhIngbaPAO9TH
dnEmRXnUH5eHAzEOl7QoY8WpV6gn7TQuxHOQdhM/Fc4ypbgmgq2w+Z0cYJSBXe3V
HDNeJhxKb06+KJvsRsOsMUM1pZdZo9SUCdPzjoxy0hk+mT5t39TTNFGzk88rcC7T
37VElKMiDb6SzqXiJ+Ijiltv0CfibcrbSOZQCrRI4MfIIxOn0K+xAfrwcEwTg1rI
nqIc87aKQ5H9qsEzKKWPcCaXefbNoAb3c6J5XVml9LN8h1+/Uvh3HbwXKw/i/F+1
QDbmiTcjauP5Sa2uK88pNMqoQG5bGJuSu/xqKTKHVlu1tzQRF8URiKNnQ4eMuwM5
ns/NaRZTYnKQWZlCi1LxBwZHbqa+TSAziLixhpuc7VWIDTuZiMrzXuiM/jK7q1Ko
kMNqBPPtvY1tNxl63d41hVR0Tx5lnCmT+uG0UQPNYpYQYRJO7MbkxXTDdPZfp0NT
A/n1YWABfMkK35ALxF5E3JicQ707o4u5T77YQ4k+j1p/S4t8h6DR8qvPZr74Utbq
SqKLf08=
=po+1
-----END PGP SIGNATURE-----

Maxim Cournoyer wrote 4 weeks ago
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)
87y0yal7r4.fsf@gmail.com
Hi Tomas,

Tomas Volf <~@wolfsden.cz> writes:

Toggle quote (23 lines)
> Hi,
>
> Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:
>
>> Hi,
>>
>> Tomas Volf <~@wolfsden.cz> writes:
>>
>>> --- /dev/null
>>> +++ b/gnu/packages/power.scm
>>> @@ -0,0 +1,125 @@
>>> +;;; Copyright (C) 2025 Tomas Volf <~@wolfsden.cz>
>>> +;;; Copyright (C) 2023 Raven Hallsby <karl@hallsby.com>
>>
>> One last thing; I don't see Raven listed in a 'Co-authored-by:' git
>> trailer in the commit message; should they? Or otherwise mention
>> plainly this work was based on their previous work, made available
>> '$where'.
>
> I did not know about co-authored trailer. Should it be used even in
> situations where I just took a look at his version online without any
> active involvement? Currently I have modified the commit message to:

I believe if you reused enough of the code to make it necessary to add
their name to the copyright notice, then a Co-authored-by git trailer
would make sense (whether they were actively involved in your version or
not).

Toggle quote (15 lines)
> gnu: Add apcupsd.
>
> Some parts were taken or inspired by the work of Raven Hallsby available
> here[0]. For that reason I have added his copyright as well (I asked for the
> permission).
>
> 0: https://raw.githubusercontent.com/KarlJoad/guix/9013b5ac3fadb48fad2e7ef1fbfaa4848dcb922a/gnu/packages/power.scm
>
> * gnu/packages/power.scm (apcupsd): New variable.
> * gnu/local.mk (GNU_SYSTEM_MODULES): Register the new file.
>
> Change-Id: I0e4b2f50c8adf0f96d140e2be0f79e3740f4955c
>
> Is that sufficient?

Co-authored-by is not mandatory, but I see it as a nice etiquette to
have, similar to how it's important to preserve the proper commit
authorship information, for due credits.

--
Thanks,
Maxim
Maxim Cournoyer wrote 4 weeks ago
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)
87tt8yl6xj.fsf@gmail.com
Hi Tomas,

Tomas Volf <~@wolfsden.cz> writes:

[...]

Toggle quote (32 lines)
>> I think /var/run is deprecated in favor of just /run, so we should
>> configure the package to use this, I think.
>
> It seems pretty much all programs on my system are using /var/run,
> including Shepherd. Only programs that store information in /run seem
> to be elogind and dbus.
>
> Is deprecation of /var/run listed somewhere in the manual? I am
> surprised Shepherd (since it is actively maintained and core component)
> is still in /var/run. I looked and found 42 references to /var/run, but
> none with the deprecation notice.
>
> Assuming you will insist on moving it to /run, is there some structure
> to follow in that directory? Or just s~/var/run~/run~? What about /var
> and /var/log? Should those be moved somewhere as well?
>
>>
>>> + (string-append "ac_cv_path_SHUTDOWN=/nope")
>>> + (string-append "ac_cv_path_APCUPSD_MAIL=/nope")
>>> + ;; While `wall' is not expanded anywhere, it still is searched for.
>>> + (string-append "ac_cv_path_WALL=/nope")
>>
>> I'm not sure if this package is actively developed, but that last issue
>> should ideally be reported (then cross-referenced here).
>
> I did not found a way to report a bug. Nothing is listed in the manual,
> and their apcupsd-users mailing list requires JavaScript to sign up (and
> does not tell me the email address otherwise).
>
> I did try to blindly send an email, will see if it will be accepted
> without being subscribed. If that happens, I will add the link here.

OK, thank you.

Toggle quote (16 lines)
>>
>>> + ;; Enable additional drivers.
>>> + "--enable-test"
>>
>> Is '--enable-test' useful? What does it do?
>
> According to the manual
>
> This turns on a test driver that is used only for debugging.
>
>
> Since there does not seem to be any harm in having it on, I enabled it,
> since Guix seems to aim for feature complete packages (I assume that is
> the reason for everything being so large). But I have no use for it, so
> I can turn it off if you would prefer.

For this kind of very edge case that is not enabled by default, I'd
leave it off. Our maximalist take on things doesn't including switching
on every non-default option, but if extra dependencies are automatically
picked as part of the configure script, then we would typically add
them, unless they grow the 'guix size' of the package unreasonably (like
adding libreoffice to emacs-org-mode because it has support for it :-)).

It's one of the reason. Other reasons are references kept erroneously,
static libraries, large documentation, wrappers keeping references to
native inputs that shouldn't be captured, etc. There's a lot of work to
do to reduce the size of the distribution :-).

[...]

Toggle quote (20 lines)
>>> + (add-before 'configure 'touch-txt-docs
>>> + (lambda _
>>> + (for-each (lambda (f)
>>> + (call-with-output-file f close-port))
>>> + '("doc/apcupsd.man.txt"
>>> + "doc/apcaccess.man.txt"
>>> + "doc/apctest.man.txt"
>>> + "doc/apccontrol.man.txt"
>>> + "doc/apcupsd.conf.man.txt"))))
>>
>> I think I'd rather depend on these than introduce hacks like below.
>
> The "hacks" *below* would still be required. The HTML manual is not
> built nor installed by default, and there is no target to invoke to
> install them. So both 'build-manual and 'move-doc phases would still be
> required.
>
> We could get rid of the "hack" *above* ('touch-txt-docs), true. It is
> worth those two additional inputs?

Yes, I meant above, sorry. I don't see mandoc and util-linux as large
dependencies enough to justify working around these, especially if they
are only used at build time. And, the would actually be useful when
working on the source of acupsd in a 'guix shell -D acupsd' environment,
so I'd just add them.

[...]

Toggle quote (12 lines)
>>
>>> + (add-after 'install 'remove-etc-apcupsd
>>> + (lambda _
>>> + (delete-file-recursively (string-append #$output "/etc/apcupsd")))))))
>>
>> Break this long line so it fits under 80 columns (our code style
>> guideline).
>
> I thought (well, still do), that going 4 characters over the limit was
> worth the increase in readability. Nevertheless, I have split it into
> two lines.

Thanks. In some cases where it really hurts readability, it may be fine
(it's a guideline after all), but the usual business of simply moving
one parentheses pair down is not one of these :-).

[...]

Toggle quote (3 lines)
> I do not think this is correct. When I check
> apcupsd-3.14.14/src/apcupsd.c file, I see this in its header:

[...]

Toggle quote (4 lines)
> There is no mention of "any later version". Does that not make the
> combined work GPL-2.0-only, even if other files would be under
> GPL-2.0-or-later (I did no check whether they are)?

You are right, and yes the combined work would be GPLv2 only then.

Toggle quote (2 lines)
> I did not find debian/copyright file in the source code.

Toggle quote (5 lines)
>> Could you please send a v2?
>
> Definitely, I will go through your review for the service file as well,
> and once I have all the answers (both here and there), will send v2.

I haven't seen v2 yet, I guess you are still working out the service part?

--
Thanks,
Maxim
Tomas Volf wrote 4 weeks ago
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 75528@debbugs.gnu.org)
87tt8w6sou.fsf@wolfsden.cz
Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (9 lines)
>> I did not found a way to report a bug. Nothing is listed in the manual,
>> and their apcupsd-users mailing list requires JavaScript to sign up (and
>> does not tell me the email address otherwise).
>>
>> I did try to blindly send an email, will see if it will be accepted
>> without being subscribed. If that happens, I will add the link here.
>
> OK, thank you.

I have now added a link to my message to the mailing list.

Toggle quote (30 lines)
>>>
>>>> + ;; Enable additional drivers.
>>>> + "--enable-test"
>>>
>>> Is '--enable-test' useful? What does it do?
>>
>> According to the manual
>>
>> This turns on a test driver that is used only for debugging.
>>
>>
>> Since there does not seem to be any harm in having it on, I enabled it,
>> since Guix seems to aim for feature complete packages (I assume that is
>> the reason for everything being so large). But I have no use for it, so
>> I can turn it off if you would prefer.
>
> For this kind of very edge case that is not enabled by default, I'd
> leave it off. Our maximalist take on things doesn't including switching
> on every non-default option, but if extra dependencies are automatically
> picked as part of the configure script, then we would typically add
> them, unless they grow the 'guix size' of the package unreasonably (like
> adding libreoffice to emacs-org-mode because it has support for it :-)).
>
> It's one of the reason. Other reasons are references kept erroneously,
> static libraries, large documentation, wrappers keeping references to
> native inputs that shouldn't be captured, etc. There's a lot of work to
> do to reduce the size of the distribution :-).
>
> [...]

Makes sense, I took it out.

Toggle quote (29 lines)
>>>> + (add-before 'configure 'touch-txt-docs
>>>> + (lambda _
>>>> + (for-each (lambda (f)
>>>> + (call-with-output-file f close-port))
>>>> + '("doc/apcupsd.man.txt"
>>>> + "doc/apcaccess.man.txt"
>>>> + "doc/apctest.man.txt"
>>>> + "doc/apccontrol.man.txt"
>>>> + "doc/apcupsd.conf.man.txt"))))
>>>
>>> I think I'd rather depend on these than introduce hacks like below.
>>
>> The "hacks" *below* would still be required. The HTML manual is not
>> built nor installed by default, and there is no target to invoke to
>> install them. So both 'build-manual and 'move-doc phases would still be
>> required.
>>
>> We could get rid of the "hack" *above* ('touch-txt-docs), true. It is
>> worth those two additional inputs?
>
> Yes, I meant above, sorry. I don't see mandoc and util-linux as large
> dependencies enough to justify working around these, especially if they
> are only used at build time. And, the would actually be useful when
> working on the source of acupsd in a 'guix shell -D acupsd' environment,
> so I'd just add them.
>
> [...]
>

Makes sense, I will remove the phase and add the dependencies into
native-inputs.

Toggle quote (8 lines)
>>> Could you please send a v2?
>>
>> Definitely, I will go through your review for the service file as well,
>> and once I have all the answers (both here and there), will send v2.
>
> I haven't seen v2 yet, I guess you are still working out the service
> part?

I did not get to the service part yet (life has sadly been somewhat
busy), will get to it during this weekend. But I was also waiting for
your response here, there was no reason to send v2 since I was pretty
sure I will need to do additional changes, so v3 would required anyway.

Tomas

--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
-----BEGIN PGP SIGNATURE-----

iQJCBAEBCgAsFiEEt4NJs4wUfTYpiGikL7/ufbZ/wakFAmevykEOHH5Ad29sZnNk
ZW4uY3oACgkQL7/ufbZ/wal1Og/+KElBecFbmgeWz9UIeEhGj8cjKnNaLzR8UJm4
/nYaAH70G1tVWlh7y91yq3Ps4xrkU/mHYAS5R7JD8wcz4W993ZSKfg1YjUdvqHQZ
6nzv/o7y+obJq58Vcghx4myIOPbF2fRhm4h3qzFYjKU5CFPIwrfisTuKrSlX6Vu/
MMrpa9H+1E3HsllIuvkfU79yodoCGdaYsSM9ss8e33G0lGFD/QvzYBpepDq+Jz9O
BFEENZOGKjrpMyUXG/2ERzLcvIex2NolJxYC8n6Kkq2XuPW4yunH8vfEWkDYGUvg
HEW9PCCsou2f44U6ozVD4ce/xH84scm06RaM8hD8WISQlkasL5FNvEw9AsdgWUnw
YIhJgoBn7oiOLCrcp6XPZWFe6pLdQJOJOOk3eKRh/4ywdyGj4GibKZ0Q18Sg0wNI
K4CGIs+1sGdqt1G/jbMBCRzuDJPq3mrkHWIPwPiACZqkIVXBmlCLv6/MmZrS8vQy
0h1EQAKr+oldvpwthPrADwwU1x1Le1/gDLOPEr/PcKGv/+edM1lQf6BqUFsK1b02
j88zRCteHHpj73QIfeUSc1Swky55JFFIKjn+Z5fJma5+jC8EEAMuxByKtzsnuhGo
HjBhFgod+6MqvTAw3uFJow+mc0bB89PABE+w7Lv7FDCorU54BQFKYHXfGSDc0D/h
9EtyOio=
=GqeM
-----END PGP SIGNATURE-----

Maxim Cournoyer wrote 4 weeks ago
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)
87frkf8fsm.fsf@gmail.com
Hi Tomas,

Thank you for having taken my suggestions into consideration :-).

[...]

Toggle quote (8 lines)
>> I haven't seen v2 yet, I guess you are still working out the service
>> part?
>
> I did not get to the service part yet (life has sadly been somewhat
> busy), will get to it during this weekend. But I was also waiting for
> your response here, there was no reason to send v2 since I was pretty
> sure I will need to do additional changes, so v3 would required anyway.

No worries. Just keep me in CC when you get around ot it, and it's
probably going to be fine to go then.

--
Thanks,
Maxim
Tomas Volf wrote 4 weeks ago
Re: [bug#75528] [PATCH 2/2] services: Add power.
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 75528@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
874j0u7tsy.fsf@wolfsden.cz
Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (9 lines)
> Hi Tomas,
>
> Tomas Volf <~@wolfsden.cz> writes:
>
>> * gnu/services/power.scm: New file.
>
> I see there's already a gnu/services/pm.scm file; it seems we don't need
> another one? Or was it a conscious decision?

Yes, it was intentional, for couple of reasons (in no particular order).

I like the symmetry between gnu/{packages,services}/power.scm. It makes
finding the service-type easy without even opening the manual.

The "pm" is for "power management", and I have never heard anyone call
apcupsd "a power management daemon". It reacts to power outage, true,
but it does not "manage" the power in any way. So it seemed like a poor
fit.

The current pm.scm exports 8 bindings, the new power.scm 54. So it
would feel like taking over the file.

Shorter files are faster to compile (especially when
define-configuration is used), so having two files that can be built in
parallel seemed better.

And like this I can also write the changelog in to the commit message in
a reasonably short form. If I put it into pm.scm, I would need to list
every single binding, in power.scm I mention just a new file.

Toggle quote (9 lines)
>
>> +The @code{(gnu services power)} module provides a service definition for
>> +@uref{http://www.apcupsd.org/, apcupsd}, a utility to interact with APC
>> +UPSes. Apcupsd also works with some OEM-branded products manufactured
>> +by APC.
>
> I'd introduce a few @acronym here to help readers, e.g. for OEM and
> UPS.

I added ones for UPS, OEM and APC.

Toggle quote (11 lines)
>
>> +@defvar apcupsd-service-type
>> +The service type for apcupsd. For USB UPSes no configuration is
>> +necessary, however tweaking some fields to better suit your needs might
>> +be desirable. The defaults are taken from the upstream configuration
>> +and they are not very conservative (@code{battery-level} of 5% is too
>> +low in my opinion).
>
> I'd say, (for example, the default @code{battery-level} of 5% may be
> considered too low by some), to keep it neutral.

Done.

Toggle quote (21 lines)
>
>> +The default event handlers do send emails, read more in
>> +@ref{apcupsd-event-handlers}.
>> +
>> +@lisp
>> +(service apcupsd-service-type)
>> +@end lisp
>> +@end defvar
>> +
>> +@deftp {Data Type} apcupsd-configuration
>> +
>> +Available @code{apcupsd-configuration} fields are:
>> +
>> +@table @asis
>> +@item @code{package} (default: @code{apcupsd}) (type: package)
>> +Package to use.
>
> That's more conventionally named with the same of the package,
> e.g. 'acpupsd', not 'package'. I'd word the description as 'The
> @code{apcupsd} package to use'.

I see, good to know, will try to adhere to that convention from now on.

Toggle quote (7 lines)
>
>> +@item @code{shepherd-base-name} (default: @code{apcupsd}) (type: symbol)
>> +Base name of the shepherd service. You can add the service multiple
>> +times with different prefix to manage multiple UPSes.
>
> s/prefix/prefixes/

I will just take your word on this one. :)

Toggle quote (10 lines)
>> +
>> +@item @code{auto-start?} (default: @code{#t}) (type: boolean)
>> +Should the shepherd service auto-start?
>> +
>> +@item @code{pid-file} (default: @code{"/var/run/apcupsd.pid"}) (type: string)
>> +Path to the pid file.
>
> All file names becoming /run/ prefixed instead of /var/run, based on a
> previous comment.

I will just quote myself from the package's patch, since it seems I did
not get an answer there:

Toggle snippet (14 lines)
It seems pretty much all programs on my system are using /var/run,
including Shepherd. Only programs that store information in /run seem
to be elogind and dbus.

Is deprecation of /var/run listed somewhere in the manual? I am
surprised Shepherd (since it is actively maintained and core component)
is still in /var/run. I looked and found 42 references to /var/run, but
none with the deprecation notice.

Assuming you will insist on moving it to /run, is there some structure
to follow in that directory? Or just s~/var/run~/run~? What about /var
and /var/log? Should those be moved somewhere as well?

Thanks.

Toggle quote (9 lines)
>
> We don't use path in GNU to denote file names, we use 'file names'.
> That's mentioned in info '(standards) GNU Manuals':
>
> Please do not use the term "pathname" that is used in Unix
> documentation; use "file name" (two words) instead. We use the term
> "path" only for search paths, which are lists of directory names.
>

Changed into "File name of the pid file.".

Out of curiosity, does this apply to directories as well? So,
hypothetically, I should instead of "Path to the directory storing XY."
use "File name of the directory storing XY."?

Toggle quote (10 lines)
>> +
>> +@item @code{debug-level} (default: @code{0}) (type: integer)
>> +Logging verbosity. Bigger number means more logs. In the source code I
>> +saw up to @code{300}, so for all logs, @code{999} seems reasonable.
>
> I think it's best to not use first person pronoun in the manual to keep
> it a bit more formal. So instead of 'I saw', this could be reworded to:
> 'The source code uses up to @code{300} as debug level value, so a value
> of @code{999} seems reasonable to enable all logs.' or similar.

Sorry, I have probably gotten too influenced by (gnus) and its personal
style I have enjoyed a lot.

I have used your suggestion verbatim.

Toggle quote (19 lines)
>
>> +
>> +@item @code{run-dir} (default: @code{"/var/run/apcupsd"}) (type: string)
>> +Directory containing runtime information. You need to change this if
>> +you desire to run multiple instances of the daemon.
>> +
>> +@item @code{name} (type: maybe-string)
>> +Use this to give your UPS a name in log files and such. This is
>> +particularly useful if you have multiple UPSes. This does not set the
>> +EEPROM. It should be 8 characters or less.
>> +
>> +@item @code{cable} (default: @code{usb}) (type: enum-cable)
>> +The type of cable connecting the UPS to your computer. Possible generic
>> +choices are @code{'simple}, @code{'smart}, @code{'ether} and
>> +@code{'usb}. Or a specific cable model number may be used:
>
> nitpick: I'd reword to 'Alternatively, a specific [...]', which reads
> better to me.

Done.

Toggle quote (7 lines)
>
>> +@item usb
>> +A null string setting enables autodetection, which is the best choice
>> +for most installations.
>
> nitpick: My dictionary (aspell) prefers auto-detection.

Huh, so does hunspell. Shame on me, I need to remember to enable
flyspell-prog-mode more often.

Toggle quote (12 lines)
>
> [...]
>
>> +@anchor{apcupsd-event-handlers}
>> +@deftp {Data Type} apcupsd-event-handlers
>> +
>> +For description of the events please refer to the @command{apcupsd}'s
>> +manual, which can be found in the @samp{apcupsd-doc} package.
>
> I think you also meant it is in the @samp{doc} output, not in a
> different package, right?

Right.

Toggle quote (7 lines)
> In any case, I'd refer them to the man page of apccontrol, which
> details the events. Also, to avoid the odd rendering as "the
> ‘apcupsd’’s manual" in info, I'd instead rephrase to:
>
> For a description of the events please refer to @samp{man 8
> apccontrol}.

I like this version, thank you.

Toggle quote (8 lines)
>
>> +
>> +Each handler shall be a gexp. It is spliced into the control script for
>> +the daemon. In addition to the standard Guile programming environment,
>> +these procedures and variables are also available.
>
> s/these/the following/ [...] s/available./available:/

Done.

Toggle quote (5 lines)
> Since these are spliced in the control script and thus not at the top
> level, users won't be able to import Guile modules they may want to use,
> so it seems there should be a field to define which Guile modules should
> be imported and used by default in the configuration.

Hm, that is a good point. I did not think of that. I have added a
`modules' field to the apcupsd-event-handlers record.

Toggle quote (8 lines)
>
>> +
>> +@table @code
>> +@item conf
>> +Variable containing path to the configuration file.
>
> s/path/file name/, anywhere this appears.

Done. I went back to the package and replaced it there as well. It is
bit unfortunate that autotools call the variables `ac_cv_path_', instead
of `ac_cv_file_name_', but nothing I can do about that.

Toggle quote (7 lines)
>
>> +
>> +@item powerfail-file
>> +Variable containing path to the powerfail file.
>
> Ditto.

And done.

Toggle quote (12 lines)
>
>> +@item cmd
>> +The event currently being handled.
>> +
>> +@item name
>> +The name of the UPS as specified in the configuration file.
>> +
>> +@item connected
>> +Is @code{"1"} if apcupsd is connected to the UPS via a serial port (or a
>
> @command{apcupsd}

Thanks.
Toggle quote (3 lines)
>
> Also, any reason why these integer values are expressed as strings?

No. I have just adhered to the original calling convention of the shell
script, and argv is all char*. These are even not really an integer
values, but bools. So I changed this one to `connected?', and the other
one to `powered?', with #t/#f values.

Toggle quote (7 lines)
>
>> +USB port). In most configurations, this will be the case. In the case
>> +of a Slave machine where apcupsd is not directly connected to the UPS,
>
> s/Slave/slave/ ? Could be nicer to use a neutral alternative like
> 'follower machine'.

The "Slave machine" (including the capitalization) is a term used in the
manual. I can lower-case it if you would prefer, seems that manual
actually uses both casings now when I looked for it.

I do not think inventing new terminology is a good idea, since users
will not be able to find it in the manual distributed with the program.

Toggle quote (98 lines)
>
>> +this value will be @code{"0"}.
>> +
>> +@item powered
>> +Is @code{"1"} if the computer on which @command{apcupsd} is running is
>> +powered by the UPS and @code{"0"} if not. At the moment, this value is
>> +unimplemented and always @code{"0"}.
>> +
>> +@item (err @var{fmt} @var{args...})
>> +Wrapper around @code{format} outputting to @code{(current-error-port)}.
>> +
>> +@item (wall @var{fmt} @var{args...})
>> +Wrapper around @code{format} outputting via @command{wall}.
>> +
>> +@item (apcupsd @var{args...})
>> +Call @command{apcupsd} while passing the correct configuration file and
>> +all the arguments.
>> +
>> +@item (mail-to-root @var{subject} @var{body})
>> +Send an email to the local administrator. This procedure assumes the
>> +@command{sendmail} is located at @command{/run/privileged/bin/sendmail}
>> +(as would be the case with @code{opensmtpd-service-type}).
>> +
>> +@end table
>> +
>> +Available @code{apcupsd-event-handlers} fields are:
>> +
>> +@table @asis
>> +@item @code{killpower} (type: gexp)
>> +Handler for killpower event.
>> +
>> +@item @code{commfailure} (type: gexp)
>> +Handler for commfailure event.
>> +
>> +@item @code{commok} (type: gexp)
>> +Handler for commfailure event.
>> +
>> +@item @code{powerout} (type: gexp)
>> +Handler for powerout event.
>> +
>> +@item @code{onbattery} (type: gexp)
>> +Handler for onbattery event.
>> +
>> +@item @code{offbattery} (type: gexp)
>> +Handler for offbattery event.
>> +
>> +@item @code{mainsback} (type: gexp)
>> +Handler for mainsback event.
>> +
>> +@item @code{failing} (type: gexp)
>> +Handler for failing event.
>> +
>> +@item @code{timeout} (type: gexp)
>> +Handler for timeout event.
>> +
>> +@item @code{loadlimit} (type: gexp)
>> +Handler for loadlimit event.
>> +
>> +@item @code{runlimit} (type: gexp)
>> +Handler for runlimit event.
>> +
>> +@item @code{doreboot} (type: gexp)
>> +Handler for doreboot event.
>> +
>> +@item @code{doshutdown} (type: gexp)
>> +Handler for doshutdown event.
>> +
>> +@item @code{annoyme} (type: gexp)
>> +Handler for annoyme event.
>> +
>> +@item @code{emergency} (type: gexp)
>> +Handler for emergency event.
>> +
>> +@item @code{changeme} (type: gexp)
>> +Handler for changeme event.
>> +
>> +@item @code{remotedown} (type: gexp)
>> +Handler for remotedown event.
>> +
>> +@item @code{startselftest} (type: gexp)
>> +Handler for startselftest event.
>> +
>> +@item @code{endselftest} (type: gexp)
>> +Handler for endselftest event.
>> +
>> +@item @code{battdetach} (type: gexp)
>> +Handler for battdetach event.
>> +
>> +@item @code{battattach} (type: gexp)
>> +Handler for battattach event.
>> +
>> +@end table
>> +
>> +@end deftp
>
> nitpick: I'd remove the newline between the two @end above (I know, it
> gets generated this way...).

Done.

Toggle quote (6 lines)
>
>> +;;; Copyright (C) 2025 Tomas Volf <~@wolfsden.cz>
>
> We use the unicode symbol (©) for copyright throughout Guix; please use
> it too, for uniformity.

So © is fine, but λ is not. I fail to see the consistency.

(But of course adjusted, it is just that I do not get the difference.)

Toggle quote (25 lines)
>
>> +
>> +;;;; Commentary:
>> +
>> +;;; Power-related services.
>> +
>> +;;;; Code:
>> +
>> +(define-module (gnu services power)
>> + #:use-module (srfi srfi-1)
>> + #:use-module (srfi srfi-26)
>> + #:use-module (ice-9 match)
>> + #:use-module (gnu)
>> + #:use-module (gnu packages admin)
>> + #:use-module (gnu packages linux)
>> + #:use-module (gnu packages power)
>> + #:use-module (gnu services)
>> + #:use-module (gnu services configuration)
>> + #:use-module (gnu services shepherd)
>> + #:use-module (guix packages)
>> + #:use-module (guix records)
>
> Please order lexicographically (that is, alphabetically but on things
> which are not necessarily words :-)).

Interesting. I personally order imports lexicographically, but within
groups of:

1. stdlib (srfi, rnrs, ...)
2. guile extensions (ice-9, ...)
3. 3rd-party dependencies (guile-lib, guile-json, ...)
4. modules from the program itself (gnu, guix, ...)

Given your requirement for sorting everything lexicographically (with no
grouping), the following:

Toggle snippet (8 lines)
(use-modules (gnu services)
(srfi srfi-1))

(when #f
(modify-services '()
(delete 'x)))

Prints a warning when compiled:

Toggle snippet (3 lines)
WARNING: (guile-user): imported module (gnu services) overrides core binding `delete'

However, when done "my" way (putting srfi-1 first, since "stdlib"), the
warning goes away.

This is not really issue here, since I am not using modify-services, the
warning is not printed (I have no idea why). But since in general order
of imports seem to matter, I wonder whether mandating global order is a
good idea.

What are your thoughts here?

Toggle quote (78 lines)
>
>> + #:export (apcupsd-service-type
>> +
>> + apcupsd-configuration
>> + apcupsd-configuration-package
>> + apcupsd-configuration-shepherd-base-name
>> + apcupsd-configuration-auto-start?
>> + apcupsd-configuration-pid-file
>> + apcupsd-configuration-debug-level
>> + apcupsd-configuration-run-dir
>> + apcupsd-configuration-name
>> + apcupsd-configuration-cable
>> + apcupsd-configuration-type
>> + apcupsd-configuration-device
>> + apcupsd-configuration-poll-time
>> + apcupsd-configuration-on-batery-delay
>> + apcupsd-configuration-battery-level
>> + apcupsd-configuration-remaining-minutes
>> + apcupsd-configuration-timeout
>> + apcupsd-configuration-annoy-interval
>> + apcupsd-configuration-annoy-delay
>> + apcupsd-configuration-no-logon
>> + apcupsd-configuration-kill-delay
>> + apcupsd-configuration-net-server
>> + apcupsd-configuration-net-server-ip
>> + apcupsd-configuration-net-server-port
>> + apcupsd-configuration-net-server-events-file
>> + apcupsd-configuration-net-server-events-file-max-size
>> + apcupsd-configuration-class
>> + apcupsd-configuration-mode
>> + apcupsd-configuration-stat-time
>> + apcupsd-configuration-log-stats
>> + apcupsd-configuration-data-time
>> + apcupsd-configuration-facility
>> + apcupsd-configuration-event-handlers
>> +
>> + apcupsd-event-handlers-annoyme
>> + apcupsd-event-handlers-battattach
>> + apcupsd-event-handlers-battdetach
>> + apcupsd-event-handlers-changeme
>> + apcupsd-event-handlers-commfailure
>> + apcupsd-event-handlers-commok
>> + apcupsd-event-handlers-doreboot
>> + apcupsd-event-handlers-doshutdown
>> + apcupsd-event-handlers-emergency
>> + apcupsd-event-handlers-endselftest
>> + apcupsd-event-handlers-failing
>> + apcupsd-event-handlers-killpower
>> + apcupsd-event-handlers-loadlimit
>> + apcupsd-event-handlers-mainsback
>> + apcupsd-event-handlers-offbattery
>> + apcupsd-event-handlers-onbattery
>> + apcupsd-event-handlers-powerout
>> + apcupsd-event-handlers-remotedown
>> + apcupsd-event-handlers-runlimit
>> + apcupsd-event-handlers-startselftest
>> + apcupsd-event-handlers-timeout))
>> +
>> +(define-configuration/no-serialization apcupsd-event-handlers
>> + (killpower
>> + (gexp
>> + #~((wall "Apccontrol doing: apcupsd --killpower on UPS ~a" name)
>> + (sleep 10)
>> + (apcupsd "--killpower")
>> + (wall "Apccontrol has done: apcupsd --killpower on UPS ~a" name)))
>> + "Handler for killpower event.")
>
> In many places in your writing, I find that it skips adding 'the'
> (definite article), which I've learned should be use when describing
> something specific, such as here with the specific handlers:
>
> "Handler for the @code{killpower} event."
>
> In case it helps, I've found this page seems useful in briefly
> explaining when to use 'the' [0].
>
> [0] https://owl.purdue.edu/owl/general_writing/grammar/using_articles.html

Yeah I always have a problem with these. My mother tongue does not have
this concept at all, so it is bit hard for me. Thank you for the link,
it was useful. I went through the texts and tried to add them where I
felt they were missing, you are right, there were a lot of them missing.

Toggle quote (53 lines)
>
>> + (commfailure
>> + (gexp
>> + #~((let ((msg (format #f "~a Communications with UPS ~a lost."
>> + (gethostname) name)))
>> + (mail-to-root msg msg))
>> + (wall "Warning: communications lost with UPS ~a" name)))
>> + "Handler for commfailure event.")
>> + (commok
>> + (gexp
>> + #~((let ((msg (format #f "~a Communications with UPS ~a restored."
>> + (gethostname) name)))
>> + (mail-to-root msg msg))
>> + (wall "Communications restored with UPS ~a" name)))
>> + "Handler for commfailure event.")
>> + (powerout
>> + (gexp
>> + #~(#t))
>> + "Handler for powerout event.")
>> + (onbattery
>> + (gexp
>> + #~((let ((msg (format #f "~a UPS ~a Power Failure !!!"
>> + (gethostname) name)))
>> + (mail-to-root msg msg))
>> + (wall "Power failure on UPS ~a. Running on batteries." name)))
>> + "Handler for onbattery event.")
>> + (offbattery
>> + (gexp
>> + #~((let ((msg (format #f "~a UPS ~a Power has returned."
>> + (gethostname) name)))
>> + (mail-to-root msg msg))
>> + (wall "Power has returned on UPS ~a..." name)))
>> + "Handler for offbattery event.")
>> + (mainsback
>> + (gexp
>> + #~((when (file-exists? powerfail-file)
>> + (wall "Continuing with shutdown."))))
>> + "Handler for mainsback event.")
>> + (failing
>> + (gexp
>> + #~((wall "Battery power exhausted on UPS ~a. Doing shutdown." name)))
>> + "Handler for failing event.")
>> + (timeout
>> + (gexp
>> + #~((wall "Battery time limit exceeded on UPS ~a. Doing shutdown." name)))
>> + "Handler for timeout event.")
>> + (loadlimit
>> + (gexp
>> + #~((wall "Remaining battery charge below limit on UPS ~a. Doing shutdown." name)))
>> + "Handler for loadlimit event.")
>> + (runlimit
>> + (gexp
>> + #~((wall "Rema
This message was truncated. Download the full message here.
-----BEGIN PGP SIGNATURE-----

iQJCBAEBCgAsFiEEt4NJs4wUfTYpiGikL7/ufbZ/wakFAmexDl0OHH5Ad29sZnNk
ZW4uY3oACgkQL7/ufbZ/wam6eg//ZsdLZ9t5dVF3UsGAkiXLAxEC9mX4NJeUA5Qk
eRvqxLPdsJmm0IJCNBmwo7fmwFyjseHO0yCGgW6S4LtwJ0zFMNDjKaOGp1a99Qqz
h6XTlh5EzXUKP9Z3NGyDpLVjVhSMY3cTkKLdnmn7wk2hnh9wehAdjeevy133yvS3
GhuRAydKE9V6uqiEqAq+CL8K30ARZL7RZQf1SZpTZTil+6DMOliw25grPMR9D48a
Vn3XrVrpE5P+nTf9RXD2+3lPfMMaEyrsVtwpWyEYBiL4/QadK5ZglNsm0ljsEh2Q
/ubmZ4n57cS8QXOiETq+RaUg0uAL3nlmoRnryu772bst19y7G7swOVYD44JFxyyi
fBriRudZTlNE22c4haFeCyXsnxi1UB4uz8MMYNDPvh39qdvL3xA+QbHKAPNhOCsB
ZfJuNRXw4XndphDXX7nAC1Qv1zs5oZrpUFuUTgQwXqsbQ9eoJJqNUdafkbdxlW9x
HDcJVi5L4Y8N/ZYhacHFDr+GBw4ql7jez0mDGUZux3OScpMPS3DDAdNkk4Pqo79a
MQPOQPV+cxqfLyY3dsuqofP+VKdzVIX+KAerJvTivjFjwbbw9IbG8kaNZuv5PA1P
jMs8dgRDdMDQ8thO+h0ktLnPNAx4tQFCvxxywpgg0VgQdxhZb5Dp1jzq21QmrLzM
wboDN48=
=7Re+
-----END PGP SIGNATURE-----

Maxim Cournoyer wrote 4 weeks ago
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
878qq678tq.fsf@gmail.com
Hi Tomas,

Tomas Volf <~@wolfsden.cz> writes:

[...]

Toggle quote (16 lines)
>> I see there's already a gnu/services/pm.scm file; it seems we don't need
>> another one? Or was it a conscious decision?
>
> Yes, it was intentional, for couple of reasons (in no particular order).
>
> I like the symmetry between gnu/{packages,services}/power.scm. It makes
> finding the service-type easy without even opening the manual.
>
> The "pm" is for "power management", and I have never heard anyone call
> apcupsd "a power management daemon". It reacts to power outage, true,
> but it does not "manage" the power in any way. So it seemed like a poor
> fit.
>
> The current pm.scm exports 8 bindings, the new power.scm 54. So it
> would feel like taking over the file.

OK, that's reasonable, let's keep it (gnu services power).

Toggle quote (8 lines)
> Shorter files are faster to compile (especially when
> define-configuration is used), so having two files that can be built in
> parallel seemed better.
>
> And like this I can also write the changelog in to the commit message in
> a reasonably short form. If I put it into pm.scm, I would need to list
> every single binding, in power.scm I mention just a new file.

Haha, yes, the last point is a funny 'loophole' of the change log
format. Change a couple things to a package? Needs a lot of
description. Add a new package? Two words suffice :-). It's
reasonable though (but still funny).

[...]

Toggle quote (26 lines)
>>> +
>>> +@item @code{auto-start?} (default: @code{#t}) (type: boolean)
>>> +Should the shepherd service auto-start?
>>> +
>>> +@item @code{pid-file} (default: @code{"/var/run/apcupsd.pid"}) (type: string)
>>> +Path to the pid file.
>>
>> All file names becoming /run/ prefixed instead of /var/run, based on a
>> previous comment.
>
> I will just quote myself from the package's patch, since it seems I did
> not get an answer there:
>
> It seems pretty much all programs on my system are using /var/run,
> including Shepherd. Only programs that store information in /run seem
> to be elogind and dbus.
>
> Is deprecation of /var/run listed somewhere in the manual? I am
> surprised Shepherd (since it is actively maintained and core component)
> is still in /var/run. I looked and found 42 references to /var/run, but
> none with the deprecation notice.
>
> Assuming you will insist on moving it to /run, is there some structure
> to follow in that directory? Or just s~/var/run~/run~? What about /var
> and /var/log? Should those be moved somewhere as well?

Deprecating /var/run to /run is not a Guix thing, but a File Hiearchy
Standard (FHS) thing [0]. Since we already use /run, there's no good
reason to have /var/run something else than a symlink to /run, and
there's an actual change pending merge that would do that, along making
/run mounted as a tmpfs (bug#73494).

Toggle quote (14 lines)
>
> Thanks.
>
>>
>> We don't use path in GNU to denote file names, we use 'file names'.
>> That's mentioned in info '(standards) GNU Manuals':
>>
>> Please do not use the term "pathname" that is used in Unix
>> documentation; use "file name" (two words) instead. We use the term
>> "path" only for search paths, which are lists of directory names.
>>
>
> Changed into "File name of the pid file.".

nitpick: s/pid/PID/

Toggle quote (4 lines)
> Out of curiosity, does this apply to directories as well? So,
> hypothetically, I should instead of "Path to the directory storing XY."
> use "File name of the directory storing XY."?

Since a directory is implicitly a file, you can just say 'Directory
storing XY.'

Toggle quote (15 lines)
>>> +
>>> +@item @code{debug-level} (default: @code{0}) (type: integer)
>>> +Logging verbosity. Bigger number means more logs. In the source code I
>>> +saw up to @code{300}, so for all logs, @code{999} seems reasonable.
>>
>> I think it's best to not use first person pronoun in the manual to keep
>> it a bit more formal. So instead of 'I saw', this could be reworded to:
>> 'The source code uses up to @code{300} as debug level value, so a value
>> of @code{999} seems reasonable to enable all logs.' or similar.
>
> Sorry, I have probably gotten too influenced by (gnus) and its personal
> style I have enjoyed a lot.
>
> I have used your suggestion verbatim.

Opinions seem to vary on which style is best, but in Guix I'm pretty
sure we've stuck to what I've described for the most part until now, so
we should continue, for consistency.

[...]

Toggle quote (4 lines)
> Done. I went back to the package and replaced it there as well. It is
> bit unfortunate that autotools call the variables `ac_cv_path_', instead
> of `ac_cv_file_name_', but nothing I can do about that.

That's funny; the 'standards' Texinfo manual comes from autoconf, and is
the one documenting one. But autoconf is old, perhaps that guide line was
clarified later.

[...]

Toggle quote (21 lines)
>> Also, any reason why these integer values are expressed as strings?
>
> No. I have just adhered to the original calling convention of the shell
> script, and argv is all char*. These are even not really an integer
> values, but bools. So I changed this one to `connected?', and the other
> one to `powered?', with #t/#f values.
>
>>
>>> +USB port). In most configurations, this will be the case. In the case
>>> +of a Slave machine where apcupsd is not directly connected to the UPS,
>>
>> s/Slave/slave/ ? Could be nicer to use a neutral alternative like
>> 'follower machine'.
>
> The "Slave machine" (including the capitalization) is a term used in the
> manual. I can lower-case it if you would prefer, seems that manual
> actually uses both casings now when I looked for it.
>
> I do not think inventing new terminology is a good idea, since users
> will not be able to find it in the manual distributed with the program.

The 'slave' word always carry a negative connotation. I think it's a
good idea to be proactive on modernizing the terminology where we can.

[...]

Toggle quote (5 lines)
>> We use the unicode symbol (©) for copyright throughout Guix; please use
>> it too, for uniformity.
>
> So © is fine, but λ is not. I fail to see the consistency.

I guess that could be explained somewhere along those lines:

- λ is used in code; newcomers could be confused as to what it means, or
have to copy-paste it around thinking it's required by the syntax to
define anonymous procedures (while it's not -- they can just type
'lambda').

- © is just a widely understood symbol used in a text comment.

Toggle quote (32 lines)
>>> +
>>> +;;;; Commentary:
>>> +
>>> +;;; Power-related services.
>>> +
>>> +;;;; Code:
>>> +
>>> +(define-module (gnu services power)
>>> + #:use-module (srfi srfi-1)
>>> + #:use-module (srfi srfi-26)
>>> + #:use-module (ice-9 match)
>>> + #:use-module (gnu)
>>> + #:use-module (gnu packages admin)
>>> + #:use-module (gnu packages linux)
>>> + #:use-module (gnu packages power)
>>> + #:use-module (gnu services)
>>> + #:use-module (gnu services configuration)
>>> + #:use-module (gnu services shepherd)
>>> + #:use-module (guix packages)
>>> + #:use-module (guix records)
>>
>> Please order lexicographically (that is, alphabetically but on things
>> which are not necessarily words :-)).
>
> Interesting. I personally order imports lexicographically, but within
> groups of:
>
> 1. stdlib (srfi, rnrs, ...)
> 2. guile extensions (ice-9, ...)
> 3. 3rd-party dependencies (guile-lib, guile-json, ...)
> 4. modules from the program itself (gnu, guix, ...)

That's similar to what I'd do in C or C++, except I'd reverse it so that
I can catch missing includes from my own files.

In Guix I've never read we do that. Perhaps it'd make sense. But
currently we typically globally sort lexicographically, I think (but
many modules imports are in a messy state).

Toggle quote (18 lines)
> Given your requirement for sorting everything lexicographically (with no
> grouping), the following:
>
> (use-modules (gnu services)
> (srfi srfi-1))
>
> (when #f
> (modify-services '()
> (delete 'x)))
>
>
> Prints a warning when compiled:
>
> WARNING: (guile-user): imported module (gnu services) overrides core binding `delete'
>
> However, when done "my" way (putting srfi-1 first, since "stdlib"), the
> warning goes away.

That's an interesting workaround, but I think we should look into that
problem and fix it definitely I often used #:hidden (delete) without any
issue it seems, so I'm not even sure if its existence still solves a
true problem in current Guile.

[...]

Toggle quote (10 lines)
>>> +(define-configuration/no-serialization apcupsd-event-handlers
>>> + (killpower
>>> + (gexp
>>> + #~((wall "Apccontrol doing: apcupsd --killpower on UPS ~a" name)
>>> + (sleep 10)
>>> + (apcupsd "--killpower")
>>> + (wall "Apccontrol has done: apcupsd --killpower on UPS ~a" name)))
>>> + "Handler for killpower event.")
>>

By the way, why do we need to sleep 10 seconds here? It seems unnecessary.

[...]

Toggle quote (28 lines)
>> Sounds fun :-). I was wondering if the handlers could be of the
>> maybe-gexp type, and when not provided the handler would not be called
>> at all? Or does the design of apcupsd mandates that all handlers be
>> defined?
>
> The control script is called for all events. So I *could* use
> maybe-gexp, and change
>
> ("battattach"
> #$@(apcupsd-event-handlers-battattach
> (apcupsd-configuration-event-handlers cfg)))
>
>
> into
>
> ("battattach"
> #$@(let ((h (apcupsd-event-handlers-battattach
> (apcupsd-configuration-event-handlers cfg))))
> (if (maybe-value-set? h)
> h
> #~(#t))))
>
>
> and do that for all the events. But I have to admit I do not consider
> that an improvement. There probably is a better way to express that,
> but the current approach (of always having, possibly NOP, handler
> defined) helps keep the code straight forward.

OK; we could probably script the control script differently, but let's
keep what we have so far, it's simple and I doubt performance is an
issue ;-).

Toggle quote (21 lines)
>>> +(define-syntax define-enum
>>> + (lambda (x)
>>> + (syntax-case x ()
>>> + ((_ name values)
>>> + (let* ((d/n (syntax->datum #'name))
>>> + (d/predicate (string->symbol
>>> + (format #f "enum-~a?" d/n)))
>>> + (d/serialize (string->symbol
>>> + (format #f "serialize-enum-~a" d/n))))
>>
>> Even for internal bindings, better variable names would help reading the
>> code.
>>
>
> When I am writing syntax-case, I always struggle with the naming, since
> I effectively have to have two variables for a single thing. One for
> datum, and one for syntax.
>
> I have expanded the d/ info full datum/, however if can suggest better
> naming scheme for situations like this, I am all ears.

I'd stick to describe what they really are, e.g. for the enum-$name?
symbol, 'predicate-name' or 'predicate-variable-name' instead of
d/predicate. When deeply nested, it's OK to abbreviate the names a bit
if it becomes difficult to fit in our 80 columns width, so it could be
'pred-var-name' or the likes.

[...]

Toggle quote (16 lines)
>>> + (shepherd-base-name
>>> + (symbol 'apcupsd)
>>> + "Base name of the shepherd service. You can add the service multiple times
>>> +with different prefix to manage multiple UPSes."
>>> + empty-serializer)
>>> + (auto-start?
>>> + (boolean #t)
>>> + "Should the shepherd service auto-start?"
>>> + empty-serializer)
>>> + (pid-file
>>> + (string "/var/run/apcupsd.pid")
>>
>> /var/run -> /run (adjust everywhere)
>
> See the reaction above regarding the /var/run.

Which I've now covered in my reply above (sorry for missing in in my
first replies).

[...]

Toggle quote (16 lines)
> The s/ was supposed to stand for script/, I originally expected I will
> have more than one. I have renamed it to %apccontrol.
>
>>
>>> + (program-file
>>> + "apccontrol"
>>> + #~(begin
>>> + (use-modules (srfi srfi-9)
>>> + (ice-9 format)
>>> + (ice-9 match)
>>> + (ice-9 popen))
>>
>> Please sort modules lexicographically.
>
> See the reaction above.

I've replied above. I'd suggest to stick to lexicographically sort the
whole imports block to stick with current conventions (although I don't
think this particular one is documented).

Toggle quote (91 lines)
>>> + ;; Script dir depends on these, and the configuration depends on the script
>>> + ;; dir. To sever the cyclic dependency, pass the paths via environment
>>> + ;; variables.
>>> + (define conf (getenv "GUIX_APCUPSD_CONF"))
>>> + (define powerfail-file (getenv "GUIX_APCUPSD_POWERFAIL_FILE"))
>>> +
>>> + (define (err . args)
>>> + (apply format (current-error-port) args))
>>> + (define (wall . args)
>>> + (system* #$(file-append util-linux "/bin/wall") (apply format #f args)))
>>> + (define (apcupsd . args)
>>> + (apply system* #$(file-append apcupsd "/sbin/apcupsd") "-f" conf args))
>>> + (define (mail-to-root subject body)
>>> + (let ((port (open-pipe* OPEN_WRITE
>>> + "/run/privileged/bin/sendmail"
>>> + "-F" "apcupsd"
>>> + "root")))
>>> + (format port "Subject: ~a~%~%~a~&" subject body)
>>> + (close-pipe port)))
>>> + (match (cdr (command-line))
>>> + (((? string? cmd) name connected powered)
>>> + (match cmd
>>> + ;; I am sure this could be done by macro, but meh. Last release of
>>> + ;; apcupsd was in 2016, so maintaining this will not be much work.
>>> + ("killpower"
>>> + #$@(apcupsd-event-handlers-killpower
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("commfailure"
>>> + #$@(apcupsd-event-handlers-commfailure
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("commok"
>>> + #$@(apcupsd-event-handlers-commok
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("powerout"
>>> + #$@(apcupsd-event-handlers-powerout
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("onbattery"
>>> + #$@(apcupsd-event-handlers-onbattery
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("offbattery"
>>> + #$@(apcupsd-event-handlers-offbattery
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("mainsback"
>>> + #$@(apcupsd-event-handlers-mainsback
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("failing"
>>> + #$@(apcupsd-event-handlers-failing
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("timeout"
>>> + #$@(apcupsd-event-handlers-timeout
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("loadlimit"
>>> + #$@(apcupsd-event-handlers-loadlimit
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("runlimit"
>>> + #$@(apcupsd-event-handlers-runlimit
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("doreboot"
>>> + #$@(apcupsd-event-handlers-doreboot
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("doshutdown"
>>> + #$@(apcupsd-event-handlers-doshutdown
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("annoyme"
>>> + #$@(apcupsd-event-handlers-annoyme
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("emergency"
>>> + #$@(apcupsd-event-handlers-emergency
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("changeme"
>>> + #$@(apcupsd-event-handlers-changeme
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("remotedown"
>>> + #$@(apcupsd-event-handlers-remotedown
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("startselftest"
>>> + #$@(apcupsd-event-handlers-startselftest
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("endselftest"
>>> + #$@(apcupsd-event-handlers-endselftest
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("battdetach"
>>> + #$@(apcupsd-event-handlers-battdetach
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + ("battattach"
>>> + #$@(apcupsd-event-handlers-battattach
>>> + (apcupsd-configuration-event-handlers cfg)))
>>> + (_
>>> + (err "Unknown event: ~a~%" cmd)
>>> + (err "Iff the event was passed by apcupsd, this is a bug.~%")

I just thought about it, perhaps it could be more concise to extract the
field values using match-record (these are bound to shorter names, since
they omit the 'apcupsd-configuration-' prefix).

[...]


Toggle quote (5 lines)
>> I don't understand, in which scenario would an event *not* be emitted
>> by apcupsd?
>
> If you call the script manually from command line. ^_^

I see!

[...]

Toggle quote (20 lines)
>>> + (let ((run-dir (apcupsd-configuration-run-dir cfg)))
>>> + (mixed-text-file
>>> + "apcupsd.conf"
>>> + "\
>>> +## apcupsd.conf v1.1 ##
>>> +#
>>> +# for apcupsd release 3.14.14 (31 May 2016) - GNU Guix
>>
>> Is this config really bound to a version like above? Unless they change
>> things in backward incompatible ways, it should work for newer ones too,
>> no?
>
> This file header is taken from the official configuration file
> distributed with the apcupsd (except the GNU Guix part). So while I do
> expect that this configuration file should work with newer version, the
> upstream decided to put the version into the configuration file.
>
> I have no strong opinion either way here, should I just remove the file
> header completely?

I think so, since we're going the path of producing our own, we don't
need to keep things that are not deemed useful.

Toggle quote (8 lines)
>>> +#
>>> +# \"apcupsd\" POSIX config file (crafted via apcupsd-service-type)
>>
>> s/crafted via/auto-generated by/
>
> But, but, "crafted" sound way more fancy. :) Well, replaced anyway.
> However I have used just "generated by", the "auto-" part felt bit off.

Eheh. Good!

Toggle quote (47 lines)
>>> +"
>>> + (serialize-configuration cfg apcupsd-configuration-fields)
>>> + ;; This one is confusing. The manual page states:
>>> + ;;
>>> + ;; > It must be changed when running more than one copy of apcupsd on the
>>> + ;; > same computer to control multiple UPSes.
>>> + ;;
>>> + ;; However would you not want the lock to be per-device, not per-process?
>>> + ;; I decided to follow the documentation, but I do not understand why it
>>> + ;; should be like this. I do not have multiple UPSes to try.
>>> + (serialize-string 'lock-dir (string-append run-dir "/lock"))
>>> + (serialize-string 'power-fail-dir run-dir)
>>> + (serialize-string 'no-login-dir run-dir)
>>> + (serialize-string 'stat-file (string-append run-dir "/apcupsd.status"))
>>> + "SCRIPTDIR " (apcupsd-script-dir cfg) "\n")))
>>> +
>>> +(define (apcupsd-shepherd-services cfg)
>>
>> s/cfg/config/
>
> And done.
>
>>
>>> + (match-record cfg <apcupsd-configuration>
>>> + ( package pid-file debug-level run-dir
>> ^ extraneous space
>
> This is intentional, it is neat little trick I have learned recently.
> Without the extra leading space, Emacs indents it in this way:
>
> (match-record config <apcupsd-configuration>
> (apcupsd pid-file debug-level run-dir
> shepherd-base-name auto-start?)
>
>
> However, with the extra leading space, the indentation changes to:
>
> (match-record config <apcupsd-configuration>
> ( apcupsd pid-file debug-level run-dir
> shepherd-base-name auto-start?)
>
>
> Since in this case it is not a procedure call, but a list, first member
> (apcupsd) is not special in any way. So it feels weird for it to have a
> special priority regarding the indentation. With the extra leading
> space, all list members are equal, as they should be.

I see; that's an issue with our .dir-locals.el indentation rules. It
should be fixed there, not worked around in actual code, though I can
that workaround documented
This message was truncated. Download the full message here.
Tomas Volf wrote 4 weeks ago
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 75528@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
875xl9538y.fsf@wolfsden.cz
Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (34 lines)
>>>> +
>>>> +@item @code{auto-start?} (default: @code{#t}) (type: boolean)
>>>> +Should the shepherd service auto-start?
>>>> +
>>>> +@item @code{pid-file} (default: @code{"/var/run/apcupsd.pid"}) (type: string)
>>>> +Path to the pid file.
>>>
>>> All file names becoming /run/ prefixed instead of /var/run, based on a
>>> previous comment.
>>
>> I will just quote myself from the package's patch, since it seems I did
>> not get an answer there:
>>
>> It seems pretty much all programs on my system are using /var/run,
>> including Shepherd. Only programs that store information in /run seem
>> to be elogind and dbus.
>>
>> Is deprecation of /var/run listed somewhere in the manual? I am
>> surprised Shepherd (since it is actively maintained and core component)
>> is still in /var/run. I looked and found 42 references to /var/run, but
>> none with the deprecation notice.
>>
>> Assuming you will insist on moving it to /run, is there some structure
>> to follow in that directory? Or just s~/var/run~/run~? What about /var
>> and /var/log? Should those be moved somewhere as well?
>
> Deprecating /var/run to /run is not a Guix thing, but a File Hiearchy
> Standard (FHS) thing [0]. Since we already use /run, there's no good
> reason to have /var/run something else than a symlink to /run, and
> there's an actual change pending merge that would do that, along making
> /run mounted as a tmpfs (bug#73494).
>
> [0] https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s13.html

I did not know we actually care about FHS (for example our /usr/bin
definitely does not look like it should in that case :) ).

I did the s|/var/run|/run|.

Toggle quote (17 lines)
>
> [..]
>
>>>
>>> s/Slave/slave/ ? Could be nicer to use a neutral alternative like
>>> 'follower machine'.
>>
>> The "Slave machine" (including the capitalization) is a term used in the
>> manual. I can lower-case it if you would prefer, seems that manual
>> actually uses both casings now when I looked for it.
>>
>> I do not think inventing new terminology is a good idea, since users
>> will not be able to find it in the manual distributed with the program.
>
> The 'slave' word always carry a negative connotation. I think it's a
> good idea to be proactive on modernizing the terminology where we can.

In that case I would suggest to contact the upstream and convince them
regarding this topic. Once that happens, we will be able to adjust the
documentation with the next release (which would be free of offensive
terminology).

In any case, this is not a first occurrence of the word "slave" in the
Guix repository nor manual.

Toggle quote (23 lines)
>> Given your requirement for sorting everything lexicographically (with no
>> grouping), the following:
>>
>> (use-modules (gnu services)
>> (srfi srfi-1))
>>
>> (when #f
>> (modify-services '()
>> (delete 'x)))
>>
>>
>> Prints a warning when compiled:
>>
>> WARNING: (guile-user): imported module (gnu services) overrides core binding `delete'
>>
>> However, when done "my" way (putting srfi-1 first, since "stdlib"), the
>> warning goes away.
>
> That's an interesting workaround, but I think we should look into that
> problem and fix it definitely I often used #:hidden (delete) without any
> issue it seems, so I'm not even sure if its existence still solves a
> true problem in current Guile.

I have recently (~few months back) tried to remove the `delete' and it a
wall quickly, so I assume for reasons I do not understand it is still
required.

Having to use #:hidden (delete) on every single relevant import is in my
opinion much worse solution than just ordering imports with srfi-1 at
the top.

But, I concur, it would be best it this just worked in any order.

Anyway, I have sorted the modules as requested. I am not sure whether
to sort

Toggle snippet (4 lines)
#$@(apcupsd-event-handlers-modules
(apcupsd-configuration-event-handlers cfg))

Under `a' or `m' though. Opinion?

Toggle quote (16 lines)
>
> [...]
>
>>>> +(define-configuration/no-serialization apcupsd-event-handlers
>>>> + (killpower
>>>> + (gexp
>>>> + #~((wall "Apccontrol doing: apcupsd --killpower on UPS ~a" name)
>>>> + (sleep 10)
>>>> + (apcupsd "--killpower")
>>>> + (wall "Apccontrol has done: apcupsd --killpower on UPS ~a" name)))
>>>> + "Handler for killpower event.")
>>>
>
> By the way, why do we need to sleep 10 seconds here? It seems
> unnecessary.

All default handlers are just conversions of the official apccontrol
script, and it has the sleep in there. I am not sure why, so I have
decided it would be better to leave it as it was.

Toggle quote (4 lines)
> I just thought about it, perhaps it could be more concise to extract the
> field values using match-record (these are bound to shorter names, since
> they omit the 'apcupsd-configuration-' prefix).

If I got the idea right, you suggest converting

Toggle snippet (5 lines)
("emergency"
#$@(apcupsd-event-handlers-emergency
(apcupsd-configuration-event-handlers cfg)))

into

Toggle snippet (6 lines)
("emergency"
#$@(match-record cfg <apcupsd-configuration> (handlers)
(match-record handlers <apcupsd-event-handlers> (emergency)
emergency)))

I am not sure it is increase in readability, but maybe I misunderstood
the suggestion.

Toggle quote (23 lines)
>>>> + (let ((run-dir (apcupsd-configuration-run-dir cfg)))
>>>> + (mixed-text-file
>>>> + "apcupsd.conf"
>>>> + "\
>>>> +## apcupsd.conf v1.1 ##
>>>> +#
>>>> +# for apcupsd release 3.14.14 (31 May 2016) - GNU Guix
>>>
>>> Is this config really bound to a version like above? Unless they change
>>> things in backward incompatible ways, it should work for newer ones too,
>>> no?
>>
>> This file header is taken from the official configuration file
>> distributed with the apcupsd (except the GNU Guix part). So while I do
>> expect that this configuration file should work with newer version, the
>> upstream decided to put the version into the configuration file.
>>
>> I have no strong opinion either way here, should I just remove the file
>> header completely?
>
> I think so, since we're going the path of producing our own, we don't
> need to keep things that are not deemed useful.

I have removed the version, but kept the header, so that there is
reference to apcupsd-service-type left in the file.

Toggle quote (33 lines)
>>>> + (match-record cfg <apcupsd-configuration>
>>>> + ( package pid-file debug-level run-dir
>>> ^ extraneous space
>>
>> This is intentional, it is neat little trick I have learned recently.
>> Without the extra leading space, Emacs indents it in this way:
>>
>> (match-record config <apcupsd-configuration>
>> (apcupsd pid-file debug-level run-dir
>> shepherd-base-name auto-start?)
>>
>>
>> However, with the extra leading space, the indentation changes to:
>>
>> (match-record config <apcupsd-configuration>
>> ( apcupsd pid-file debug-level run-dir
>> shepherd-base-name auto-start?)
>>
>>
>> Since in this case it is not a procedure call, but a list, first member
>> (apcupsd) is not special in any way. So it feels weird for it to have a
>> special priority regarding the indentation. With the extra leading
>> space, all list members are equal, as they should be.
>
> I see; that's an issue with our .dir-locals.el indentation rules. It
> should be fixed there, not worked around in actual code, though I can
> that workaround documented in 'info "(emacs) Lisp Indent"', for lists.
> In theory, we could define a function and assign it to the
> lisp-indent-function property (see .dir-locals.el), per 'C-h f
> scheme-indent-function' in Emacs to do whatever we like. While an
> interesting side project, I think using the workaround you propose is
> fine until then :-).

Getting bit off topic, but I am not sure how that would work. Between
various macros, and Scheme being LISP-1, I have no idea how to write
robust code that would just magically always indent "right".

But I would be glad to be proven wrong. :)

Toggle quote (18 lines)
>
> [...]
>
>>> If you want, margin comments are ok without space or full sentence
>>> (punctuation), e.g.:
>>>
>>> ;do not daemonize
>>
>> I have no strong preference. What is the convention in Guix/Guile
>> community? Or is this down to each individual programmer?
>
> Our coding style in general comes from
> https://mumble.net/~campbell/scheme/style.txt (info '(guix) Formatting
> Code'), which mentions the above but leaves the choice. Since Ludo uses
> the above style, and authored most of the original code, it's the
> prevalent style in the code base and I suggest we stick to it, for
> consistency.

Sure, adjusted.

Toggle quote (12 lines)
>
>> I have added:
>>
>> ;; The apcupsd can be configured to prevent users from logging in on certain
>> ;; conditions. This is implemented by creation of a "nologin" file, and
>> ;; using a pam nologin module to prevent the login (if the file exists).
>
> Thanks, that helps!
>
> I'll let you take care of the small remaining things, and probably the
> next revision will be good to go.

One additional change I have made is to change

Toggle snippet (3 lines)
(requirement '())

into

Toggle snippet (3 lines)
(requirement '(user-processes))

Without the dependency, the daemon got respawned by shepherd on shutdown
and prevented the machine from going offline, which sucked a bit. ^_^

I thought I would mention it so that you can keep it in mind for future
code reviews.

With that I think I am out of things that I want to change or would
appreciate feedback on, so I am sending a v2. Thank you for working
with me through this, the code is much better than in v1.

Tomas

--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
-----BEGIN PGP SIGNATURE-----

iQJCBAEBCgAsFiEEt4NJs4wUfTYpiGikL7/ufbZ/wakFAmeyAU0OHH5Ad29sZnNk
ZW4uY3oACgkQL7/ufbZ/wak4wg/+PGqF0Acnye2mO13Lw+gXEmqCgvtzSZidWT5h
Zm0v+amhsB4z663+3HvGBCJ1CEcbze7myZpWPC88jtNkzytwD3YUPLUXu9ruvjBy
XfFOZf6iGdjdvg/M9mwCNGqO1RPCDPa3gKC3kJQdZZ3gqd4r2Sp6DWnJs6gK+qpT
KBkiX3h0snPZgydqImM0h8c3J70cv1WmQaGX+IYGuNxQdBnFArmGyu9G2dLNwYMp
O+uCAtpGPDiT8gCwXv8ck6ItvRY7vfil6Vc57/kEpar67D+ZNkNLnUsVzZkrXlBc
he5kXcyURvtvwx7csfCFkHKvVw6LuHNAvOqx9m3+wepZWVPPXYj/93H/eBe999Rj
TM5/ej7+qXyfX90drf+z4BSmhtVbo0JbtNrN12eS8SGD5qEEYt1SXZijG1PDnsUE
QGS/avJRVlOQgLKlpEwjSICdCO47BzCQui3C0B/CLn4cLk6eDRLcKVY2xK/5xn+6
e3JhpQwoapTpcD6HCqklF0OwCr3NjWRLChNuMbOAWMs5Q/P0eUlXfItBB+sQOiMy
H5IJzWvkLWQVia/AFnndYJmx1EzRoKtK5w/dOQwXefY58+3iZbS2TfkN+9lpA3iv
hXmlkSZZhIzP348xXbIT9wcWnL0uXLdL9PUba265As4mGWOGo6NBgtPUzud+4mQt
bglEh18=
=C70Z
-----END PGP SIGNATURE-----

Tomas Volf wrote 4 weeks ago
[PATCH v2 1/2] gnu: Add apcupsd.
(address . 75528@debbugs.gnu.org)(name . Tomas Volf)(address . ~@wolfsden.cz)(name . Raven Hallsby)(address . karl@hallsby.com)
9b91fcc95120426959b89c466beadf61505fee59.1739719006.git.~@wolfsden.cz
* gnu/packages/power.scm (apcupsd): New variable.
* gnu/local.mk (GNU_SYSTEM_MODULES): Register the new file.

Co-authored-by: Raven Hallsby <karl@hallsby.com>
Change-Id: I5366f6deea111a6a9ea56648122cdc8b3297f08c
---
gnu/local.mk | 1 +
gnu/packages/power.scm | 122 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 123 insertions(+)
create mode 100644 gnu/packages/power.scm

Toggle diff (142 lines)
diff --git a/gnu/local.mk b/gnu/local.mk
index bbe6fe4fce..3e9740779e 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -560,6 +560,7 @@ GNU_SYSTEM_MODULES = \
%D%/packages/polkit.scm \
%D%/packages/popt.scm \
%D%/packages/potassco.scm \
+ %D%/packages/power.scm \
%D%/packages/printers.scm \
%D%/packages/profiling.scm \
%D%/packages/prolog.scm \
diff --git a/gnu/packages/power.scm b/gnu/packages/power.scm
new file mode 100644
index 0000000000..843a907aa5
--- /dev/null
+++ b/gnu/packages/power.scm
@@ -0,0 +1,122 @@
+;;; Copyright © 2025 Tomas Volf <~@wolfsden.cz>
+;;; Copyright © 2023 Raven Hallsby <karl@hallsby.com>
+
+;;;; Commentary:
+
+;;; Power-related packages.
+
+;;;; Code:
+
+(define-module (gnu packages power)
+ #:use-module (gnu)
+ #:use-module (gnu packages libusb)
+ #:use-module (gnu packages linux)
+ #:use-module (gnu packages man)
+ #:use-module (gnu packages pkg-config)
+ #:use-module (gnu packages python-xyz)
+ #:use-module (guix build-system gnu)
+ #:use-module (guix download)
+ #:use-module (guix gexp)
+ #:use-module ((guix licenses) #:prefix license:)
+ #:use-module (guix packages))
+
+(define-public apcupsd
+ (package
+ (name "apcupsd")
+ (version "3.14.14")
+ (source (origin
+ (method url-fetch)
+ (uri
+ (string-append
+ "mirror://sourceforge/" name "/" name " - Stable/" version
+ "/" name "-" version ".tar.gz"))
+ (sha256
+ (base32
+ "0rwqiyzlg9p0szf3x6q1ppvrw6f6dbpn2rc5z623fk3bkdalhxyv"))))
+ (outputs '("out" "doc"))
+ (build-system gnu-build-system)
+ (arguments
+ (list
+ #:configure-flags
+ #~(list
+ ;; The configure script ignores --prefix for most of the file names.
+ (string-append "--exec-prefix=" #$output)
+ (string-append "--mandir=" #$output "/share/man")
+ (string-append "--sbindir=" #$output "/sbin")
+ (string-append "--sysconfdir=" #$output "/etc/apcupsd")
+ (string-append "--with-halpolicydir=" #$output "/share/halpolicy")
+
+ ;; Put us into the version string.
+ "--with-distname=GNU Guix"
+ "--disable-install-distdir"
+
+ ;; State directories.
+ "--localstatedir=/var"
+ "--with-log-dir=/var/log"
+ "--with-pid-dir=/run"
+ "--with-lock-dir=/run/apcupsd/lock"
+ "--with-nologin=/run/apcupsd"
+ "--with-pwrfail-dir=/run/apcupsd"
+
+ ;; Configure requires these, but we do not use the genenerated
+ ;; apcupsd.conf, so in order to reduce dependencies of the package,
+ ;; provide fake values.
+ (string-append "ac_cv_path_SHUTDOWN=/nope")
+ (string-append "ac_cv_path_APCUPSD_MAIL=/nope")
+ ;; While `wall' is not expanded anywhere, it still is searched for.
+ ;; See https://sourceforge.net/p/apcupsd/mailman/message/59128628/ .
+ (string-append "ac_cv_path_WALL=/nope")
+
+ ;; Enable additional drivers.
+ "--enable-usb"
+ "--enable-modbus-usb")
+ #:tests? #f ; There are no tests.
+ #:modules (cons '(ice-9 ftw) %default-gnu-modules)
+ #:phases
+ #~(modify-phases %standard-phases
+ (add-before 'configure 'remove-time-from-manual
+ (lambda _
+ ;; Do not bake the date and time of the build into the manual.
+ (substitute* "doc/manual/manual.rst"
+ (("\\| \\|date\\| \\|time\\|") ""))
+ (substitute* "autoconf/variables.mak.in"
+ (("^(RST2HTMLOPTS = .*) --time (.*)" all pref suff)
+ (string-append pref " " suff)))))
+ (add-after 'build 'build-manual
+ (lambda _
+ (invoke "make" "-C" "doc/manual" "manual.html")))
+ (add-after 'install-license-files 'move-doc
+ (lambda _
+ (let ((target (string-append #$output:doc
+ "/share/doc/"
+ (strip-store-file-name #$output))))
+ (mkdir-p target)
+ (for-each (lambda (f)
+ (copy-file (string-append "doc/manual/" f)
+ (string-append target "/" f)))
+ (scandir "doc/manual"
+ (lambda (f)
+ (or (string-suffix? ".png" f)
+ (string-suffix? ".html" f))))))))
+ ;; If sending mails is required, use proper mail program.
+ (add-after 'install 'remove-smtp
+ (lambda _
+ (delete-file (string-append #$output "/sbin/smtp"))))
+ ;; The configuration files and scripts are not really suitable for
+ ;; Guix, and our service provides its own version anyway. So delete
+ ;; these to make sure `apcupsd' and `apctest' executed without any
+ ;; arguments fail. `apctest' actually segfaults, but only after
+ ;; printing an error.
+ (add-after 'install 'remove-etc-apcupsd
+ (lambda _
+ (delete-file-recursively
+ (string-append #$output "/etc/apcupsd")))))))
+ (native-inputs (list mandoc pkg-config python-docutils util-linux))
+ (inputs (list libusb libusb-compat))
+ (home-page "http://www.apcupsd.org")
+ (synopsis "Daemon for controlling APC UPSes")
+ (description "The apcupsd can be used for power management and controlling
+most of APC’s UPS models on Unix and Windows machines. The apcupsd works with
+most of APC’s Smart-UPS models as well as most simple signalling models such a
+Back-UPS, and BackUPS-Office.")
+ (license license:gpl2)))
--
2.48.1
Tomas Volf wrote 4 weeks ago
[PATCH v2 2/2] services: Add power.
(address . 75528@debbugs.gnu.org)(name . Tomas Volf)(address . ~@wolfsden.cz)
0b295c3bf879d00157b0476744e218ad0d921656.1739719006.git.~@wolfsden.cz
* gnu/services/power.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (Power Management Services): Document service and data types.

Change-Id: If205d19bea1d20a99309626e28521a2d6fe6702f
---
doc/guix.texi | 382 +++++++++++++++++++++-
gnu/local.mk | 1 +
gnu/services/power.scm | 711 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 1091 insertions(+), 3 deletions(-)
create mode 100644 gnu/services/power.scm

Toggle diff (533 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index f8b3022ccf..eee47d0b86 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -123,7 +123,7 @@
Copyright @copyright{} 2023 Thomas Ieong@*
Copyright @copyright{} 2023 Saku Laesvuori@*
Copyright @copyright{} 2023 Graham James Addis@*
-Copyright @copyright{} 2023, 2024 Tomas Volf@*
+Copyright @copyright{} 2023-2025 Tomas Volf@*
Copyright @copyright{} 2024, 2025 Herman Rimm@*
Copyright @copyright{} 2024 Matthew Trzcinski@*
Copyright @copyright{} 2024 Richard Sent@*
@@ -421,7 +421,7 @@ Top
* Network File System:: NFS related services.
* Samba Services:: Samba services.
* Continuous Integration:: Cuirass and Laminar services.
-* Power Management Services:: Extending battery life.
+* Power Management Services:: Extending battery life, etc.
* Audio Services:: The MPD.
* Virtualization Services:: Virtualization services.
* Version Control Services:: Providing remote access to Git repositories.
@@ -19269,7 +19269,7 @@ Services
* Network File System:: NFS related services.
* Samba Services:: Samba services.
* Continuous Integration:: Cuirass and Laminar services.
-* Power Management Services:: Extending battery life.
+* Power Management Services:: Extending battery life, etc.
* Audio Services:: The MPD.
* Virtualization Services:: Virtualization services.
* Version Control Services:: Providing remote access to Git repositories.
@@ -36516,6 +36516,382 @@ Power Management Services
@end table
@end deftp
+The @code{(gnu services power)} module provides a service definition for
+@uref{http://www.apcupsd.org/, apcupsd}, a utility to interact with
+@acronym{APC, APC by Schneider Electric or formerly American Power
+Conversion Corporation} @acronym{UPS, Uninterruptible Power Supply}
+devices. Apcupsd also works with some @acronym{OEM, Original Equipment
+Manufacturer}-branded products manufactured by APC.
+
+@defvar apcupsd-service-type
+The service type for apcupsd. For USB UPSes no configuration is
+necessary, however tweaking some fields to better suit your needs might
+be desirable. The defaults are taken from the upstream configuration
+and they are not very conservative (for example, the default
+@code{battery-level} of 5% may be considered too low by some).
+
+The default event handlers do send emails, read more in
+@ref{apcupsd-event-handlers}.
+
+@lisp
+(service apcupsd-service-type)
+@end lisp
+@end defvar
+
+@deftp {Data Type} apcupsd-configuration
+
+Available @code{apcupsd-configuration} fields are:
+
+@table @asis
+@item @code{apcupsd} (default: @code{apcupsd}) (type: package)
+The @code{apcupsd} package to use.
+
+@item @code{shepherd-service-name} (default: @code{apcupsd}) (type: symbol)
+The name of the shepherd service. You can add the service multiple
+times with different names to manage multiple UPSes.
+
+@item @code{auto-start?} (default: @code{#t}) (type: boolean)
+Should the shepherd service auto-start?
+
+@item @code{pid-file} (default: @code{"/var/run/apcupsd.pid"}) (type: string)
+The file name of the pid file.
+
+@item @code{debug-level} (default: @code{0}) (type: integer)
+The logging verbosity. Bigger number means more logs. The source code
+uses up to @code{300} as debug level value, so a value of @code{999}
+seems reasonable to enable all the logs.
+
+@item @code{run-dir} (default: @code{"/var/run/apcupsd"}) (type: string)
+The directory containing runtime information. You need to change this
+if you desire to run multiple instances of the daemon.
+
+@item @code{name} (type: maybe-string)
+Use this to give your UPS a name in log files and such. This is
+particularly useful if you have multiple UPSes. This does not set the
+EEPROM. It should be 8 characters or less.
+
+@item @code{cable} (default: @code{usb}) (type: enum-cable)
+The type of a cable connecting the UPS to your computer. Possible
+generic choices are @code{'simple}, @code{'smart}, @code{'ether} and
+@code{'usb}.
+
+Alternatively, a specific cable model number may be used:
+@code{'940-0119A}, @code{'940-0127A}, @code{'940-0128A},
+@code{'940-0020B}, @code{'940-0020C}, @code{'940-0023A},
+@code{'940-0024B}, @code{'940-0024C}, @code{'940-1524C},
+@code{'940-0024G}, @code{'940-0095A}, @code{'940-0095B},
+@code{'940-0095C}, @code{'940-0625A}, @code{'M-04-02-2000}.
+
+@item @code{type} (default: @code{usb}) (type: enum-type)
+The type of the UPS you have.
+
+@table @code
+@item apcsmart
+Newer serial character device, appropriate for SmartUPS models using a
+serial cable (not an USB).
+
+@item usb
+Most new UPSes are an USB.
+
+@item net
+Network link to a master apcupsd through apcupsd's Network Information
+Server. This is used if the UPS powering your computer is connected to
+a different computer for monitoring.
+
+@item snmp
+SNMP network link to an SNMP-enabled UPS device.
+
+@item netsnmp
+Same as the SNMP above but requires use of the net-snmp library. Unless
+you have a specific need for this old driver, you should use the
+@code{'snmp} instead.
+
+@item dumb
+An old serial character device for use with simple-signaling UPSes.
+
+@item pcnet
+A PowerChute Network Shutdown protocol which can be used as an
+alternative to an SNMP with the AP9617 family of smart slot cards.
+
+@item modbus
+A serial device for use with newest SmartUPS models supporting the
+MODBUS protocol.
+
+@end table
+
+@item @code{device} (default: @code{""}) (type: string)
+For USB UPSes, usually you want to set this to an empty string (the
+default). For other UPS types, you must specify an appropriate port or
+address.
+
+@table @code
+@item apcsmart
+Set to the appropriate @file{/dev/tty**} device.
+
+@item usb
+A null string setting enables auto-detection, which is the best choice
+for most installations.
+
+@item net
+Set to @code{@var{hostname}:@var{port}}.
+
+@item snmp
+Set to @code{@var{hostname}:@var{port}:@var{vendor}:@var{community}}.
+The @var{hostname} is the ip address or hostname of the UPS on the
+network. The @var{vendor} can be can be "APC" or "APC_NOTRAP".
+"APC_NOTRAP" will disable SNMP trap catching; you usually want "APC".
+The @var{port} is usually 161. The @var{community} is usually
+"private".
+
+@item netsnmp
+Same as the @code{'snmp}.
+
+@item dumb
+Set to the appropriate @file{/dev/tty**} device.
+
+@item pcnet
+Set to @code{@var{ipaddr}:@var{username}:@var{passphrase}:@var{port}}.
+The @var{ipaddr} is the IP address of the UPS management card. The
+@var{username} and the @var{passphrase} are the credentials for which
+the card has been configured. The @var{port} is the port number on
+which to listen for messages from the UPS, normally 3052. If this
+parameter is empty or missing, the default of 3052 will be used.
+
+@item modbus
+Set to the appropriate @file{/dev/tty**} device. You can also leave it
+empty for MODBUS over USB or set to the serial number of the UPS.
+
+@end table
+
+@item @code{poll-time} (default: @code{60}) (type: integer)
+The interval (in seconds) at which apcupsd polls the UPS for status.
+This setting applies both to directly-attached UPSes (apcsmart, usb,
+dumb) and networked UPSes (net, snmp). Lowering this setting will
+improve the apcupsd's responsiveness to certain events at the cost of
+higher CPU utilization.
+
+@item @code{on-batery-delay} (default: @code{6}) (type: integer)
+The time in seconds from when a power failure is detected until we react
+to it with an onbattery event. The @code{'powerout} event will be
+triggered immediately when a power failure is detected. However, the
+@code{'onbattery} event will be trigger only after this delay.
+
+@item @code{battery-level} (default: @code{5}) (type: integer)
+If during a power failure, the remaining battery percentage (as reported
+by the UPS) is below or equal to this value, the apcupsd will initiate a
+system shutdown.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in a conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{remaining-minutes} (default: @code{3}) (type: integer)
+If during a power failure, the remaining runtime in minutes (as
+calculated internally by the UPS) is below or equal to this value,
+apcupsd will initiate a system shutdown.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in a conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{timeout} (default: @code{0}) (type: integer)
+If during a power failure, the UPS has run on batteries for this many
+seconds or longer, apcupsd will initiate a system shutdown. The value
+of 0 disables this timer.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in a conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{annoy-interval} (default: @code{300}) (type: integer)
+The time in seconds between annoying users (via the @code{'annoyme}
+event) to sign off prior to system shutdown. 0 disables.
+
+@item @code{annoy-delay} (default: @code{60}) (type: integer)
+The initial delay in seconds after a power failure before warning users
+to get off the system.
+
+@item @code{no-logon} (default: @code{disable}) (type: enum-no-logon)
+The condition which determines when users are prevented from logging in
+during a power failure.
+
+@item @code{kill-delay} (default: @code{0}) (type: integer)
+If this is non-zero, the apcupsd will continue running after a shutdown
+has been requested, and after the specified time in seconds attempt to
+kill the power. This is for use on systems where apcupsd cannot regain
+control after a shutdown.
+
+@item @code{net-server} (default: @code{#f}) (type: boolean)
+If enabled, a network information server process will be started.
+
+@item @code{net-server-ip} (default: @code{"127.0.0.1"}) (type: string)
+An IP address on which the NIS server will listen for incoming
+connections.
+
+@item @code{net-server-port} (default: @code{3551}) (type: integer)
+An IP port on which the NIS server will listen for incoming connections.
+
+@item @code{net-server-events-file} (type: maybe-string)
+If you want the last few EVENTS to be available over the network by the
+network information server, you must set this to a file name.
+
+@item @code{net-server-events-file-max-size} (default: @code{10}) (type: integer)
+The maximum size of the events file in kilobytes.
+
+@item @code{class} (default: @code{standalone}) (type: enum-class)
+Normally standalone unless you share an UPS using an APC ShareUPS card.
+
+@item @code{mode} (default: @code{disable}) (type: enum-mode)
+Normally disable unless you share an UPS using an APC ShareUPS card.
+
+@item @code{stat-time} (default: @code{0}) (type: integer)
+The time interval in seconds between writing the status file, 0
+disables.
+
+@item @code{log-stats} (default: @code{#f}) (type: boolean)
+Also write the stats as a logs. This generates a lot of output.
+
+@item @code{data-time} (default: @code{0}) (type: integer)
+The time interval in seconds between writing the data records to the log
+file, 0 disables.
+
+@item @code{facility} (type: maybe-string)
+The logging facility for the syslog.
+
+@item @code{event-handlers} (type: apcupsd-event-handlers)
+Handlers for events produced by apcupsd.
+
+@end table
+@end deftp
+
+@anchor{apcupsd-event-handlers}
+@deftp {Data Type} apcupsd-event-handlers
+
+For a description of the events please refer to @samp{man 8 apccontrol}.
+
+Each handler shall be a gexp. It is spliced into the control script for
+the daemon. In addition to the standard Guile programming environment,
+the following procedures and variables are also available:
+
+@table @code
+@item conf
+Variable containing the file name of the configuration file.
+
+@item powerfail-file
+Variable containing the file name of the powerfail file.
+
+@item cmd
+The event currently being handled.
+
+@item name
+The name of the UPS as specified in the configuration file.
+
+@item connected?
+Is @code{#t} if @command{apcupsd} is connected to the UPS via a serial
+port (or a USB port). In most configurations, this will be the case.
+In the case of a Slave machine where apcupsd is not directly connected
+to the UPS, this value will be @code{#f}.
+
+@item powered?
+Is @code{#t} if the computer on which @command{apcupsd} is running is
+powered by the UPS and @code{#f} if not. At the moment, this value is
+unimplemented and always @code{#f}.
+
+@item (err @var{fmt} @var{args...})
+Wrapper around @code{format} outputting to @code{(current-error-port)}.
+
+@item (wall @var{fmt} @var{args...})
+Wrapper around @code{format} outputting via @command{wall}.
+
+@item (apcupsd @var{args...})
+Call @command{apcupsd} while passing the correct configuration file and
+all the arguments.
+
+@item (mail-to-root @var{subject} @var{body})
+Send an email to the local administrator. This procedure assumes the
+@command{sendmail} is located at @command{/run/privileged/bin/sendmail}
+(as would be the case with @code{opensmtpd-service-type}).
+
+@end table
+
+Available @code{apcupsd-event-handlers} fields are:
+
+@table @asis
+@item @code{modules} (type: gexp)
+Additional modules to import into the generated handler script.
+
+@item @code{killpower} (type: gexp)
+The handler for the killpower event.
+
+@item @code{commfailure} (type: gexp)
+The handler for the commfailure event.
+
+@item @code{commok} (type: gexp)
+The handler for the commfailure event.
+
+@item @code{powerout} (type: gexp)
+The handler for the powerout event.
+
+@item @code{onbattery} (type: gexp)
+The handler for the onbattery event.
+
+@item @code{offbattery} (type: gexp)
+The handler for the offbattery event.
+
+@item @code{mainsback} (type: gexp)
+The handler for the mainsback event.
+
+@item @code{failing} (type: gexp)
+The handler for the failing event.
+
+@item @code{timeout} (type: gexp)
+The handler for the timeout event.
+
+@item @code{loadlimit} (type: gexp)
+The handler for the loadlimit event.
+
+@item @code{runlimit} (type: gexp)
+The handler for the runlimit event.
+
+@item @code{doreboot} (type: gexp)
+The handler for the doreboot event.
+
+@item @code{doshutdown} (type: gexp)
+The handler for the doshutdown event.
+
+@item @code{annoyme} (type: gexp)
+The handler for the annoyme event.
+
+@item @code{emergency} (type: gexp)
+The handler for the emergency event.
+
+@item @code{changeme} (type: gexp)
+The handler for the changeme event.
+
+@item @code{remotedown} (type: gexp)
+The handler for the remotedown event.
+
+@item @code{startselftest} (type: gexp)
+The handler for the startselftest event.
+
+@item @code{endselftest} (type: gexp)
+The handler for the endselftest event.
+
+@item @code{battdetach} (type: gexp)
+The handler for the battdetach event.
+
+@item @code{battattach} (type: gexp)
+The handler for the battattach event.
+
+@end table
+@end deftp
+
@node Audio Services
@subsection Audio Services
diff --git a/gnu/local.mk b/gnu/local.mk
index 3e9740779e..61f652876e 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -761,6 +761,7 @@ GNU_SYSTEM_MODULES = \
%D%/services/nix.scm \
%D%/services/nfs.scm \
%D%/services/pam-mount.scm \
+ %D%/services/power.scm \
%D%/services/science.scm \
%D%/services/security.scm \
%D%/services/security-token.scm \
diff --git a/gnu/services/power.scm b/gnu/services/power.scm
new file mode 100644
index 0000000000..72b2a40fef
--- /dev/null
+++ b/gnu/services/power.scm
@@ -0,0 +1,711 @@
+;;; Copyright © 2025 Tomas Volf <~@wolfsden.cz>
+
+;;;; Commentary:
+
+;;; Power-related services.
+
+;;;; Code:
+
+(define-module (gnu services power)
+ #:use-module (gnu)
+ #:use-module (gnu packages admin)
+ #:use-module (gnu packages linux)
+ #:use-module (gnu packages power)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services shepherd)
+ #:use-module (gnu services)
+ #:use-module (guix packages)
+ #:use-module (guix records)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
+ #:export (apcupsd-service-type
+
+ apcupsd-configuration
+ apcupsd-configuration-apcupsd
+ apcupsd-configuration-shepherd-service-name
+ apcupsd-configuration-auto-start?
+ apcupsd-configuration-pid-file
+ apcupsd-configuration-debug-level
+ apcupsd-configuration-run-dir
+ apcupsd-configuration-name
+ apcupsd-configuration-cable
+ apcupsd-configuration-type
+ apcupsd-configuration-device
+ apcupsd-configuration-poll-time
+ apcupsd-configuration-on-batery-delay
+ apcupsd-configuration-battery-level
+ apcupsd-configuration-remaining-minutes
+ apcupsd-configuration-timeout
+ apcupsd-configuration-annoy-interval
+ apcupsd-configuration-annoy-delay
+ apcupsd-configuration-no-logon
+ apcupsd-configuration-kill-delay
+ apcupsd-configuration-net-server
+ apcupsd-configuration-net-server-ip
+ apcupsd-configuration-net-server-port
+ apcupsd-configuration-net-server-events-file
+ apcupsd-configuration-net-server-events-file-max-size
+ apcupsd-configuration-class
+ apcupsd-configuration-mode
+ apcupsd-configuration-stat-time
+ apcupsd-configuration-log-stats
+ apcupsd-configuration-data-time
+ apcupsd-configuration-facility
+ apcupsd-configuration-event-handlers
+
+ apcupsd-event-handlers
+ apcupsd-event-handlers-modules
+ apcupsd-event-handlers-annoyme
+ apcupsd-event-handlers-battattach
+ apcupsd-event-handlers-battdetach
+ apcupsd-event-handlers-changeme
+ apcupsd-event-handlers-commfailure
+ apcupsd-event-handlers-commok
+ apcupsd-event-handlers-doreboot
+ apcupsd-event-handlers-doshutdown
+ apcupsd-event-handlers-emergency
+ apcupsd-event-handlers-endselftest
+ apcupsd-event-handlers-failing
+ apcupsd-event-handlers-killpower
+ apcupsd-event-handlers-loadlimit
+ apcupsd-event-handlers-mainsback
+ apcupsd-event-handlers-offbattery
+ apcupsd-event-handlers-onbattery
+ apcupsd-event-handlers-powerout
+ apcupsd-event-handlers-remotedown
+ apcupsd-event-handlers-runlimit
+ apcupsd-event-handlers-startselftest
+ apcupsd-event-handlers-timeout))
+
+(define-configuration/no-serialization apcupsd-event-handlers
+ (modules
+ (gexp #~())
+ "Additional modules to import into the generated handler script.")
+ (killpower
+ (gexp
+ #~((wall "Apccontrol doing: apcupsd --killpower on UPS ~a" name)
+ (sleep 10)
+ (apcupsd "--killpower")
+ (wall "Apccontrol has done: apcupsd --killpower on UPS ~a" name)))
+ "The handler for the killpower event.")
+ (commfailure
+ (gexp
+ #~((let ((msg (format #f "~a Communications with UPS ~a lost."
+ (gethostname) name)))
+ (mail-to-root msg msg))
+ (wall "Warning: communications lost with UPS ~a" name)))
+ "The handler for the commfailure event.")
+ (commok
+ (gexp
+ #~(
This message was truncated. Download the full message here.
Maxim Cournoyer wrote 3 weeks ago
Re: [bug#75528] [PATCH 2/2] services: Add power.
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
87r03vycdh.fsf@gmail.com
Hi Tomas,

Tomas Volf <~@wolfsden.cz> writes:

[...]

Toggle quote (13 lines)
>> Deprecating /var/run to /run is not a Guix thing, but a File Hiearchy
>> Standard (FHS) thing [0]. Since we already use /run, there's no good
>> reason to have /var/run something else than a symlink to /run, and
>> there's an actual change pending merge that would do that, along making
>> /run mounted as a tmpfs (bug#73494).
>>
>> [0] https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s13.html
>
> I did not know we actually care about FHS (for example our /usr/bin
> definitely does not look like it should in that case :) ).
>
> I did the s|/var/run|/run|.

We typically do not follow FHS much due to each of our packages being
installed under their own prefix, but for the few things where we do,
like here, I think it makes sense to stick to it, as it brings
familiarity and consistency to our users.

Toggle quote (21 lines)
>>>> s/Slave/slave/ ? Could be nicer to use a neutral alternative like
>>>> 'follower machine'.
>>>
>>> The "Slave machine" (including the capitalization) is a term used in the
>>> manual. I can lower-case it if you would prefer, seems that manual
>>> actually uses both casings now when I looked for it.
>>>
>>> I do not think inventing new terminology is a good idea, since users
>>> will not be able to find it in the manual distributed with the program.
>>
>> The 'slave' word always carry a negative connotation. I think it's a
>> good idea to be proactive on modernizing the terminology where we can.
>
> In that case I would suggest to contact the upstream and convince them
> regarding this topic. Once that happens, we will be able to adjust the
> documentation with the next release (which would be free of offensive
> terminology).
>
> In any case, this is not a first occurrence of the word "slave" in the
> Guix repository nor manual.

That there are currently other occurrences is not a good reason to add
more, in my opinion :-). That said, I've looked at upstream sources and
it's true that the old master/slave terminology is currently deeply
interwined in code and doc... so I did an experiment and came up with
svn diff to upstream via their mailing list. We'll see what they think.


[...]

Toggle quote (20 lines)
>>> WARNING: (guile-user): imported module (gnu services) overrides core binding `delete'
>>>
>>> However, when done "my" way (putting srfi-1 first, since "stdlib"), the
>>> warning goes away.
>>
>> That's an interesting workaround, but I think we should look into that
>> problem and fix it definitely I often used #:hidden (delete) without any
>> issue it seems, so I'm not even sure if its existence still solves a
>> true problem in current Guile.
>
> I have recently (~few months back) tried to remove the `delete' and it a
> wall quickly, so I assume for reasons I do not understand it is still
> required.
>
> Having to use #:hidden (delete) on every single relevant import is in my
> opinion much worse solution than just ordering imports with srfi-1 at
> the top.
>
> But, I concur, it would be best it this just worked in any order.

OK, good to know, but let's keep this out of scope for the context of
this change.

Toggle quote (9 lines)
> Anyway, I have sorted the modules as requested. I am not sure whether
> to sort
>
> #$@(apcupsd-event-handlers-modules
> (apcupsd-configuration-event-handlers cfg))
>
>
> Under `a' or `m' though. Opinion?

I'm not sure I understood the question, but under `a' makes more sense
to me (which `m' are you referring to?).

Toggle quote (17 lines)
>>>>> +(define-configuration/no-serialization apcupsd-event-handlers
>>>>> + (killpower
>>>>> + (gexp
>>>>> + #~((wall "Apccontrol doing: apcupsd --killpower on UPS ~a" name)
>>>>> + (sleep 10)
>>>>> + (apcupsd "--killpower")
>>>>> + (wall "Apccontrol has done: apcupsd --killpower on UPS ~a" name)))
>>>>> + "Handler for killpower event.")
>>>>
>>
>> By the way, why do we need to sleep 10 seconds here? It seems
>> unnecessary.
>
> All default handlers are just conversions of the official apccontrol
> script, and it has the sleep in there. I am not sure why, so I have
> decided it would be better to leave it as it was.

OK, I guess it's safer to keep it.

Toggle quote (22 lines)
>> I just thought about it, perhaps it could be more concise to extract the
>> field values using match-record (these are bound to shorter names, since
>> they omit the 'apcupsd-configuration-' prefix).
>
> If I got the idea right, you suggest converting
>
> ("emergency"
> #$@(apcupsd-event-handlers-emergency
> (apcupsd-configuration-event-handlers cfg)))
>
>
> into
>
> ("emergency"
> #$@(match-record cfg <apcupsd-configuration> (handlers)
> (match-record handlers <apcupsd-event-handlers> (emergency)
> emergency)))
>
>
> I am not sure it is increase in readability, but maybe I misunderstood
> the suggestion.

I think something like this should work:

Toggle snippet (183 lines)
modified gnu/services/power.scm
@@ -494,119 +494,67 @@ (define-configuration apcupsd-configuration
empty-serializer))
(define (%apccontrol config)
- (program-file
- "apccontrol"
- #~(begin
- (use-modules (ice-9 format)
- (ice-9 match)
- (ice-9 popen)
- (srfi srfi-9)
- #$@(apcupsd-event-handlers-modules
- (apcupsd-configuration-event-handlers config)))
- ;; Script dir depends on these, and the configuration depends on the
- ;; script dir. To sever the cyclic dependency, pass the file names via
- ;; environment variables.
- (define conf (getenv "GUIX_APCUPSD_CONF"))
- (define powerfail-file (getenv "GUIX_APCUPSD_POWERFAIL_FILE"))
-
- (define (err . args)
- (apply format (current-error-port) args))
- (define (wall . args)
- (system* #$(file-append util-linux "/bin/wall") (apply format #f args)))
- (define (apcupsd . args)
- (apply system* #$(file-append apcupsd "/sbin/apcupsd") "-f" conf args))
- (define (mail-to-root subject body)
- (let ((port (open-pipe* OPEN_WRITE
- "/run/privileged/bin/sendmail"
- "-F" "apcupsd"
- "root")))
- (format port "Subject: ~a~%~%~a~&" subject body)
- (close-pipe port)))
- (match (cdr (command-line))
- (((? string? cmd) name connected powered)
- (let ((connected? (match connected
+ (define event-handlers (apcupsd-configuration-event-handlers config))
+ (match-record event-handlers <apcupsd-event-handlers>
+ ( modules killpower commfailure commok powerout
+ onbattery offbattery ...)
+ (program-file
+ "apccontrol"
+ #~(begin
+ (use-modules (ice-9 format)
+ (ice-9 match)
+ (ice-9 popen)
+ (srfi srfi-9)
+ #$@modules)
+ ;; Script dir depends on these, and the configuration depends on the
+ ;; script dir. To sever the cyclic dependency, pass the file names via
+ ;; environment variables.
+ (define conf (getenv "GUIX_APCUPSD_CONF"))
+ (define powerfail-file (getenv "GUIX_APCUPSD_POWERFAIL_FILE"))
+
+ (define (err . args)
+ (apply format (current-error-port) args))
+
+ (define (wall . args)
+ (system* #$(file-append util-linux "/bin/wall") (apply format #f args)))
+
+ (define (apcupsd . args)
+ (apply system* #$(file-append apcupsd "/sbin/apcupsd") "-f" conf args))
+
+ (define (mail-to-root subject body)
+ (let ((port (open-pipe* OPEN_WRITE
+ "/run/privileged/bin/sendmail"
+ "-F" "apcupsd"
+ "root")))
+ (format port "Subject: ~a~%~%~a~&" subject body)
+ (close-pipe port)))
+
+ (match (cdr (command-line))
+ (((? string? cmd) name connected powered)
+ (let ((connected? (match connected
+ ("1" #t)
+ ("0" #f)))
+ (powered? (match powered
("1" #t)
- ("0" #f)))
- (powered? (match powered
- ("1" #t)
- ("0" #f))))
- (match cmd
- ;; I am sure this could be done by macro, but meh. Last release
- ;; of apcupsd was in 2016, so maintaining this will not be much
- ;; work.
- ("killpower"
- #$@(apcupsd-event-handlers-killpower
- (apcupsd-configuration-event-handlers config)))
- ("commfailure"
- #$@(apcupsd-event-handlers-commfailure
- (apcupsd-configuration-event-handlers config)))
- ("commok"
- #$@(apcupsd-event-handlers-commok
- (apcupsd-configuration-event-handlers config)))
- ("powerout"
- #$@(apcupsd-event-handlers-powerout
- (apcupsd-configuration-event-handlers config)))
- ("onbattery"
- #$@(apcupsd-event-handlers-onbattery
- (apcupsd-configuration-event-handlers config)))
- ("offbattery"
- #$@(apcupsd-event-handlers-offbattery
- (apcupsd-configuration-event-handlers config)))
- ("mainsback"
- #$@(apcupsd-event-handlers-mainsback
- (apcupsd-configuration-event-handlers config)))
- ("failing"
- #$@(apcupsd-event-handlers-failing
- (apcupsd-configuration-event-handlers config)))
- ("timeout"
- #$@(apcupsd-event-handlers-timeout
- (apcupsd-configuration-event-handlers config)))
- ("loadlimit"
- #$@(apcupsd-event-handlers-loadlimit
- (apcupsd-configuration-event-handlers config)))
- ("runlimit"
- #$@(apcupsd-event-handlers-runlimit
- (apcupsd-configuration-event-handlers config)))
- ("doreboot"
- #$@(apcupsd-event-handlers-doreboot
- (apcupsd-configuration-event-handlers config)))
- ("doshutdown"
- #$@(apcupsd-event-handlers-doshutdown
- (apcupsd-configuration-event-handlers config)))
- ("annoyme"
- #$@(apcupsd-event-handlers-annoyme
- (apcupsd-configuration-event-handlers config)))
- ("emergency"
- #$@(apcupsd-event-handlers-emergency
- (apcupsd-configuration-event-handlers config)))
- ("changeme"
- #$@(apcupsd-event-handlers-changeme
- (apcupsd-configuration-event-handlers config)))
- ("remotedown"
- #$@(apcupsd-event-handlers-remotedown
- (apcupsd-configuration-event-handlers config)))
- ("startselftest"
- #$@(apcupsd-event-handlers-startselftest
- (apcupsd-configuration-event-handlers config)))
- ("endselftest"
- #$@(apcupsd-event-handlers-endselftest
- (apcupsd-configuration-event-handlers config)))
- ("battdetach"
- #$@(apcupsd-event-handlers-battdetach
- (apcupsd-configuration-event-handlers config)))
- ("battattach"
- #$@(apcupsd-event-handlers-battattach
- (apcupsd-configuration-event-handlers config)))
- (_
- (err "Unknown event: ~a~%" cmd)
- (err "Iff the event was emitted by apcupsd, this is a bug.~%")
- (err "Please report to bug-guix@gnu.org.~%")
- (exit #f)))))
- (args
- (err "Unknown arguments: ~a~%" args)
- (err "Iff the arguments were passed by apcupsd, this is a bug.~%")
- (err "Please report to bug-guix@gnu.org.~%")
- (exit #f))))))
+ ("0" #f))))
+ (match cmd
+ ("killpower" #$killpower)
+ ("commfailure" #$commfailure)
+ ("commok" #$commok)
+ ("powerout" #$powerout)
+ ("onbattery" #$onbattery)
+ ("offbattery" #$offbattery)
+ [...]
+ (_
+ (err "Unknown event: ~a~%" cmd)
+ (err "If the event was emitted by apcupsd, this is a bug.~%")
+ (err "Please report to bug-guix@gnu.org.~%")
+ (exit #f)))))
+ (args
+ (err "Unknown arguments: ~a~%" args)
+ (err "If the arguments were passed by apcupsd, this is a bug.~%")
+ (err "Please report to bug-guix@gnu.org.~%")
+ (exit #f))))))))
(define (apcupsd-script-dir config)
(computed-file

[...]

Toggle quote (14 lines)
>>> This file header is taken from the official configuration file
>>> distributed with the apcupsd (except the GNU Guix part). So while I do
>>> expect that this configuration file should work with newer version, the
>>> upstream decided to put the version into the configuration file.
>>>
>>> I have no strong opinion either way here, should I just remove the file
>>> header completely?
>>
>> I think so, since we're going the path of producing our own, we don't
>> need to keep things that are not deemed useful.
>
> I have removed the version, but kept the header, so that there is
> reference to apcupsd-service-type left in the file.

Sounds reasonable.

[...]

Toggle quote (6 lines)
> Getting bit off topic, but I am not sure how that would work. Between
> various macros, and Scheme being LISP-1, I have no idea how to write
> robust code that would just magically always indent "right".
>
> But I would be glad to be proven wrong. :)

I guess you could have a procedure that calls the existing indenting
Elisp functions, and then have a post-processing pass on top to fix it
the way wa want. It's probably a bit over the top :-). I like
your/Emacs' down to Earth solution.

[...]

Toggle quote (12 lines)
> One additional change I have made is to change
>
> (requirement '())
>
>
> into
>
> (requirement '(user-processes))
>
> Without the dependency, the daemon got respawned by shepherd on shutdown
> and prevented the machine from going offline, which sucked a bit. ^_^

Oh wow; good catch. That seems a rather large foot gun to have for
service writers. I wonder if something could be done about it in the
Shepherd.

Toggle quote (3 lines)
> I thought I would mention it so that you can keep it in mind for future
> code reviews.

Indeed, thanks for bringing this to my attention.

Toggle quote (4 lines)
> With that I think I am out of things that I want to change or would
> appreciate feedback on, so I am sending a v2. Thank you for working
> with me through this, the code is much better than in v1.

I'll let you work through my last match-record suggestion above, if you
want and send a v3.

I'm also attaching a fixup with the terminology changes I've submitted
upstream if you'd like to validate it still works (I don't have any
UPS to try it with myself).
Thank you for your patience!

--
Thanks,
Maxim
Maxim Cournoyer wrote 3 weeks ago
Re: [bug#75528] [PATCH v2 1/2] gnu: Add apcupsd.
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)(name . Raven Hallsby)(address . karl@hallsby.com)
87wmdirpr0.fsf@gmail.com
Hi,

Tomas Volf <~@wolfsden.cz> writes:

Toggle quote (3 lines)
> * gnu/packages/power.scm (apcupsd): New variable.
> * gnu/local.mk (GNU_SYSTEM_MODULES): Register the new file.

Edited this one lightly:

Toggle snippet (16 lines)
modified gnu/packages/power.scm
@@ -115,8 +115,9 @@ (define-public apcupsd
(inputs (list libusb libusb-compat))
(home-page "http://www.apcupsd.org")
(synopsis "Daemon for controlling APC UPSes")
- (description "The apcupsd can be used for power management and controlling
-most of APC’s UPS models on Unix and Windows machines. The apcupsd works with
-most of APC’s Smart-UPS models as well as most simple signalling models such a
+ (description "@command{apcupsd} can be used for power management and
+controlling most of @acronym{APC, American Power Conversion}’s @acronym{UPS,
+Uninterruptible Power Supply} models. @command{apcupsd} works with most of
+APC’s Smart-UPS models as well as most simple signalling models such a
Back-UPS, and BackUPS-Office.")
(license license:gpl2)))

We do not mention Windows or other nonfree OS support in package
descriptions as Guix doesn't run natively there anyway and we'd rather
not advertise them.

And pushed. I'm still waiting for a v3 for the service (if you can
implement the match-record suggestion) to push 2/2.

--
Thanks,
Maxim
Tomas Volf wrote 3 weeks ago
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 75528@debbugs.gnu.org)
8734g54lp9.fsf@wolfsden.cz
Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (28 lines)
> Hi,
>
> Tomas Volf <~@wolfsden.cz> writes:
>
>> * gnu/packages/power.scm (apcupsd): New variable.
>> * gnu/local.mk (GNU_SYSTEM_MODULES): Register the new file.
>
> Edited this one lightly:
>
> modified gnu/packages/power.scm
> @@ -115,8 +115,9 @@ (define-public apcupsd
> (inputs (list libusb libusb-compat))
> (home-page "http://www.apcupsd.org")
> (synopsis "Daemon for controlling APC UPSes")
> - (description "The apcupsd can be used for power management and controlling
> -most of APC’s UPS models on Unix and Windows machines. The apcupsd works with
> -most of APC’s Smart-UPS models as well as most simple signalling models such a
> + (description "@command{apcupsd} can be used for power management and
> +controlling most of @acronym{APC, American Power Conversion}’s @acronym{UPS,
> +Uninterruptible Power Supply} models. @command{apcupsd} works with most of
> +APC’s Smart-UPS models as well as most simple signalling models such a
> Back-UPS, and BackUPS-Office.")
> (license license:gpl2)))
>
> We do not mention Windows or other nonfree OS support in package
> descriptions as Guix doesn't run natively there anyway and we'd rather
> not advertise them.

Makes sense.

Toggle quote (4 lines)
>
> And pushed. I'm still waiting for a v3 for the service (if you can
> implement the match-record suggestion) to push 2/2.

Will take a look (and hopefully send the patch) tomorrow, sorry for the
delays. Other things got in the way.

Tomas

--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
-----BEGIN PGP SIGNATURE-----

iQJCBAEBCgAsFiEEt4NJs4wUfTYpiGikL7/ufbZ/wakFAme6WiIOHH5Ad29sZnNk
ZW4uY3oACgkQL7/ufbZ/wakIGQ//QkDjf8YPm7gqfR04NXymV/KvVCJ2rvKC7z04
X6eIbwUyuyh5l92MJg4dHICHazE1HYiyX5e0ZJOmoonrWByKz8ViY/8qewyA0jJA
Zbcq+NQS3RH2q/Xb9puRgRUmdw4cSUcN09VV7vCOIPPGM0fj+0VlV3kWS51MCGzq
Lbo7ijQ7G8ndKEjfOrUkDKdh9ObGbWJ3hT8LcvAxV4haLf4dcm+0gG8/sRy3AySd
MTrICgkml6clBdKzS3I0nQT+XmQ49DDhxpv/CQSiNPs1SyaVx+ceMXkPiuxOwS3c
UuzLqmlS7dBvMqUhD1llzF6tJ/JL13/SzqNHc31OLDnJU1Xkuys3aqCRPk/PGZAq
uffqFZf/p5uHp8F6tOueE9Zo389YC0BesJRPr+jKe3OnKc0LXFne17rvO/vpf9yo
/5OCMsiMMeJfMoo2NBy2qVxmaxOOfTgibjMmldaxJELgjlpV4t+639dKqsan08lF
Ps+0m6I5Ch/MjqQ5d+h7PF7dSezCuO+KsHEutdihvSXS9INE7c55N/XIlRX32YEU
V7SdgzaBMh/aVkBudE5satNfuVfyy+Sb50YyPKj4CrqmfLNB3oS9l5L/UylhbQn9
Uk/s/Zq/a9TDVpsk50yIbaB+b33TGYVgORrKEtIS97zblCdiQLFueQ0esSl2n9h5
v2vqLUs=
=Qix9
-----END PGP SIGNATURE-----

Maxim Cournoyer wrote 3 weeks ago
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)
87ikp1qvb3.fsf@gmail.com
Hi Tomas,

Tomas Volf <~@wolfsden.cz> writes:

[...]

Toggle quote (3 lines)
> Will take a look (and hopefully send the patch) tomorrow, sorry for the
> delays. Other things got in the way.

No worries, juggling with all life things is tough!

--
Thanks,
Maxim
Tomas Volf wrote 3 weeks ago
[PATCH v3] services: Add power.
(address . 75528@debbugs.gnu.org)(name . Tomas Volf)(address . ~@wolfsden.cz)
376f456f8d49b825ddb1969734cc2aab4c99d780.1740345021.git.~@wolfsden.cz
* gnu/services/power.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (Power Management Services): Document service and data types.

Change-Id: If205d19bea1d20a99309626e28521a2d6fe6702f
---
doc/guix.texi | 382 ++++++++++++++++++++++-
gnu/local.mk | 1 +
gnu/services/power.scm | 672 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 1052 insertions(+), 3 deletions(-)
create mode 100644 gnu/services/power.scm

Toggle diff (533 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 83ba0f3292..a9eb6e4da3 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -123,7 +123,7 @@
Copyright @copyright{} 2023 Thomas Ieong@*
Copyright @copyright{} 2023 Saku Laesvuori@*
Copyright @copyright{} 2023 Graham James Addis@*
-Copyright @copyright{} 2023, 2024 Tomas Volf@*
+Copyright @copyright{} 2023-2025 Tomas Volf@*
Copyright @copyright{} 2024, 2025 Herman Rimm@*
Copyright @copyright{} 2024 Matthew Trzcinski@*
Copyright @copyright{} 2024 Richard Sent@*
@@ -422,7 +422,7 @@ Top
* Network File System:: NFS related services.
* Samba Services:: Samba services.
* Continuous Integration:: Cuirass and Laminar services.
-* Power Management Services:: Extending battery life.
+* Power Management Services:: Extending battery life, etc.
* Audio Services:: The MPD.
* Virtualization Services:: Virtualization services.
* Version Control Services:: Providing remote access to Git repositories.
@@ -19270,7 +19270,7 @@ Services
* Network File System:: NFS related services.
* Samba Services:: Samba services.
* Continuous Integration:: Cuirass and Laminar services.
-* Power Management Services:: Extending battery life.
+* Power Management Services:: Extending battery life, etc.
* Audio Services:: The MPD.
* Virtualization Services:: Virtualization services.
* Version Control Services:: Providing remote access to Git repositories.
@@ -36849,6 +36849,382 @@ Power Management Services
@end table
@end deftp
+The @code{(gnu services power)} module provides a service definition for
+@uref{http://www.apcupsd.org/, apcupsd}, a utility to interact with
+@acronym{APC, APC by Schneider Electric or formerly American Power
+Conversion Corporation} @acronym{UPS, Uninterruptible Power Supply}
+devices. Apcupsd also works with some @acronym{OEM, Original Equipment
+Manufacturer}-branded products manufactured by APC.
+
+@defvar apcupsd-service-type
+The service type for apcupsd. For USB UPSes no configuration is
+necessary, however tweaking some fields to better suit your needs might
+be desirable. The defaults are taken from the upstream configuration
+and they are not very conservative (for example, the default
+@code{battery-level} of 5% may be considered too low by some).
+
+The default event handlers do send emails, read more in
+@ref{apcupsd-event-handlers}.
+
+@lisp
+(service apcupsd-service-type)
+@end lisp
+@end defvar
+
+@deftp {Data Type} apcupsd-configuration
+
+Available @code{apcupsd-configuration} fields are:
+
+@table @asis
+@item @code{apcupsd} (default: @code{apcupsd}) (type: package)
+The @code{apcupsd} package to use.
+
+@item @code{shepherd-service-name} (default: @code{apcupsd}) (type: symbol)
+The name of the shepherd service. You can add the service multiple
+times with different names to manage multiple UPSes.
+
+@item @code{auto-start?} (default: @code{#t}) (type: boolean)
+Should the shepherd service auto-start?
+
+@item @code{pid-file} (default: @code{"/var/run/apcupsd.pid"}) (type: string)
+The file name of the pid file.
+
+@item @code{debug-level} (default: @code{0}) (type: integer)
+The logging verbosity. Bigger number means more logs. The source code
+uses up to @code{300} as debug level value, so a value of @code{999}
+seems reasonable to enable all the logs.
+
+@item @code{run-dir} (default: @code{"/var/run/apcupsd"}) (type: string)
+The directory containing runtime information. You need to change this
+if you desire to run multiple instances of the daemon.
+
+@item @code{name} (type: maybe-string)
+Use this to give your UPS a name in log files and such. This is
+particularly useful if you have multiple UPSes. This does not set the
+EEPROM. It should be 8 characters or less.
+
+@item @code{cable} (default: @code{usb}) (type: enum-cable)
+The type of a cable connecting the UPS to your computer. Possible
+generic choices are @code{'simple}, @code{'smart}, @code{'ether} and
+@code{'usb}.
+
+Alternatively, a specific cable model number may be used:
+@code{'940-0119A}, @code{'940-0127A}, @code{'940-0128A},
+@code{'940-0020B}, @code{'940-0020C}, @code{'940-0023A},
+@code{'940-0024B}, @code{'940-0024C}, @code{'940-1524C},
+@code{'940-0024G}, @code{'940-0095A}, @code{'940-0095B},
+@code{'940-0095C}, @code{'940-0625A}, @code{'M-04-02-2000}.
+
+@item @code{type} (default: @code{usb}) (type: enum-type)
+The type of the UPS you have.
+
+@table @code
+@item apcsmart
+Newer serial character device, appropriate for SmartUPS models using a
+serial cable (not an USB).
+
+@item usb
+Most new UPSes are an USB.
+
+@item net
+Network link to a master apcupsd through apcupsd's Network Information
+Server. This is used if the UPS powering your computer is connected to
+a different computer for monitoring.
+
+@item snmp
+SNMP network link to an SNMP-enabled UPS device.
+
+@item netsnmp
+Same as the SNMP above but requires use of the net-snmp library. Unless
+you have a specific need for this old driver, you should use the
+@code{'snmp} instead.
+
+@item dumb
+An old serial character device for use with simple-signaling UPSes.
+
+@item pcnet
+A PowerChute Network Shutdown protocol which can be used as an
+alternative to an SNMP with the AP9617 family of smart slot cards.
+
+@item modbus
+A serial device for use with newest SmartUPS models supporting the
+MODBUS protocol.
+
+@end table
+
+@item @code{device} (default: @code{""}) (type: string)
+For USB UPSes, usually you want to set this to an empty string (the
+default). For other UPS types, you must specify an appropriate port or
+address.
+
+@table @code
+@item apcsmart
+Set to the appropriate @file{/dev/tty**} device.
+
+@item usb
+A null string setting enables auto-detection, which is the best choice
+for most installations.
+
+@item net
+Set to @code{@var{hostname}:@var{port}}.
+
+@item snmp
+Set to @code{@var{hostname}:@var{port}:@var{vendor}:@var{community}}.
+The @var{hostname} is the ip address or hostname of the UPS on the
+network. The @var{vendor} can be can be "APC" or "APC_NOTRAP".
+"APC_NOTRAP" will disable SNMP trap catching; you usually want "APC".
+The @var{port} is usually 161. The @var{community} is usually
+"private".
+
+@item netsnmp
+Same as the @code{'snmp}.
+
+@item dumb
+Set to the appropriate @file{/dev/tty**} device.
+
+@item pcnet
+Set to @code{@var{ipaddr}:@var{username}:@var{passphrase}:@var{port}}.
+The @var{ipaddr} is the IP address of the UPS management card. The
+@var{username} and the @var{passphrase} are the credentials for which
+the card has been configured. The @var{port} is the port number on
+which to listen for messages from the UPS, normally 3052. If this
+parameter is empty or missing, the default of 3052 will be used.
+
+@item modbus
+Set to the appropriate @file{/dev/tty**} device. You can also leave it
+empty for MODBUS over USB or set to the serial number of the UPS.
+
+@end table
+
+@item @code{poll-time} (default: @code{60}) (type: integer)
+The interval (in seconds) at which apcupsd polls the UPS for status.
+This setting applies both to directly-attached UPSes (apcsmart, usb,
+dumb) and networked UPSes (net, snmp). Lowering this setting will
+improve the apcupsd's responsiveness to certain events at the cost of
+higher CPU utilization.
+
+@item @code{on-batery-delay} (default: @code{6}) (type: integer)
+The time in seconds from when a power failure is detected until we react
+to it with an onbattery event. The @code{'powerout} event will be
+triggered immediately when a power failure is detected. However, the
+@code{'onbattery} event will be trigger only after this delay.
+
+@item @code{battery-level} (default: @code{5}) (type: integer)
+If during a power failure, the remaining battery percentage (as reported
+by the UPS) is below or equal to this value, the apcupsd will initiate a
+system shutdown.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in a conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{remaining-minutes} (default: @code{3}) (type: integer)
+If during a power failure, the remaining runtime in minutes (as
+calculated internally by the UPS) is below or equal to this value,
+apcupsd will initiate a system shutdown.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in a conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{timeout} (default: @code{0}) (type: integer)
+If during a power failure, the UPS has run on batteries for this many
+seconds or longer, apcupsd will initiate a system shutdown. The value
+of 0 disables this timer.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in a conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{annoy-interval} (default: @code{300}) (type: integer)
+The time in seconds between annoying users (via the @code{'annoyme}
+event) to sign off prior to system shutdown. 0 disables.
+
+@item @code{annoy-delay} (default: @code{60}) (type: integer)
+The initial delay in seconds after a power failure before warning users
+to get off the system.
+
+@item @code{no-logon} (default: @code{disable}) (type: enum-no-logon)
+The condition which determines when users are prevented from logging in
+during a power failure.
+
+@item @code{kill-delay} (default: @code{0}) (type: integer)
+If this is non-zero, the apcupsd will continue running after a shutdown
+has been requested, and after the specified time in seconds attempt to
+kill the power. This is for use on systems where apcupsd cannot regain
+control after a shutdown.
+
+@item @code{net-server} (default: @code{#f}) (type: boolean)
+If enabled, a network information server process will be started.
+
+@item @code{net-server-ip} (default: @code{"127.0.0.1"}) (type: string)
+An IP address on which the NIS server will listen for incoming
+connections.
+
+@item @code{net-server-port} (default: @code{3551}) (type: integer)
+An IP port on which the NIS server will listen for incoming connections.
+
+@item @code{net-server-events-file} (type: maybe-string)
+If you want the last few EVENTS to be available over the network by the
+network information server, you must set this to a file name.
+
+@item @code{net-server-events-file-max-size} (default: @code{10}) (type: integer)
+The maximum size of the events file in kilobytes.
+
+@item @code{class} (default: @code{standalone}) (type: enum-class)
+Normally standalone unless you share an UPS using an APC ShareUPS card.
+
+@item @code{mode} (default: @code{disable}) (type: enum-mode)
+Normally disable unless you share an UPS using an APC ShareUPS card.
+
+@item @code{stat-time} (default: @code{0}) (type: integer)
+The time interval in seconds between writing the status file, 0
+disables.
+
+@item @code{log-stats} (default: @code{#f}) (type: boolean)
+Also write the stats as a logs. This generates a lot of output.
+
+@item @code{data-time} (default: @code{0}) (type: integer)
+The time interval in seconds between writing the data records to the log
+file, 0 disables.
+
+@item @code{facility} (type: maybe-string)
+The logging facility for the syslog.
+
+@item @code{event-handlers} (type: apcupsd-event-handlers)
+Handlers for events produced by apcupsd.
+
+@end table
+@end deftp
+
+@anchor{apcupsd-event-handlers}
+@deftp {Data Type} apcupsd-event-handlers
+
+For a description of the events please refer to @samp{man 8 apccontrol}.
+
+Each handler shall be a gexp. It is spliced into the control script for
+the daemon. In addition to the standard Guile programming environment,
+the following procedures and variables are also available:
+
+@table @code
+@item conf
+Variable containing the file name of the configuration file.
+
+@item powerfail-file
+Variable containing the file name of the powerfail file.
+
+@item cmd
+The event currently being handled.
+
+@item name
+The name of the UPS as specified in the configuration file.
+
+@item connected?
+Is @code{#t} if @command{apcupsd} is connected to the UPS via a serial
+port (or a USB port). In most configurations, this will be the case.
+In the case of a Slave machine where apcupsd is not directly connected
+to the UPS, this value will be @code{#f}.
+
+@item powered?
+Is @code{#t} if the computer on which @command{apcupsd} is running is
+powered by the UPS and @code{#f} if not. At the moment, this value is
+unimplemented and always @code{#f}.
+
+@item (err @var{fmt} @var{args...})
+Wrapper around @code{format} outputting to @code{(current-error-port)}.
+
+@item (wall @var{fmt} @var{args...})
+Wrapper around @code{format} outputting via @command{wall}.
+
+@item (apcupsd @var{args...})
+Call @command{apcupsd} while passing the correct configuration file and
+all the arguments.
+
+@item (mail-to-root @var{subject} @var{body})
+Send an email to the local administrator. This procedure assumes the
+@command{sendmail} is located at @command{/run/privileged/bin/sendmail}
+(as would be the case with @code{opensmtpd-service-type}).
+
+@end table
+
+Available @code{apcupsd-event-handlers} fields are:
+
+@table @asis
+@item @code{modules} (type: gexp)
+Additional modules to import into the generated handler script.
+
+@item @code{killpower} (type: gexp)
+The handler for the killpower event.
+
+@item @code{commfailure} (type: gexp)
+The handler for the commfailure event.
+
+@item @code{commok} (type: gexp)
+The handler for the commfailure event.
+
+@item @code{powerout} (type: gexp)
+The handler for the powerout event.
+
+@item @code{onbattery} (type: gexp)
+The handler for the onbattery event.
+
+@item @code{offbattery} (type: gexp)
+The handler for the offbattery event.
+
+@item @code{mainsback} (type: gexp)
+The handler for the mainsback event.
+
+@item @code{failing} (type: gexp)
+The handler for the failing event.
+
+@item @code{timeout} (type: gexp)
+The handler for the timeout event.
+
+@item @code{loadlimit} (type: gexp)
+The handler for the loadlimit event.
+
+@item @code{runlimit} (type: gexp)
+The handler for the runlimit event.
+
+@item @code{doreboot} (type: gexp)
+The handler for the doreboot event.
+
+@item @code{doshutdown} (type: gexp)
+The handler for the doshutdown event.
+
+@item @code{annoyme} (type: gexp)
+The handler for the annoyme event.
+
+@item @code{emergency} (type: gexp)
+The handler for the emergency event.
+
+@item @code{changeme} (type: gexp)
+The handler for the changeme event.
+
+@item @code{remotedown} (type: gexp)
+The handler for the remotedown event.
+
+@item @code{startselftest} (type: gexp)
+The handler for the startselftest event.
+
+@item @code{endselftest} (type: gexp)
+The handler for the endselftest event.
+
+@item @code{battdetach} (type: gexp)
+The handler for the battdetach event.
+
+@item @code{battattach} (type: gexp)
+The handler for the battattach event.
+
+@end table
+@end deftp
+
@node Audio Services
@subsection Audio Services
diff --git a/gnu/local.mk b/gnu/local.mk
index 257b66d1c2..bf82ec1c43 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -764,6 +764,7 @@ GNU_SYSTEM_MODULES = \
%D%/services/nix.scm \
%D%/services/nfs.scm \
%D%/services/pam-mount.scm \
+ %D%/services/power.scm \
%D%/services/science.scm \
%D%/services/security.scm \
%D%/services/security-token.scm \
diff --git a/gnu/services/power.scm b/gnu/services/power.scm
new file mode 100644
index 0000000000..7e30955f83
--- /dev/null
+++ b/gnu/services/power.scm
@@ -0,0 +1,672 @@
+;;; Copyright © 2025 Tomas Volf <~@wolfsden.cz>
+
+;;;; Commentary:
+
+;;; Power-related services.
+
+;;;; Code:
+
+(define-module (gnu services power)
+ #:use-module (gnu)
+ #:use-module (gnu packages admin)
+ #:use-module (gnu packages linux)
+ #:use-module (gnu packages power)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services shepherd)
+ #:use-module (gnu services)
+ #:use-module (guix packages)
+ #:use-module (guix records)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
+ #:export (apcupsd-service-type
+
+ apcupsd-configuration
+ apcupsd-configuration-apcupsd
+ apcupsd-configuration-shepherd-service-name
+ apcupsd-configuration-auto-start?
+ apcupsd-configuration-pid-file
+ apcupsd-configuration-debug-level
+ apcupsd-configuration-run-dir
+ apcupsd-configuration-name
+ apcupsd-configuration-cable
+ apcupsd-configuration-type
+ apcupsd-configuration-device
+ apcupsd-configuration-poll-time
+ apcupsd-configuration-on-batery-delay
+ apcupsd-configuration-battery-level
+ apcupsd-configuration-remaining-minutes
+ apcupsd-configuration-timeout
+ apcupsd-configuration-annoy-interval
+ apcupsd-configuration-annoy-delay
+ apcupsd-configuration-no-logon
+ apcupsd-configuration-kill-delay
+ apcupsd-configuration-net-server
+ apcupsd-configuration-net-server-ip
+ apcupsd-configuration-net-server-port
+ apcupsd-configuration-net-server-events-file
+ apcupsd-configuration-net-server-events-file-max-size
+ apcupsd-configuration-class
+ apcupsd-configuration-mode
+ apcupsd-configuration-stat-time
+ apcupsd-configuration-log-stats
+ apcupsd-configuration-data-time
+ apcupsd-configuration-facility
+ apcupsd-configuration-event-handlers
+
+ apcupsd-event-handlers
+ apcupsd-event-handlers-modules
+ apcupsd-event-handlers-annoyme
+ apcupsd-event-handlers-battattach
+ apcupsd-event-handlers-battdetach
+ apcupsd-event-handlers-changeme
+ apcupsd-event-handlers-commfailure
+ apcupsd-event-handlers-commok
+ apcupsd-event-handlers-doreboot
+ apcupsd-event-handlers-doshutdown
+ apcupsd-event-handlers-emergency
+ apcupsd-event-handlers-endselftest
+ apcupsd-event-handlers-failing
+ apcupsd-event-handlers-killpower
+ apcupsd-event-handlers-loadlimit
+ apcupsd-event-handlers-mainsback
+ apcupsd-event-handlers-offbattery
+ apcupsd-event-handlers-onbattery
+ apcupsd-event-handlers-powerout
+ apcupsd-event-handlers-remotedown
+ apcupsd-event-handlers-runlimit
+ apcupsd-event-handlers-startselftest
+ apcupsd-event-handlers-timeout))
+
+(define-configuration/no-serialization apcupsd-event-handlers
+ (modules
+ (gexp #~())
+ "Additional modules to import into the generated handler script.")
+ (killpower
+ (gexp
+ #~((wall "Apccontrol doing: apcupsd --killpower on UPS ~a" name)
+ (sleep 10)
+ (apcupsd "--killpower")
+ (wall "Apccontrol has done: apcupsd --killpower on UPS ~a" name)))
+ "The handler for the killpower event.")
+ (commfailure
+ (gexp
+ #~((let ((msg (format #f "~a Communications with UPS ~a lost."
+ (gethostname) name)))
+ (mail-to-root msg msg))
+ (wall "Warning: communications lost with UPS ~a" name)))
+ "The handler for the commfailure event.")
+ (commok
+ (gexp
+ #~
This message was truncated. Download the full message here.
Tomas Volf wrote 3 weeks ago
Re: [bug#75528] [PATCH 2/2] services: Add power.
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 75528@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
87jz9g2w80.fsf@wolfsden.cz
Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (210 lines)
> [..]

>>> I just thought about it, perhaps it could be more concise to extract the
>>> field values using match-record (these are bound to shorter names, since
>>> they omit the 'apcupsd-configuration-' prefix).
>>
>> If I got the idea right, you suggest converting
>>
>> ("emergency"
>> #$@(apcupsd-event-handlers-emergency
>> (apcupsd-configuration-event-handlers cfg)))
>>
>>
>> into
>>
>> ("emergency"
>> #$@(match-record cfg <apcupsd-configuration> (handlers)
>> (match-record handlers <apcupsd-event-handlers> (emergency)
>> emergency)))
>>
>>
>> I am not sure it is increase in readability, but maybe I misunderstood
>> the suggestion.
>
> I think something like this should work:
>
> modified gnu/services/power.scm
> @@ -494,119 +494,67 @@ (define-configuration apcupsd-configuration
> empty-serializer))
>
> (define (%apccontrol config)
> - (program-file
> - "apccontrol"
> - #~(begin
> - (use-modules (ice-9 format)
> - (ice-9 match)
> - (ice-9 popen)
> - (srfi srfi-9)
> - #$@(apcupsd-event-handlers-modules
> - (apcupsd-configuration-event-handlers config)))
> - ;; Script dir depends on these, and the configuration depends on the
> - ;; script dir. To sever the cyclic dependency, pass the file names via
> - ;; environment variables.
> - (define conf (getenv "GUIX_APCUPSD_CONF"))
> - (define powerfail-file (getenv "GUIX_APCUPSD_POWERFAIL_FILE"))
> -
> - (define (err . args)
> - (apply format (current-error-port) args))
> - (define (wall . args)
> - (system* #$(file-append util-linux "/bin/wall") (apply format #f args)))
> - (define (apcupsd . args)
> - (apply system* #$(file-append apcupsd "/sbin/apcupsd") "-f" conf args))
> - (define (mail-to-root subject body)
> - (let ((port (open-pipe* OPEN_WRITE
> - "/run/privileged/bin/sendmail"
> - "-F" "apcupsd"
> - "root")))
> - (format port "Subject: ~a~%~%~a~&" subject body)
> - (close-pipe port)))
> - (match (cdr (command-line))
> - (((? string? cmd) name connected powered)
> - (let ((connected? (match connected
> + (define event-handlers (apcupsd-configuration-event-handlers config))
> + (match-record event-handlers <apcupsd-event-handlers>
> + ( modules killpower commfailure commok powerout
> + onbattery offbattery ...)
> + (program-file
> + "apccontrol"
> + #~(begin
> + (use-modules (ice-9 format)
> + (ice-9 match)
> + (ice-9 popen)
> + (srfi srfi-9)
> + #$@modules)
> + ;; Script dir depends on these, and the configuration depends on the
> + ;; script dir. To sever the cyclic dependency, pass the file names via
> + ;; environment variables.
> + (define conf (getenv "GUIX_APCUPSD_CONF"))
> + (define powerfail-file (getenv "GUIX_APCUPSD_POWERFAIL_FILE"))
> +
> + (define (err . args)
> + (apply format (current-error-port) args))
> +
> + (define (wall . args)
> + (system* #$(file-append util-linux "/bin/wall") (apply format #f args)))
> +
> + (define (apcupsd . args)
> + (apply system* #$(file-append apcupsd "/sbin/apcupsd") "-f" conf args))
> +
> + (define (mail-to-root subject body)
> + (let ((port (open-pipe* OPEN_WRITE
> + "/run/privileged/bin/sendmail"
> + "-F" "apcupsd"
> + "root")))
> + (format port "Subject: ~a~%~%~a~&" subject body)
> + (close-pipe port)))
> +
> + (match (cdr (command-line))
> + (((? string? cmd) name connected powered)
> + (let ((connected? (match connected
> + ("1" #t)
> + ("0" #f)))
> + (powered? (match powered
> ("1" #t)
> - ("0" #f)))
> - (powered? (match powered
> - ("1" #t)
> - ("0" #f))))
> - (match cmd
> - ;; I am sure this could be done by macro, but meh. Last release
> - ;; of apcupsd was in 2016, so maintaining this will not be much
> - ;; work.
> - ("killpower"
> - #$@(apcupsd-event-handlers-killpower
> - (apcupsd-configuration-event-handlers config)))
> - ("commfailure"
> - #$@(apcupsd-event-handlers-commfailure
> - (apcupsd-configuration-event-handlers config)))
> - ("commok"
> - #$@(apcupsd-event-handlers-commok
> - (apcupsd-configuration-event-handlers config)))
> - ("powerout"
> - #$@(apcupsd-event-handlers-powerout
> - (apcupsd-configuration-event-handlers config)))
> - ("onbattery"
> - #$@(apcupsd-event-handlers-onbattery
> - (apcupsd-configuration-event-handlers config)))
> - ("offbattery"
> - #$@(apcupsd-event-handlers-offbattery
> - (apcupsd-configuration-event-handlers config)))
> - ("mainsback"
> - #$@(apcupsd-event-handlers-mainsback
> - (apcupsd-configuration-event-handlers config)))
> - ("failing"
> - #$@(apcupsd-event-handlers-failing
> - (apcupsd-configuration-event-handlers config)))
> - ("timeout"
> - #$@(apcupsd-event-handlers-timeout
> - (apcupsd-configuration-event-handlers config)))
> - ("loadlimit"
> - #$@(apcupsd-event-handlers-loadlimit
> - (apcupsd-configuration-event-handlers config)))
> - ("runlimit"
> - #$@(apcupsd-event-handlers-runlimit
> - (apcupsd-configuration-event-handlers config)))
> - ("doreboot"
> - #$@(apcupsd-event-handlers-doreboot
> - (apcupsd-configuration-event-handlers config)))
> - ("doshutdown"
> - #$@(apcupsd-event-handlers-doshutdown
> - (apcupsd-configuration-event-handlers config)))
> - ("annoyme"
> - #$@(apcupsd-event-handlers-annoyme
> - (apcupsd-configuration-event-handlers config)))
> - ("emergency"
> - #$@(apcupsd-event-handlers-emergency
> - (apcupsd-configuration-event-handlers config)))
> - ("changeme"
> - #$@(apcupsd-event-handlers-changeme
> - (apcupsd-configuration-event-handlers config)))
> - ("remotedown"
> - #$@(apcupsd-event-handlers-remotedown
> - (apcupsd-configuration-event-handlers config)))
> - ("startselftest"
> - #$@(apcupsd-event-handlers-startselftest
> - (apcupsd-configuration-event-handlers config)))
> - ("endselftest"
> - #$@(apcupsd-event-handlers-endselftest
> - (apcupsd-configuration-event-handlers config)))
> - ("battdetach"
> - #$@(apcupsd-event-handlers-battdetach
> - (apcupsd-configuration-event-handlers config)))
> - ("battattach"
> - #$@(apcupsd-event-handlers-battattach
> - (apcupsd-configuration-event-handlers config)))
> - (_
> - (err "Unknown event: ~a~%" cmd)
> - (err "Iff the event was emitted by apcupsd, this is a bug.~%")
> - (err "Please report to bug-guix@gnu.org.~%")
> - (exit #f)))))
> - (args
> - (err "Unknown arguments: ~a~%" args)
> - (err "Iff the arguments were passed by apcupsd, this is a bug.~%")
> - (err "Please report to bug-guix@gnu.org.~%")
> - (exit #f))))))
> + ("0" #f))))
> + (match cmd
> + ("killpower" #$killpower)
> + ("commfailure" #$commfailure)
> + ("commok" #$commok)
> + ("powerout" #$powerout)
> + ("onbattery" #$onbattery)
> + ("offbattery" #$offbattery)
> + [...]
> + (_
> + (err "Unknown event: ~a~%" cmd)
> + (err "If the event was emitted by apcupsd, this is a bug.~%")
> + (err "Please report to bug-guix@gnu.org.~%")
> + (exit #f)))))
> + (args
> + (err "Unknown arguments: ~a~%" args)
> + (err "If the arguments were passed by apcupsd, this is a bug.~%")
> + (err "Please report to bug-guix@gnu.org.~%")
> + (exit #f))))))))
>
> (define (apcupsd-script-dir config)
> (computed-file
>
> [...]

I get the idea now, thank you. Yes, that is *much* nicer. Updated.

Toggle quote (4 lines)
> [..]
> I'll let you work through my last match-record suggestion above, if you
> want and send a v3.

Incorporated and v3 sent. This time a single patch, since the package
was already merged.

Toggle quote (5 lines)
>
> I'm also attaching a fixup with the terminology changes I've submitted
> upstream if you'd like to validate it still works (I don't have any
> UPS to try it with myself).

Thank you, but I will pass on that. I respect your choice to invest
time into this effort, but for me this issue it not high enough on my
priority list to do the same.

Have a nice day,
Tomas

--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
-----BEGIN PGP SIGNATURE-----

iQJCBAEBCgAsFiEEt4NJs4wUfTYpiGikL7/ufbZ/wakFAme7kV8OHH5Ad29sZnNk
ZW4uY3oACgkQL7/ufbZ/walzCBAAkJIp59W6bm0O5lXkXNQXC5TDfeHGkxtfP6i/
/eIH6HOyln9+AmMltb2ppmRI3ZtGPBXVv6ix9mzDuFlq9AG/xRQF9u8pZaWUb2q0
kaSR1JWwSpVbKEEmZbM1/CkaKnLqL2/bYVT+eHRSXb3Gopoqlgx/+h+R9OFpe/mX
UywKOBAwGxGzdIMB/i0uyVutuG5rPsjnGfGnTBqCKggcYW33ELkwVZ/39PXaiBfc
dm2n6hTl/iXr+Tas3niQx3YQavqapAkDdFcffuCZQS6Sv6VBzHOAJxtF7XCevvVc
RT15Y+mk2v1Ajf5+gRHkXpOH7zfg/4ALFC8nTU9VSUxEQDLfbn0F/cnzSQLKc6A+
W1T65RHyE2voK1e9WpK4xa5QH3I7lrkGLIWULJgOZYITHt/QGooQ+8H4z1Z44gdx
vdq9CQbGjCxb6OZi/61i4bpT8Iv3v8slplVwqPyq5wVAzjMUdT3mjgwObEYrfgpx
iYPWBn3G69YkqW89rxr9ju6MwoHSp6qvOexjbbGdaGZbitKpfUhKvuw2QvcpQN2q
4oc4FQgmfK5Mjd7AkE3EDW6z0iqOlEQJOPqYVUhNfGDl6eBTWx2Tn48DJJtpwETM
F0B5cEjzRGosFtDDqZYTNbQiHpdiwIhH8mpBh7OnIAqUDeV370dZbcGTnmd3fx1K
CYAi8HE=
=hzbK
-----END PGP SIGNATURE-----

Maxim Cournoyer wrote 2 weeks ago
Re: [bug#75528] [PATCH v3] services: Add power.
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
87ecziizrn.fsf@gmail.com
Hi Tomas,

I was about to merge this, but I have one last (I promise!) question, see
below.

Tomas Volf <~@wolfsden.cz> writes:

[...]

Toggle quote (14 lines)
> +(define apcupsd-service-type
> + (service-type
> + (name 'apcupsd)
> + (description "Configure and optionally start the apcupsd.")
> + (extensions (list (service-extension activation-service-type
> + apcupsd-activation)
> + (service-extension shepherd-root-service-type
> + apcupsd-shepherd-services)
> + (service-extension pam-root-service-type
> + apcupsd-pam-extensions)))
> + (compose identity)
> + (extend (lambda (cfg lst)
> + (fold (cut <> <>) cfg lst)))

What does the above extend does? I find the (cut <> <>) particularly
cryptic, as it's typically used to specialize procedures, which there
are none above.

--
Thanks,
Maxim
Tomas Volf wrote 2 weeks ago
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 75528@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
87h64cnwl5.fsf@wolfsden.cz
Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (5 lines)
> Hi Tomas,
>
> I was about to merge this, but I have one last (I promise!) question, see
> below.

No problem, I appreciate the review, it lead to higher code quality of
this series.

Toggle quote (23 lines)
>
> Tomas Volf <~@wolfsden.cz> writes:
>
> [...]
>
>> +(define apcupsd-service-type
>> + (service-type
>> + (name 'apcupsd)
>> + (description "Configure and optionally start the apcupsd.")
>> + (extensions (list (service-extension activation-service-type
>> + apcupsd-activation)
>> + (service-extension shepherd-root-service-type
>> + apcupsd-shepherd-services)
>> + (service-extension pam-root-service-type
>> + apcupsd-pam-extensions)))
>> + (compose identity)
>> + (extend (lambda (cfg lst)
>> + (fold (cut <> <>) cfg lst)))
>
> What does the above extend does? I find the (cut <> <>) particularly
> cryptic, as it's typically used to specialize procedures, which there
> are none above.

I consider the behavior of the default #f value for extend field to be
unfortunate, so I prefer to specify extend for all my services. Yet in
this case there is no obvious thing to extend, so I opted for the
extension to be a procedure, taking in the configuration and returning,
possibly modified, configuration.

The (cut <> <>) is just a handy, more concise way to write
(lambda (proc arg) (proc arg)).

What are your thoughts here? Should I just drop both compose and extend
from here? Or is it fine like this?

Tomas

--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
-----BEGIN PGP SIGNATURE-----

iQJCBAEBCgAsFiEEt4NJs4wUfTYpiGikL7/ufbZ/wakFAmfDnEYOHH5Ad29sZnNk
ZW4uY3oACgkQL7/ufbZ/walz8w//e+ledml4Eug7ORzwjOdPY1RR949ZnenMowGU
psEEpxX7ikWjzPjJimnfdOoOYzfowwWEVg8ENn673ycWS3E5TKFzuPACD3ilmwew
aJgiCQztpHPRl7VqPtcR7lM0Tet6Qptcb71wJJQKpUusfYMY7ukPg4rEmoI3Au5s
9pMjyq6IdoZ7/yOYdjzipy2IiP2ra0fZsQ4ap6BTzIBnvx501GjHMJG1FcU6AKHB
X1ius2qp4cG6+sjLCtB2/9WcrJqCJfGrpMLPKAB2Ldv6ZFzU9aVwFH1lLGd6Fo+L
iI0D//lLgokug59y2BKJ7/DjQ3eMsDYY/GGE5jUJPY0ZjreUbPj+zuf5u1Mhiko8
c7Lbl+fX+VUN1zE+9iKnsH8459f+vktSmn1NS321Sh2uusTRCLDtvS30g0Pxz6WJ
64gITXDO1V/g1Ptu62VwjZRXM+KBvhsWWdgzXTF9Dn7baZE34KQlN/FF4YQYG757
KO9RYgRRB/TrUzhhYVW2xTO9gap/8az1M24HpHEK2IWrJnIPf8akHpzSOpmODSOO
sygVN8Nu/b+QEpNo4kfcG6CpnTV7LRgEfV6jR1wxgsNN2ukqkAWq+jU2Hf3Qp16W
a1N5pUgLANnNBJ721JAmW94QsIiVII7HP+Nqsub0o3X6gCNzJGmuIUwiZW9cZ85K
JM7RHTQ=
=Uatq
-----END PGP SIGNATURE-----

Maxim Cournoyer wrote 2 weeks ago
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
875xksgfz1.fsf@gmail.com
Hi Tomas,

Tomas Volf <~@wolfsden.cz> writes:

Toggle quote (10 lines)
> Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:
>
>> Hi Tomas,
>>
>> I was about to merge this, but I have one last (I promise!) question, see
>> below.
>
> No problem, I appreciate the review, it lead to higher code quality of
> this series.

:-)

Toggle quote (29 lines)
>>
>> Tomas Volf <~@wolfsden.cz> writes:
>>
>> [...]
>>
>>> +(define apcupsd-service-type
>>> + (service-type
>>> + (name 'apcupsd)
>>> + (description "Configure and optionally start the apcupsd.")
>>> + (extensions (list (service-extension activation-service-type
>>> + apcupsd-activation)
>>> + (service-extension shepherd-root-service-type
>>> + apcupsd-shepherd-services)
>>> + (service-extension pam-root-service-type
>>> + apcupsd-pam-extensions)))
>>> + (compose identity)
>>> + (extend (lambda (cfg lst)
>>> + (fold (cut <> <>) cfg lst)))
>>
>> What does the above extend does? I find the (cut <> <>) particularly
>> cryptic, as it's typically used to specialize procedures, which there
>> are none above.
>
> I consider the behavior of the default #f value for extend field to be
> unfortunate, so I prefer to specify extend for all my services. Yet in
> this case there is no obvious thing to extend, so I opted for the
> extension to be a procedure, taking in the configuration and returning,
> possibly modified, configuration.

I see. That's wasn't obvious to me. If there's nothing to be
composed/extended, I think leaving the compose and extend fields to
their default values communicate this better.

Toggle quote (3 lines)
> The (cut <> <>) is just a handy, more concise way to write
> (lambda (proc arg) (proc arg)).

I see. I think my confusion also stemmed from the fact I can never
fully remember how extend/compose work until I look it up again :-).

Toggle quote (3 lines)
> What are your thoughts here? Should I just drop both compose and extend
> from here? Or is it fine like this?

I'd just drop them. If we discover a need later, we can add them back
in, ideally with users (other services) as self-documenting code.

--
Thanks,
Maxim
Tomas Volf wrote 2 weeks ago
[PATCH v4] services: Add power.
(address . 75528@debbugs.gnu.org)(name . Tomas Volf)(address . ~@wolfsden.cz)
8430d41161be949bd8bbb869036bec8f3009e97d.1740923074.git.~@wolfsden.cz
* gnu/services/power.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (Power Management Services): Document service and data types.

Change-Id: If205d19bea1d20a99309626e28521a2d6fe6702f
---
doc/guix.texi | 382 ++++++++++++++++++++++-
gnu/local.mk | 1 +
gnu/services/power.scm | 669 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 1049 insertions(+), 3 deletions(-)
create mode 100644 gnu/services/power.scm

Toggle diff (533 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index a036c85c31..095e067ea1 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -123,7 +123,7 @@
Copyright @copyright{} 2023 Thomas Ieong@*
Copyright @copyright{} 2023 Saku Laesvuori@*
Copyright @copyright{} 2023 Graham James Addis@*
-Copyright @copyright{} 2023, 2024 Tomas Volf@*
+Copyright @copyright{} 2023-2025 Tomas Volf@*
Copyright @copyright{} 2024, 2025 Herman Rimm@*
Copyright @copyright{} 2024 Matthew Trzcinski@*
Copyright @copyright{} 2024 Richard Sent@*
@@ -422,7 +422,7 @@ Top
* Network File System:: NFS related services.
* Samba Services:: Samba services.
* Continuous Integration:: Cuirass and Laminar services.
-* Power Management Services:: Extending battery life.
+* Power Management Services:: Extending battery life, etc.
* Audio Services:: The MPD.
* Virtualization Services:: Virtualization services.
* Version Control Services:: Providing remote access to Git repositories.
@@ -19270,7 +19270,7 @@ Services
* Network File System:: NFS related services.
* Samba Services:: Samba services.
* Continuous Integration:: Cuirass and Laminar services.
-* Power Management Services:: Extending battery life.
+* Power Management Services:: Extending battery life, etc.
* Audio Services:: The MPD.
* Virtualization Services:: Virtualization services.
* Version Control Services:: Providing remote access to Git repositories.
@@ -36868,6 +36868,382 @@ Power Management Services
@end table
@end deftp
+The @code{(gnu services power)} module provides a service definition for
+@uref{http://www.apcupsd.org/, apcupsd}, a utility to interact with
+@acronym{APC, APC by Schneider Electric or formerly American Power
+Conversion Corporation} @acronym{UPS, Uninterruptible Power Supply}
+devices. Apcupsd also works with some @acronym{OEM, Original Equipment
+Manufacturer}-branded products manufactured by APC.
+
+@defvar apcupsd-service-type
+The service type for apcupsd. For USB UPSes no configuration is
+necessary, however tweaking some fields to better suit your needs might
+be desirable. The defaults are taken from the upstream configuration
+and they are not very conservative (for example, the default
+@code{battery-level} of 5% may be considered too low by some).
+
+The default event handlers do send emails, read more in
+@ref{apcupsd-event-handlers}.
+
+@lisp
+(service apcupsd-service-type)
+@end lisp
+@end defvar
+
+@deftp {Data Type} apcupsd-configuration
+
+Available @code{apcupsd-configuration} fields are:
+
+@table @asis
+@item @code{apcupsd} (default: @code{apcupsd}) (type: package)
+The @code{apcupsd} package to use.
+
+@item @code{shepherd-service-name} (default: @code{apcupsd}) (type: symbol)
+The name of the shepherd service. You can add the service multiple
+times with different names to manage multiple UPSes.
+
+@item @code{auto-start?} (default: @code{#t}) (type: boolean)
+Should the shepherd service auto-start?
+
+@item @code{pid-file} (default: @code{"/var/run/apcupsd.pid"}) (type: string)
+The file name of the pid file.
+
+@item @code{debug-level} (default: @code{0}) (type: integer)
+The logging verbosity. Bigger number means more logs. The source code
+uses up to @code{300} as debug level value, so a value of @code{999}
+seems reasonable to enable all the logs.
+
+@item @code{run-dir} (default: @code{"/var/run/apcupsd"}) (type: string)
+The directory containing runtime information. You need to change this
+if you desire to run multiple instances of the daemon.
+
+@item @code{name} (type: maybe-string)
+Use this to give your UPS a name in log files and such. This is
+particularly useful if you have multiple UPSes. This does not set the
+EEPROM. It should be 8 characters or less.
+
+@item @code{cable} (default: @code{usb}) (type: enum-cable)
+The type of a cable connecting the UPS to your computer. Possible
+generic choices are @code{'simple}, @code{'smart}, @code{'ether} and
+@code{'usb}.
+
+Alternatively, a specific cable model number may be used:
+@code{'940-0119A}, @code{'940-0127A}, @code{'940-0128A},
+@code{'940-0020B}, @code{'940-0020C}, @code{'940-0023A},
+@code{'940-0024B}, @code{'940-0024C}, @code{'940-1524C},
+@code{'940-0024G}, @code{'940-0095A}, @code{'940-0095B},
+@code{'940-0095C}, @code{'940-0625A}, @code{'M-04-02-2000}.
+
+@item @code{type} (default: @code{usb}) (type: enum-type)
+The type of the UPS you have.
+
+@table @code
+@item apcsmart
+Newer serial character device, appropriate for SmartUPS models using a
+serial cable (not an USB).
+
+@item usb
+Most new UPSes are an USB.
+
+@item net
+Network link to a master apcupsd through apcupsd's Network Information
+Server. This is used if the UPS powering your computer is connected to
+a different computer for monitoring.
+
+@item snmp
+SNMP network link to an SNMP-enabled UPS device.
+
+@item netsnmp
+Same as the SNMP above but requires use of the net-snmp library. Unless
+you have a specific need for this old driver, you should use the
+@code{'snmp} instead.
+
+@item dumb
+An old serial character device for use with simple-signaling UPSes.
+
+@item pcnet
+A PowerChute Network Shutdown protocol which can be used as an
+alternative to an SNMP with the AP9617 family of smart slot cards.
+
+@item modbus
+A serial device for use with newest SmartUPS models supporting the
+MODBUS protocol.
+
+@end table
+
+@item @code{device} (default: @code{""}) (type: string)
+For USB UPSes, usually you want to set this to an empty string (the
+default). For other UPS types, you must specify an appropriate port or
+address.
+
+@table @code
+@item apcsmart
+Set to the appropriate @file{/dev/tty**} device.
+
+@item usb
+A null string setting enables auto-detection, which is the best choice
+for most installations.
+
+@item net
+Set to @code{@var{hostname}:@var{port}}.
+
+@item snmp
+Set to @code{@var{hostname}:@var{port}:@var{vendor}:@var{community}}.
+The @var{hostname} is the ip address or hostname of the UPS on the
+network. The @var{vendor} can be can be "APC" or "APC_NOTRAP".
+"APC_NOTRAP" will disable SNMP trap catching; you usually want "APC".
+The @var{port} is usually 161. The @var{community} is usually
+"private".
+
+@item netsnmp
+Same as the @code{'snmp}.
+
+@item dumb
+Set to the appropriate @file{/dev/tty**} device.
+
+@item pcnet
+Set to @code{@var{ipaddr}:@var{username}:@var{passphrase}:@var{port}}.
+The @var{ipaddr} is the IP address of the UPS management card. The
+@var{username} and the @var{passphrase} are the credentials for which
+the card has been configured. The @var{port} is the port number on
+which to listen for messages from the UPS, normally 3052. If this
+parameter is empty or missing, the default of 3052 will be used.
+
+@item modbus
+Set to the appropriate @file{/dev/tty**} device. You can also leave it
+empty for MODBUS over USB or set to the serial number of the UPS.
+
+@end table
+
+@item @code{poll-time} (default: @code{60}) (type: integer)
+The interval (in seconds) at which apcupsd polls the UPS for status.
+This setting applies both to directly-attached UPSes (apcsmart, usb,
+dumb) and networked UPSes (net, snmp). Lowering this setting will
+improve the apcupsd's responsiveness to certain events at the cost of
+higher CPU utilization.
+
+@item @code{on-batery-delay} (default: @code{6}) (type: integer)
+The time in seconds from when a power failure is detected until we react
+to it with an onbattery event. The @code{'powerout} event will be
+triggered immediately when a power failure is detected. However, the
+@code{'onbattery} event will be trigger only after this delay.
+
+@item @code{battery-level} (default: @code{5}) (type: integer)
+If during a power failure, the remaining battery percentage (as reported
+by the UPS) is below or equal to this value, the apcupsd will initiate a
+system shutdown.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in a conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{remaining-minutes} (default: @code{3}) (type: integer)
+If during a power failure, the remaining runtime in minutes (as
+calculated internally by the UPS) is below or equal to this value,
+apcupsd will initiate a system shutdown.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in a conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{timeout} (default: @code{0}) (type: integer)
+If during a power failure, the UPS has run on batteries for this many
+seconds or longer, apcupsd will initiate a system shutdown. The value
+of 0 disables this timer.
+
+@quotation Note
+@code{battery-level}, @code{remaining-minutes}, and @code{timeout} work
+in a conjunction, so the first that occurs will cause the initation of a
+shutdown.
+@end quotation
+
+@item @code{annoy-interval} (default: @code{300}) (type: integer)
+The time in seconds between annoying users (via the @code{'annoyme}
+event) to sign off prior to system shutdown. 0 disables.
+
+@item @code{annoy-delay} (default: @code{60}) (type: integer)
+The initial delay in seconds after a power failure before warning users
+to get off the system.
+
+@item @code{no-logon} (default: @code{disable}) (type: enum-no-logon)
+The condition which determines when users are prevented from logging in
+during a power failure.
+
+@item @code{kill-delay} (default: @code{0}) (type: integer)
+If this is non-zero, the apcupsd will continue running after a shutdown
+has been requested, and after the specified time in seconds attempt to
+kill the power. This is for use on systems where apcupsd cannot regain
+control after a shutdown.
+
+@item @code{net-server} (default: @code{#f}) (type: boolean)
+If enabled, a network information server process will be started.
+
+@item @code{net-server-ip} (default: @code{"127.0.0.1"}) (type: string)
+An IP address on which the NIS server will listen for incoming
+connections.
+
+@item @code{net-server-port} (default: @code{3551}) (type: integer)
+An IP port on which the NIS server will listen for incoming connections.
+
+@item @code{net-server-events-file} (type: maybe-string)
+If you want the last few EVENTS to be available over the network by the
+network information server, you must set this to a file name.
+
+@item @code{net-server-events-file-max-size} (default: @code{10}) (type: integer)
+The maximum size of the events file in kilobytes.
+
+@item @code{class} (default: @code{standalone}) (type: enum-class)
+Normally standalone unless you share an UPS using an APC ShareUPS card.
+
+@item @code{mode} (default: @code{disable}) (type: enum-mode)
+Normally disable unless you share an UPS using an APC ShareUPS card.
+
+@item @code{stat-time} (default: @code{0}) (type: integer)
+The time interval in seconds between writing the status file, 0
+disables.
+
+@item @code{log-stats} (default: @code{#f}) (type: boolean)
+Also write the stats as a logs. This generates a lot of output.
+
+@item @code{data-time} (default: @code{0}) (type: integer)
+The time interval in seconds between writing the data records to the log
+file, 0 disables.
+
+@item @code{facility} (type: maybe-string)
+The logging facility for the syslog.
+
+@item @code{event-handlers} (type: apcupsd-event-handlers)
+Handlers for events produced by apcupsd.
+
+@end table
+@end deftp
+
+@anchor{apcupsd-event-handlers}
+@deftp {Data Type} apcupsd-event-handlers
+
+For a description of the events please refer to @samp{man 8 apccontrol}.
+
+Each handler shall be a gexp. It is spliced into the control script for
+the daemon. In addition to the standard Guile programming environment,
+the following procedures and variables are also available:
+
+@table @code
+@item conf
+Variable containing the file name of the configuration file.
+
+@item powerfail-file
+Variable containing the file name of the powerfail file.
+
+@item cmd
+The event currently being handled.
+
+@item name
+The name of the UPS as specified in the configuration file.
+
+@item connected?
+Is @code{#t} if @command{apcupsd} is connected to the UPS via a serial
+port (or a USB port). In most configurations, this will be the case.
+In the case of a Slave machine where apcupsd is not directly connected
+to the UPS, this value will be @code{#f}.
+
+@item powered?
+Is @code{#t} if the computer on which @command{apcupsd} is running is
+powered by the UPS and @code{#f} if not. At the moment, this value is
+unimplemented and always @code{#f}.
+
+@item (err @var{fmt} @var{args...})
+Wrapper around @code{format} outputting to @code{(current-error-port)}.
+
+@item (wall @var{fmt} @var{args...})
+Wrapper around @code{format} outputting via @command{wall}.
+
+@item (apcupsd @var{args...})
+Call @command{apcupsd} while passing the correct configuration file and
+all the arguments.
+
+@item (mail-to-root @var{subject} @var{body})
+Send an email to the local administrator. This procedure assumes the
+@command{sendmail} is located at @command{/run/privileged/bin/sendmail}
+(as would be the case with @code{opensmtpd-service-type}).
+
+@end table
+
+Available @code{apcupsd-event-handlers} fields are:
+
+@table @asis
+@item @code{modules} (type: gexp)
+Additional modules to import into the generated handler script.
+
+@item @code{killpower} (type: gexp)
+The handler for the killpower event.
+
+@item @code{commfailure} (type: gexp)
+The handler for the commfailure event.
+
+@item @code{commok} (type: gexp)
+The handler for the commfailure event.
+
+@item @code{powerout} (type: gexp)
+The handler for the powerout event.
+
+@item @code{onbattery} (type: gexp)
+The handler for the onbattery event.
+
+@item @code{offbattery} (type: gexp)
+The handler for the offbattery event.
+
+@item @code{mainsback} (type: gexp)
+The handler for the mainsback event.
+
+@item @code{failing} (type: gexp)
+The handler for the failing event.
+
+@item @code{timeout} (type: gexp)
+The handler for the timeout event.
+
+@item @code{loadlimit} (type: gexp)
+The handler for the loadlimit event.
+
+@item @code{runlimit} (type: gexp)
+The handler for the runlimit event.
+
+@item @code{doreboot} (type: gexp)
+The handler for the doreboot event.
+
+@item @code{doshutdown} (type: gexp)
+The handler for the doshutdown event.
+
+@item @code{annoyme} (type: gexp)
+The handler for the annoyme event.
+
+@item @code{emergency} (type: gexp)
+The handler for the emergency event.
+
+@item @code{changeme} (type: gexp)
+The handler for the changeme event.
+
+@item @code{remotedown} (type: gexp)
+The handler for the remotedown event.
+
+@item @code{startselftest} (type: gexp)
+The handler for the startselftest event.
+
+@item @code{endselftest} (type: gexp)
+The handler for the endselftest event.
+
+@item @code{battdetach} (type: gexp)
+The handler for the battdetach event.
+
+@item @code{battattach} (type: gexp)
+The handler for the battattach event.
+
+@end table
+@end deftp
+
@node Audio Services
@subsection Audio Services
diff --git a/gnu/local.mk b/gnu/local.mk
index c8a29bf98b..0048e09ede 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -764,6 +764,7 @@ GNU_SYSTEM_MODULES = \
%D%/services/nix.scm \
%D%/services/nfs.scm \
%D%/services/pam-mount.scm \
+ %D%/services/power.scm \
%D%/services/science.scm \
%D%/services/security.scm \
%D%/services/security-token.scm \
diff --git a/gnu/services/power.scm b/gnu/services/power.scm
new file mode 100644
index 0000000000..68a67e63e8
--- /dev/null
+++ b/gnu/services/power.scm
@@ -0,0 +1,669 @@
+;;; Copyright © 2025 Tomas Volf <~@wolfsden.cz>
+
+;;;; Commentary:
+
+;;; Power-related services.
+
+;;;; Code:
+
+(define-module (gnu services power)
+ #:use-module (gnu)
+ #:use-module (gnu packages admin)
+ #:use-module (gnu packages linux)
+ #:use-module (gnu packages power)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services shepherd)
+ #:use-module (gnu services)
+ #:use-module (guix packages)
+ #:use-module (guix records)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
+ #:export (apcupsd-service-type
+
+ apcupsd-configuration
+ apcupsd-configuration-apcupsd
+ apcupsd-configuration-shepherd-service-name
+ apcupsd-configuration-auto-start?
+ apcupsd-configuration-pid-file
+ apcupsd-configuration-debug-level
+ apcupsd-configuration-run-dir
+ apcupsd-configuration-name
+ apcupsd-configuration-cable
+ apcupsd-configuration-type
+ apcupsd-configuration-device
+ apcupsd-configuration-poll-time
+ apcupsd-configuration-on-batery-delay
+ apcupsd-configuration-battery-level
+ apcupsd-configuration-remaining-minutes
+ apcupsd-configuration-timeout
+ apcupsd-configuration-annoy-interval
+ apcupsd-configuration-annoy-delay
+ apcupsd-configuration-no-logon
+ apcupsd-configuration-kill-delay
+ apcupsd-configuration-net-server
+ apcupsd-configuration-net-server-ip
+ apcupsd-configuration-net-server-port
+ apcupsd-configuration-net-server-events-file
+ apcupsd-configuration-net-server-events-file-max-size
+ apcupsd-configuration-class
+ apcupsd-configuration-mode
+ apcupsd-configuration-stat-time
+ apcupsd-configuration-log-stats
+ apcupsd-configuration-data-time
+ apcupsd-configuration-facility
+ apcupsd-configuration-event-handlers
+
+ apcupsd-event-handlers
+ apcupsd-event-handlers-modules
+ apcupsd-event-handlers-annoyme
+ apcupsd-event-handlers-battattach
+ apcupsd-event-handlers-battdetach
+ apcupsd-event-handlers-changeme
+ apcupsd-event-handlers-commfailure
+ apcupsd-event-handlers-commok
+ apcupsd-event-handlers-doreboot
+ apcupsd-event-handlers-doshutdown
+ apcupsd-event-handlers-emergency
+ apcupsd-event-handlers-endselftest
+ apcupsd-event-handlers-failing
+ apcupsd-event-handlers-killpower
+ apcupsd-event-handlers-loadlimit
+ apcupsd-event-handlers-mainsback
+ apcupsd-event-handlers-offbattery
+ apcupsd-event-handlers-onbattery
+ apcupsd-event-handlers-powerout
+ apcupsd-event-handlers-remotedown
+ apcupsd-event-handlers-runlimit
+ apcupsd-event-handlers-startselftest
+ apcupsd-event-handlers-timeout))
+
+(define-configuration/no-serialization apcupsd-event-handlers
+ (modules
+ (gexp #~())
+ "Additional modules to import into the generated handler script.")
+ (killpower
+ (gexp
+ #~((wall "Apccontrol doing: apcupsd --killpower on UPS ~a" name)
+ (sleep 10)
+ (apcupsd "--killpower")
+ (wall "Apccontrol has done: apcupsd --killpower on UPS ~a" name)))
+ "The handler for the killpower event.")
+ (commfailure
+ (gexp
+ #~((let ((msg (format #f "~a Communications with UPS ~a lost."
+ (gethostname) name)))
+ (mail-to-root msg msg))
+ (wall "Warning: communications lost with UPS ~a" name)))
+ "The handler for the commfailure event.")
+ (commok
+ (gexp
+ #~
This message was truncated. Download the full message here.
Tomas Volf wrote 2 weeks ago
Re: [bug#75528] [PATCH v3] services: Add power.
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 75528@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
87cyezo877.fsf@wolfsden.cz
Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (8 lines)
> [..]
>
>> What are your thoughts here? Should I just drop both compose and extend
>> from here? Or is it fine like this?
>
> I'd just drop them. If we discover a need later, we can add them back
> in, ideally with users (other services) as self-documenting code.

Makes sense, I have sent v4.

Have a nice day,
Tomas

--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
-----BEGIN PGP SIGNATURE-----

iQJCBAEBCgAsFiEEt4NJs4wUfTYpiGikL7/ufbZ/wakFAmfEYXwOHH5Ad29sZnNk
ZW4uY3oACgkQL7/ufbZ/walEOg//ThhL2tNdAOoQnnHNoT6VM4/sbVOvOg3+o//B
1nd8JyYXxp1r8bGKb+jOqHyKY6MnOpWKbtazGqLnNM6+UwxI3BrrjB59z1GcxbJ7
RvkOS81CQjOrFlNBwUSReVDA/QJA1jyvwQsnF2pY6Mc2CNo9zC+g08IWPY1Qz5i/
x1l/Vyemq11rpbV7ceE+Du4Hk2xH70UP09Hn7ws/MQJ2SCbTWn6dxRw6qvnTJ+fh
IWNzdeJCPfZvZ+sEfO1VO4Mr8jKVPlzKhQf5M0K2XcitDwyi7ObDS8UBfOcCDHRl
6PP4nZ+Ck0E6nfX4izoXOgqdK/lyTAbWl+VDLBcsiUQQoO+xDh0rHDk9jw/lRx3C
L/K/qkoKWb5lIT0V50XRhIEU5TaSkYUZgijvCGygLwe+PAHV+wJENMz0R59Ufatx
L/hPW8RBzrBaTBbGsZE2bHNvLpPDb04cTb+u7FRL2VoF0XQxyhJF2Do02QFa6ad6
bQdCLm9Pjsb5xHyNcNvJzuklSXAgWwzjkx3v8GQV3LzcsVJIryRFHqp67KSlW4ME
LrCHS/G8dwpuapiW9k+5NmsHINu1D0SlqZvU/swo8lIYupYSmW1Eipjqp8m6pda7
g9TqnPt8jg2LRPNFj8t8NIeeuxZl5YxDLj+wX4L8rUCX8nQlX39fi2tkXm0jx1pS
vQv01m4=
=3ovo
-----END PGP SIGNATURE-----

Maxim Cournoyer wrote 2 weeks ago
(name . Tomas Volf)(address . ~@wolfsden.cz)(address . 75528-done@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
87mse3fnaf.fsf@gmail.com
Hi,

Tomas Volf <~@wolfsden.cz> writes:

[...]

Toggle quote (3 lines)
> What are your thoughts here? Should I just drop both compose and extend
> from here? Or is it fine like this?

This was the last small change, so I've adjusted on my side and pushed
as commit d0e46a0003.

Thank you for your patience!

--
Maxim
Closed
Tomas Volf wrote 2 weeks ago
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 75528@debbugs.gnu.org)
87h64bmkzl.fsf@wolfsden.cz
Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (14 lines)
> Hi,
>
> Tomas Volf <~@wolfsden.cz> writes:
>
> [...]
>
>> What are your thoughts here? Should I just drop both compose and extend
>> from here? Or is it fine like this?
>
> This was the last small change, so I've adjusted on my side and pushed
> as commit d0e46a0003.
>
> Thank you for your patience!

And thank you for working with me through this. :)

--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
-----BEGIN PGP SIGNATURE-----

iQJCBAEBCgAsFiEEt4NJs4wUfTYpiGikL7/ufbZ/wakFAmfEjT4OHH5Ad29sZnNk
ZW4uY3oACgkQL7/ufbZ/wam7exAAp6U0kwERjWjl0C/+ahS6mI74tYTvjVtkKhy0
E8lIDbFsQYbmf+60BA3KQuHmuMcDMC64FP3ASF6sqz7ZbT8vImPWxmRkJClz552K
yfKTM1CZjFCeLzJAADZdDugPZMFcRPA9/Bl8B/lvnG/FXgdfbozAr1Ta1uxlfeIL
s6yJzlGrOd/hrRTM1HRoaNzuz3oebo3Qoq2c19i35znseZwy7Fegs9Uv9TtI4Zpf
lTf4Ncq8Ac2p0CR7fxLRmKtkHYFCgZTxK1NkhexDY9/58dD7S8EFmdgf3rzqYsRV
aqvTXTIZ1ifv2AkWsLwv6vBXocEmiSKOSPEzqDEULyRjtPcMwoNfL4Ixf4s9nYZN
FBSLmZi/5lrHPrGvyG7e23YsBmgJ8XJQySBEiUNDsOUgpkDXVPOT38RGdQrLTm+R
iMQYQNJTVtsGMPB3ECX6Rq+UskMI4yu0XrsZchiTfbBuIC5ok59NR+S71OYXG0lp
XUmX1VXAMEjYS2mhTgz44NmFxojbE6O4ON3/H2EmA0dyFD5niUUoYjKyqh8eV+Jy
Qf7D0Pg/YSMIkjFQjkgnQ85KQFaZs7NsF3qANTx/ngWX+rgJb0E2z0FuBh0qiY00
LIWLycDf2yysNg79KnYmANGx0zXdjPf3lOoKjDqmffv56QcxBnLV10g3DBzw5Yd+
rvj1t2I=
=JYSo
-----END PGP SIGNATURE-----

?
Your comment

Commenting via the web interface is currently disabled.

To comment on this conversation send an email to 75528@debbugs.gnu.org

To respond to this issue using the mumi CLI, first switch to it
mumi current 75528
Then, you may apply the latest patchset in this issue (with sign off)
mumi am -- -s
Or, compose a reply to this issue
mumi compose
Or, send patches to this issue
mumi send-email *.patch
You may also tag this issue. See list of standard tags. For example, to set the confirmed and easy tags
mumi command -t +confirmed -t +easy
Or, remove the moreinfo tag and set the help tag
mumi command -t -moreinfo -t +help