{ config, lib, options, pkgs, ... }: with lib; let host = config.networking.hostName or "unknown" + optionalString (config.networking.domain != null) ".${config.networking.domain}"; cfg = config.services.smartd; opt = options.services.smartd; nm = cfg.notifications.mail; nw = cfg.notifications.wall; nx = cfg.notifications.x11; smartdNotify = pkgs.writeScript "smartd-notify.sh" '' #! ${pkgs.runtimeShell} ${optionalString nm.enable '' { ${pkgs.coreutils}/bin/cat << EOF From: smartd on ${host} <${nm.sender}> To: undisclosed-recipients:; Subject: $SMARTD_SUBJECT $SMARTD_FULLMESSAGE EOF ${pkgs.smartmontools}/sbin/smartctl -a -d "$SMARTD_DEVICETYPE" "$SMARTD_DEVICE" } | ${nm.mailer} -i "${nm.recipient}" ''} ${optionalString nw.enable '' { ${pkgs.coreutils}/bin/cat << EOF Problem detected with disk: $SMARTD_DEVICESTRING Warning message from smartd is: $SMARTD_MESSAGE EOF } | ${pkgs.util-linux}/bin/wall 2>/dev/null ''} ${optionalString nx.enable '' export DISPLAY=${nx.display} { ${pkgs.coreutils}/bin/cat << EOF Problem detected with disk: $SMARTD_DEVICESTRING Warning message from smartd is: $SMARTD_FULLMESSAGE EOF } | ${pkgs.xorg.xmessage}/bin/xmessage -file - 2>/dev/null & ''} ''; notifyOpts = optionalString (nm.enable || nw.enable || nx.enable) ("-m -M exec ${smartdNotify} " + optionalString cfg.notifications.test "-M test "); smartdConf = pkgs.writeText "smartd.conf" '' # Autogenerated smartd startup config file DEFAULT ${notifyOpts}${cfg.defaults.monitored} ${concatMapStringsSep "\n" (d: "${d.device} ${d.options}") cfg.devices} ${optionalString cfg.autodetect "DEVICESCAN ${notifyOpts}${cfg.defaults.autodetected}"} ''; smartdDeviceOpts = { ... }: { options = { device = mkOption { example = "/dev/sda"; type = types.str; description = lib.mdDoc "Location of the device."; }; options = mkOption { default = ""; example = "-d sat"; type = types.separatedString " "; description = lib.mdDoc "Options that determine how smartd monitors the device."; }; }; }; in { ###### interface options = { services.smartd = { enable = mkEnableOption "smartd daemon from smartmontools package"; autodetect = mkOption { default = true; type = types.bool; description = lib.mdDoc '' Whenever smartd should monitor all devices connected to the machine at the time it's being started (the default). Set to false to monitor the devices listed in {option}`services.smartd.devices` only. ''; }; extraOptions = mkOption { default = []; type = types.listOf types.str; example = ["-A /var/log/smartd/" "--interval=3600"]; description = lib.mdDoc '' Extra command-line options passed to the `smartd` daemon on startup. (See `man 8 smartd`.) ''; }; notifications = { mail = { enable = mkOption { default = config.services.mail.sendmailSetuidWrapper != null; defaultText = literalExpression "config.services.mail.sendmailSetuidWrapper != null"; type = types.bool; description = lib.mdDoc "Whenever to send e-mail notifications."; }; sender = mkOption { default = "root"; example = "example@domain.tld"; type = types.str; description = '' Sender of the notification messages. Acts as the value of email in the emails' From: ... field. ''; }; recipient = mkOption { default = "root"; type = types.str; description = lib.mdDoc "Recipient of the notification messages."; }; mailer = mkOption { default = "/run/wrappers/bin/sendmail"; type = types.path; description = lib.mdDoc '' Sendmail-compatible binary to be used to send the messages. You should probably enable {option}`services.postfix` or some other MTA for this to work. ''; }; }; wall = { enable = mkOption { default = true; type = types.bool; description = lib.mdDoc "Whenever to send wall notifications to all users."; }; }; x11 = { enable = mkOption { default = config.services.xserver.enable; defaultText = literalExpression "config.services.xserver.enable"; type = types.bool; description = lib.mdDoc "Whenever to send X11 xmessage notifications."; }; display = mkOption { default = ":${toString config.services.xserver.display}"; defaultText = literalExpression ''":''${toString config.services.xserver.display}"''; type = types.str; description = lib.mdDoc "DISPLAY to send X11 notifications to."; }; }; test = mkOption { default = false; type = types.bool; description = lib.mdDoc "Whenever to send a test notification on startup."; }; }; defaults = { monitored = mkOption { default = "-a"; type = types.separatedString " "; example = "-a -o on -s (S/../.././02|L/../../7/04)"; description = lib.mdDoc '' Common default options for explicitly monitored (listed in {option}`services.smartd.devices`) devices. The default value turns on monitoring of all the things (see `man 5 smartd.conf`). The example also turns on SMART Automatic Offline Testing on startup, and schedules short self-tests daily, and long self-tests weekly. ''; }; autodetected = mkOption { default = cfg.defaults.monitored; defaultText = literalExpression "config.${opt.defaults.monitored}"; type = types.separatedString " "; description = lib.mdDoc '' Like {option}`services.smartd.defaults.monitored`, but for the autodetected devices. ''; }; }; devices = mkOption { default = []; example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ]; type = with types; listOf (submodule smartdDeviceOpts); description = lib.mdDoc "List of devices to monitor."; }; }; }; ###### implementation config = mkIf cfg.enable { assertions = [ { assertion = cfg.autodetect || cfg.devices != []; message = "smartd can't run with both disabled autodetect and an empty list of devices to monitor."; } ]; systemd.services.smartd = { description = "S.M.A.R.T. Daemon"; wantedBy = [ "multi-user.target" ]; serviceConfig.ExecStart = "${pkgs.smartmontools}/sbin/smartd ${lib.concatStringsSep " " cfg.extraOptions} --no-fork --configfile=${smartdConf}"; }; }; }