From 3dd5d8606a9f7217c5f880be5bf0eed3a84c51d4 Mon Sep 17 00:00:00 2001
From: Philip Wilk
Date: Mon, 31 Mar 2025 12:48:05 +0100
Subject: [PATCH] nixos/hddfancontrol: use attrset for config
Enables use of multiple instances, eg; multiple drive bays.
---
.../manual/release-notes/rl-2505.section.md | 2 +-
.../services/hardware/hddfancontrol.nix | 194 +++++++++++++-----
2 files changed, 144 insertions(+), 52 deletions(-)
diff --git a/nixos/doc/manual/release-notes/rl-2505.section.md b/nixos/doc/manual/release-notes/rl-2505.section.md
index 31b57c914b91..a106d2f1444c 100644
--- a/nixos/doc/manual/release-notes/rl-2505.section.md
+++ b/nixos/doc/manual/release-notes/rl-2505.section.md
@@ -618,7 +618,7 @@
- All services that require a root certificate bundle now use the value of a new read-only option, `security.pki.caBundle`.
-- hddfancontrol has been updated to major release 2. See the [migration guide](https://github.com/desbma/hddfancontrol/tree/master?tab=readme-ov-file#migrating-from-v1x), as there are breaking changes.
+- hddfancontrol has been updated to major release 2. See the [migration guide](https://github.com/desbma/hddfancontrol/tree/master?tab=readme-ov-file#migrating-from-v1x), as there are breaking changes. The settings options have been modified to use an attrset, enabling configurations with multiple instances of the daemon running at once, eg, for two separate drive bays.
- `services.cloudflared` now uses a dynamic user, and its `user` and `group` options have been removed. If the user or group is still necessary, they can be created manually.
diff --git a/nixos/modules/services/hardware/hddfancontrol.nix b/nixos/modules/services/hardware/hddfancontrol.nix
index 942425d63f7e..a1e36012893d 100644
--- a/nixos/modules/services/hardware/hddfancontrol.nix
+++ b/nixos/modules/services/hardware/hddfancontrol.nix
@@ -18,64 +18,162 @@ in
"hddfancontrol"
"smartctl"
] "Smartctl is now automatically used when necessary, which makes this option redundant")
+ (lib.mkRemovedOptionModule [
+ "services"
+ "hddfancontrol"
+ "disks"
+ ] "Disks should now be specified per hddfancontrol instance in its attrset")
+ (lib.mkRemovedOptionModule [
+ "services"
+ "hddfancontrol"
+ "pwmPaths"
+ ] "Pwm Paths should now be specified per hddfancontrol instance in its attrset")
+ (lib.mkRemovedOptionModule [
+ "services"
+ "hddfancontrol"
+ "logVerbosity"
+ ] "Log Verbosity should now be specified per hddfancontrol instance in its attrset")
+ (lib.mkRemovedOptionModule [
+ "services"
+ "hddfancontrol"
+ "extraArgs"
+ ] "Extra Args should now be specified per hddfancontrol instance in its attrset")
];
options = {
services.hddfancontrol.enable = lib.mkEnableOption "hddfancontrol daemon";
- services.hddfancontrol.disks = lib.mkOption {
- type = lib.types.listOf lib.types.path;
- default = [ ];
- description = ''
- Drive(s) to get temperature from
- '';
- example = [ "/dev/sda" ];
- };
+ services.hddfancontrol.settings = lib.mkOption {
+ type = lib.types.attrsWith {
+ placeholder = "drive-bay-name";
+ elemType = (
+ lib.types.submodule (
+ { ... }:
+ {
+ options = {
+ disks = lib.mkOption {
+ type = lib.types.listOf lib.types.path;
+ default = [ ];
+ description = ''
+ Drive(s) to get temperature from
+ '';
+ example = [ "/dev/sda" ];
+ };
- services.hddfancontrol.pwmPaths = lib.mkOption {
- type = lib.types.listOf lib.types.path;
- default = [ ];
- description = ''
- PWM filepath(s) to control fan speed (under /sys), followed by initial and fan-stop PWM values
- '';
- example = [ "/sys/class/hwmon/hwmon2/pwm1:30:10" ];
- };
+ pwmPaths = lib.mkOption {
+ type = lib.types.listOf lib.types.path;
+ default = [ ];
+ description = ''
+ PWM filepath(s) to control fan speed (under /sys), followed by initial and fan-stop PWM values
+ '';
+ example = [ "/sys/class/hwmon/hwmon2/pwm1:30:10" ];
+ };
- services.hddfancontrol.logVerbosity = lib.mkOption {
- type = lib.types.enum [
- "TRACE"
- "DEBUG"
- "INFO"
- "WARN"
- "ERROR"
- ];
- default = "INFO";
- description = ''
- Verbosity of the log level
- '';
- };
+ logVerbosity = lib.mkOption {
+ type = lib.types.enum [
+ "TRACE"
+ "DEBUG"
+ "INFO"
+ "WARN"
+ "ERROR"
+ ];
+ default = "INFO";
+ description = ''
+ Verbosity of the log level
+ '';
+ };
- services.hddfancontrol.extraArgs = lib.mkOption {
- type = lib.types.listOf lib.types.str;
- default = [ ];
+ extraArgs = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ description = ''
+ Extra commandline arguments for hddfancontrol
+ '';
+ example = [
+ "--min-fan-speed-prct=10"
+ "--interval=1min"
+ ];
+ };
+ };
+ }
+ )
+ );
+ };
+ default = { };
description = ''
- Extra commandline arguments for hddfancontrol
+ Parameter-sets for each instance of hddfancontrol.
+ '';
+ example = lib.literalExpression ''
+ {
+ harddrives = {
+ disks = [
+ "/dev/sda"
+ "/dev/sdb"
+ "/dev/sdc"
+ ];
+ pwmPaths = [
+ "/sys/class/hwmon/hwmon1/pwm1:25:10"
+ ];
+ logVerbosity = "DEBUG";
+ };
+ ssddrives = {
+ disks = [
+ "/dev/sdd"
+ "/dev/sde"
+ "/dev/sdf"
+ ];
+ pwmPaths = [
+ "/sys/class/hwmon/hwmon1/pwm2:25:10"
+ ];
+ extraArgs = [
+ "--interval=30s"
+ ];
+ };
+ }
'';
- example = [
- "--min-fan-speed-prct=10"
- "--interval=1min"
- ];
};
};
config = lib.mkIf cfg.enable (
let
- args = lib.concatLists [
- [ "-d" ]
- cfg.disks
- [ "-p" ]
- cfg.pwmPaths
- cfg.extraArgs
+ args =
+ cnf:
+ lib.concatLists [
+ [ "-d" ]
+ cnf.disks
+ [ "-p" ]
+ cnf.pwmPaths
+ cnf.extraArgs
+ ];
+
+ createService = cnf: {
+ description = "HDD fan control";
+ documentation = [ "man:hddfancontrol(1)" ];
+ after = [ "hddtemp.service" ];
+ wants = [ "hddtemp.service" ];
+ serviceConfig = {
+ ExecStart = "${lib.getExe pkgs.hddfancontrol} -v ${cnf.logVerbosity} daemon ${lib.escapeShellArgs (args cnf)}";
+
+ CPUSchedulingPolicy = "rr";
+ CPUSchedulingPriority = 49;
+
+ ProtectSystem = "strict";
+ PrivateTmp = true;
+ ProtectHome = true;
+ SystemCallArchitectures = "native";
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ };
+ wantedBy = [ "multi-user.target" ];
+ };
+
+ services = lib.attrsets.mergeAttrsList [
+ (lib.attrsets.mapAttrs' (
+ name: cnf: lib.nameValuePair "hddfancontrol-${name}" (createService cnf)
+ ) cfg.settings)
+ {
+ "hddfancontrol".enable = false;
+ }
];
in
{
@@ -83,16 +181,10 @@ in
hardware.sensor.hddtemp = {
enable = true;
- drives = cfg.disks;
+ drives = lib.lists.flatten (lib.attrsets.catAttrs "disks" (lib.attrsets.attrValues cfg.settings));
};
- systemd.services.hddfancontrol = {
- wantedBy = [ "multi-user.target" ];
- environment = {
- HDDFANCONTROL_LOG_LEVEL = cfg.logVerbosity;
- HDDFANCONTROL_DAEMON_ARGS = lib.escapeShellArgs args;
- };
- };
+ systemd.services = services;
}
);
}