diff options
-rw-r--r-- | nixos/doc/manual/from_md/release-notes/rl-2305.section.xml | 7 | ||||
-rw-r--r-- | nixos/doc/manual/release-notes/rl-2305.section.md | 2 | ||||
-rw-r--r-- | nixos/modules/module-list.nix | 1 | ||||
-rw-r--r-- | nixos/modules/services/misc/autosuspend.nix | 230 |
4 files changed, 240 insertions, 0 deletions
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml index 17769ff0b45bc..8e9a81f3499a3 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml @@ -130,6 +130,13 @@ <link xlink:href="options.html#opt-services.photoprism.enable">services.photoprism</link>. </para> </listitem> + <listitem> + <para> + <link xlink:href="https://github.com/languitar/autosuspend">autosuspend</link>, + a python daemon that suspends a system if certain conditions + are met, or not met. + </para> + </listitem> </itemizedlist> </section> <section xml:id="sec-release-23.05-incompatibilities"> diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md index d4b31a9df1b8e..845f9037e7222 100644 --- a/nixos/doc/manual/release-notes/rl-2305.section.md +++ b/nixos/doc/manual/release-notes/rl-2305.section.md @@ -42,6 +42,8 @@ In addition to numerous new and upgraded packages, this release has the followin - [photoprism](https://photoprism.app/), a AI-Powered Photos App for the Decentralized Web. Available as [services.photoprism](options.html#opt-services.photoprism.enable). +- [autosuspend](https://github.com/languitar/autosuspend), a python daemon that suspends a system if certain conditions are met, or not met. + ## Backward Incompatibilities {#sec-release-23.05-incompatibilities} <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 45a7acdedc410..e1c174a00f99e 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -571,6 +571,7 @@ ./services/misc/atuin.nix ./services/misc/autofs.nix ./services/misc/autorandr.nix + ./services/misc/autosuspend.nix ./services/misc/bazarr.nix ./services/misc/beanstalkd.nix ./services/misc/bees.nix diff --git a/nixos/modules/services/misc/autosuspend.nix b/nixos/modules/services/misc/autosuspend.nix new file mode 100644 index 0000000000000..b3e362533a093 --- /dev/null +++ b/nixos/modules/services/misc/autosuspend.nix @@ -0,0 +1,230 @@ +{ config, pkgs, lib, ... }: +let + inherit (lib) mapAttrs' nameValuePair filterAttrs types mkEnableOption + mdDoc mkPackageOptionMD mkOption literalExpression mkIf flatten + maintainers attrValues; + + cfg = config.services.autosuspend; + + settingsFormat = pkgs.formats.ini { }; + + checks = + mapAttrs' + (n: v: nameValuePair "check.${n}" (filterAttrs (_: v: v != null) v)) + cfg.checks; + wakeups = + mapAttrs' + (n: v: nameValuePair "wakeup.${n}" (filterAttrs (_: v: v != null) v)) + cfg.wakeups; + + # Whether the given check is enabled + hasCheck = class: + (filterAttrs + (n: v: v.enabled && (if v.class == null then n else v.class) == class) + cfg.checks) + != { }; + + # Dependencies needed by specific checks + dependenciesForChecks = { + "Smb" = pkgs.samba; + "XIdleTime" = [ pkgs.xprintidle pkgs.sudo ]; + }; + + autosuspend-conf = + settingsFormat.generate "autosuspend.conf" ({ general = cfg.settings; } // checks // wakeups); + + autosuspend = cfg.package; + + checkType = types.submodule { + freeformType = settingsFormat.type.nestedTypes.elemType; + + options.enabled = mkEnableOption (mdDoc "this activity check") // { default = true; }; + + options.class = mkOption { + default = null; + type = with types; nullOr (enum [ + "ActiveCalendarEvent" + "ActiveConnection" + "ExternalCommand" + "JsonPath" + "Kodi" + "KodiIdleTime" + "LastLogActivity" + "Load" + "LogindSessionsIdle" + "Mpd" + "NetworkBandwidth" + "Ping" + "Processes" + "Smb" + "Users" + "XIdleTime" + "XPath" + ]); + description = mdDoc '' + Name of the class implementing the check. If this option is not specified, the check's + name must represent a valid internal check class. + ''; + }; + }; + + wakeupType = types.submodule { + freeformType = settingsFormat.type.nestedTypes.elemType; + + options.enabled = mkEnableOption (mdDoc "this wake-up check") // { default = true; }; + + options.class = mkOption { + default = null; + type = with types; nullOr (enum [ + "Calendar" + "Command" + "File" + "Periodic" + "SystemdTimer" + "XPath" + "XPathDelta" + ]); + description = mdDoc '' + Name of the class implementing the check. If this option is not specified, the check's + name must represent a valid internal check class. + ''; + }; + }; +in +{ + options = { + services.autosuspend = { + enable = mkEnableOption (mdDoc "the autosuspend daemon"); + + package = mkPackageOptionMD pkgs "autosuspend" { }; + + settings = mkOption { + type = types.submodule { + freeformType = settingsFormat.type.nestedTypes.elemType; + + options = { + # Provide reasonable defaults for these two (required) options + suspend_cmd = mkOption { + default = "systemctl suspend"; + type = with types; str; + description = mdDoc '' + The command to execute in case the host shall be suspended. This line can contain + additional command line arguments to the command to execute. + ''; + }; + wakeup_cmd = mkOption { + default = ''sh -c 'echo 0 > /sys/class/rtc/rtc0/wakealarm && echo {timestamp:.0f} > /sys/class/rtc/rtc0/wakealarm' ''; + type = with types; str; + description = mdDoc '' + The command to execute for scheduling a wake up of the system. The given string is + processed using Python’s `str.format()` and a format argument called `timestamp` + encodes the UTC timestamp of the planned wake up time (float). Additionally `iso` + can be used to acquire the timestamp in ISO 8601 format. + ''; + }; + }; + }; + default = { }; + example = literalExpression '' + { + enable = true; + interval = 30; + idle_time = 120; + } + ''; + description = mdDoc '' + Configuration for autosuspend, see + <https://autosuspend.readthedocs.io/en/latest/configuration_file.html#general-configuration> + for supported values. + ''; + }; + + checks = mkOption { + default = { }; + type = with types; attrsOf checkType; + description = mdDoc '' + Checks for activity. For more information, see: + - <https://autosuspend.readthedocs.io/en/latest/configuration_file.html#activity-check-configuration> + - <https://autosuspend.readthedocs.io/en/latest/available_checks.html> + ''; + example = literalExpression '' + { + # Basic activity check configuration. + # The check class name is derived from the section header (Ping in this case). + # Remember to enable desired checks. They are disabled by default. + Ping = { + hosts = "192.168.0.7"; + }; + + # This check is disabled. + Smb.enabled = false; + + # Example for a custom check name. + # This will use the Users check with the custom name RemoteUsers. + # Custom names are necessary in case a check class is used multiple times. + # Custom names can also be used for clarification. + RemoteUsers = { + class = "Users"; + name = ".*"; + terminal = ".*"; + host = "[0-9].*"; + }; + + # Here the Users activity check is used again with different settings and a different name + LocalUsers = { + class = "Users"; + name = ".*"; + terminal = ".*"; + host = "localhost"; + }; + } + ''; + }; + + wakeups = mkOption { + default = { }; + type = with types; attrsOf wakeupType; + description = mdDoc '' + Checks for wake up. For more information, see: + - <https://autosuspend.readthedocs.io/en/latest/configuration_file.html#wake-up-check-configuration> + - <https://autosuspend.readthedocs.io/en/latest/available_wakeups.html> + ''; + example = literalExpression '' + { + # Wake up checks reuse the same configuration mechanism as activity checks. + Calendar = { + url = "http://example.org/test.ics"; + }; + } + ''; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.autosuspend = { + description = "A daemon to suspend your server in case of inactivity"; + documentation = [ "https://autosuspend.readthedocs.io/en/latest/systemd_integration.html" ]; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + path = flatten (attrValues (filterAttrs (n: _: hasCheck n) dependenciesForChecks)); + serviceConfig = { + ExecStart = ''${autosuspend}/bin/autosuspend -l ${autosuspend}/etc/autosuspend-logging.conf -c ${autosuspend-conf} daemon''; + }; + }; + + systemd.services.autosuspend-detect-suspend = { + description = "Notifies autosuspend about suspension"; + documentation = [ "https://autosuspend.readthedocs.io/en/latest/systemd_integration.html" ]; + wantedBy = [ "sleep.target" ]; + after = [ "sleep.target" ]; + serviceConfig = { + ExecStart = ''${autosuspend}/bin/autosuspend -l ${autosuspend}/etc/autosuspend-logging.conf -c ${autosuspend-conf} presuspend''; + }; + }; + }; + + meta = { + maintainers = with maintainers; [ xlambein ]; + }; +} |