summary refs log tree commit diff
path: root/nixos/modules/services/monitoring/smartd.nix
diff options
context:
space:
mode:
authorJan Malakhovski <oxij@oxij.org>2015-08-16 17:03:48 +0000
committerJan Malakhovski <oxij@oxij.org>2015-08-16 22:36:13 +0000
commit2a9dbf36b3404ffc4d3ec3147e177c924b7b112a (patch)
treebb6bbaf8738da4deb5988a55505a74e005627c96 /nixos/modules/services/monitoring/smartd.nix
parent1d35d31a2fe2c48918fd42f65a21fe0464b93a3e (diff)
downloadnixpkgs-2a9dbf36b3404ffc4d3ec3147e177c924b7b112a.tar
nixpkgs-2a9dbf36b3404ffc4d3ec3147e177c924b7b112a.tar.gz
nixpkgs-2a9dbf36b3404ffc4d3ec3147e177c924b7b112a.tar.bz2
nixpkgs-2a9dbf36b3404ffc4d3ec3147e177c924b7b112a.tar.lz
nixpkgs-2a9dbf36b3404ffc4d3ec3147e177c924b7b112a.tar.xz
nixpkgs-2a9dbf36b3404ffc4d3ec3147e177c924b7b112a.tar.zst
nixpkgs-2a9dbf36b3404ffc4d3ec3147e177c924b7b112a.zip
nixos: make services.smartd much more helpful
Now it generates notifications for auto-detected devices as well as
for explicitly configured ones, sends well formed e-mails and supports
immediate `wall` and `xmessage` notifications.
Diffstat (limited to 'nixos/modules/services/monitoring/smartd.nix')
-rw-r--r--nixos/modules/services/monitoring/smartd.nix211
1 files changed, 163 insertions, 48 deletions
diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix
index 803bd9e9a65..61ba1612325 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} <root>
+      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 <nomailer> -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 <root>"
-      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 <literal>smartmontools</literal> 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 <varname>services.smartd.devices</varname> 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
+          <option>services.smartd.devices</option> 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
+              <option>services.postfix</option> 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
+            <option>services.smartd.devices</option>) devices.
+
+            The default value turns on monitoring of all the things (see
+            <literal>man 5 smartd.conf</literal>).
+
+            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 <option>services.smartd.defaults.monitored</option>, 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}";
     };
 
   };