diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index abecc065634a..4db08b7ad3d0 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -98,6 +98,9 @@ in zipModules ([] ++ obsolete [ "boot" "initrd" "extraKernelModules" ] [ "boot" "initrd" "kernelModules" ] ++ obsolete [ "boot" "extraKernelParams" ] [ "boot" "kernelParams" ] +# smartd +++ obsolete [ "services" "smartd" "deviceOpts" ] [ "services" "smartd" "defaults" "monitored" ] + # OpenSSH ++ obsolete [ "services" "sshd" "ports" ] [ "services" "openssh" "ports" ] ++ alias [ "services" "sshd" "enable" ] [ "services" "openssh" "enable" ] diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix index 803bd9e9a65a..61ba16123252 100644 --- a/nixos/modules/services/monitoring/smartd.nix +++ b/nixos/modules/services/monitoring/smartd.nix @@ -4,8 +4,66 @@ with lib; let + host = config.networking.hostName or "unknown" + + optionalString (config.networking.domain != null) ".${config.networking.domain}"; + cfg = config.services.smartd; + nm = cfg.notifications.mail; + nw = cfg.notifications.wall; + nx = cfg.notifications.x11; + + smartdNotify = pkgs.writeScript "smartd-notify.sh" '' + #! ${pkgs.stdenv.shell} + ${optionalString nm.enable '' + { + cat << EOF + From: smartd on ${host} + To: undisclosed-recipients:; + Subject: SMART error on $SMARTD_DEVICESTRING: $SMARTD_FAILTYPE + + $SMARTD_FULLMESSAGE + EOF + + ${pkgs.smartmontools}/sbin/smartctl -a -d "$SMARTD_DEVICETYPE" "$SMARTD_DEVICE" + } | ${nm.mailer} -i "${nm.recipient}" + ''} + ${optionalString nw.enable '' + { + cat << EOF + Problem detected with disk: $SMARTD_DEVICESTRING + Warning message from smartd is: + + $SMARTD_MESSAGE + EOF + } | ${pkgs.utillinux}/bin/wall 2>/dev/null + ''} + ${optionalString nx.enable '' + export DISPLAY=${nx.display} + { + 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}"} + ''; + smartdOpts = { name, ... }: { options = { @@ -22,34 +80,11 @@ let type = types.separatedString " "; description = "Options that determine how smartd monitors the device."; }; + }; }; - smartdMail = pkgs.writeScript "smartdmail.sh" '' - #! ${pkgs.stdenv.shell} - TMPNAM=/tmp/smartd-message.$$.tmp - if test -n "$SMARTD_ADDRESS"; then - echo >"$TMPNAM" "From: smartd " - echo >>"$TMPNAM" 'To: undisclosed-recipients:;' - echo >>"$TMPNAM" "Subject: $SMARTD_SUBJECT" - echo >>"$TMPNAM" - echo >>"$TMPNAM" "Failure on $SMARTD_DEVICESTRING: $SMARTD_FAILTYPE" - echo >>"$TMPNAM" - cat >>"$TMPNAM" - ${pkgs.smartmontools}/sbin/smartctl >>"$TMPNAM" -a -d "$SMARTD_DEVICETYPE" "$SMARTD_DEVICE" - /var/setuid-wrappers/sendmail <"$TMPNAM" -f "$SENDER" -i "$SMARTD_ADDRESS" - fi - ''; - - smartdConf = pkgs.writeText "smartd.conf" (concatMapStrings (device: - '' - ${device.device} -a -m root -M exec ${smartdMail} ${device.options} ${cfg.deviceOpts} - '' - ) cfg.devices); - - smartdFlags = if (cfg.devices == []) then "" else "--configfile=${smartdConf}"; - in { @@ -59,26 +94,104 @@ in services.smartd = { - enable = mkOption { - default = false; + enable = mkEnableOption "smartd daemon from smartmontools package"; + + autodetect = mkOption { + default = true; type = types.bool; - example = true; description = '' - Run smartd from the smartmontools package. Note that e-mail - notifications will not be enabled unless you configure the list of - devices with services.smartd.devices as well. + 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 + only. ''; }; - deviceOpts = mkOption { - default = ""; - type = types.string; - example = "-o on -s (S/../.././02|L/../../7/04)"; - description = '' - Additional options for each device that is monitored. The example - turns on SMART Automatic Offline Testing on startup, and schedules short - self-tests daily, and long self-tests weekly. - ''; + notifications = { + + mail = { + enable = mkOption { + default = config.services.mail.sendmailSetuidWrapper != null; + type = types.bool; + description = "Whenever to send e-mail notifications."; + }; + + recipient = mkOption { + default = "root"; + type = types.string; + description = "Recipient of the notification messages."; + }; + + mailer = mkOption { + default = "/var/setuid-wrappers/sendmail"; + type = types.path; + description = '' + Sendmail-compatible binary to be used to send the messages. + + You should probably enable + or some other MTA for + this to work. + ''; + }; + }; + + wall = { + enable = mkOption { + default = true; + type = types.bool; + description = "Whenever to send wall notifications to all users."; + }; + }; + + x11 = { + enable = mkOption { + default = config.services.xserver.enable; + type = types.bool; + description = "Whenever to send X11 xmessage notifications."; + }; + + display = mkOption { + default = ":${toString config.services.xserver.display}"; + type = types.string; + description = "DISPLAY to send X11 notifications to."; + }; + }; + + test = mkOption { + default = false; + type = types.bool; + description = "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 = '' + Common default options for explicitly monitored (listed in + ) 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; + type = types.separatedString " "; + description = '' + Like , but for the + autodetected devices. + ''; + }; }; devices = mkOption { @@ -86,14 +199,9 @@ in example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ]; type = types.listOf types.optionSet; options = [ smartdOpts ]; - description = '' - List of devices to monitor. By default -- if this list is empty --, - smartd will monitor all devices connected to the machine at the time - it's being run. Configuring this option has the added benefit of - enabling e-mail notifications to "root" every time smartd detects an - error. - ''; - }; + description = "List of devices to monitor."; + }; + }; }; @@ -103,12 +211,19 @@ in 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 --no-fork ${smartdFlags}"; + path = [ pkgs.nettools ]; # for hostname and dnsdomanname calls in smartd + + serviceConfig.ExecStart = "${pkgs.smartmontools}/sbin/smartd --no-fork --configfile=${smartdConf}"; }; };