forked from mirrors/nixpkgs
Merge pull request #9280 from oxij/better-smartd
nixos: make services.smartd much more helpful
This commit is contained in:
commit
7ca6742acc
|
@ -98,6 +98,9 @@ in zipModules ([]
|
||||||
++ obsolete [ "boot" "initrd" "extraKernelModules" ] [ "boot" "initrd" "kernelModules" ]
|
++ obsolete [ "boot" "initrd" "extraKernelModules" ] [ "boot" "initrd" "kernelModules" ]
|
||||||
++ obsolete [ "boot" "extraKernelParams" ] [ "boot" "kernelParams" ]
|
++ obsolete [ "boot" "extraKernelParams" ] [ "boot" "kernelParams" ]
|
||||||
|
|
||||||
|
# smartd
|
||||||
|
++ obsolete [ "services" "smartd" "deviceOpts" ] [ "services" "smartd" "defaults" "monitored" ]
|
||||||
|
|
||||||
# OpenSSH
|
# OpenSSH
|
||||||
++ obsolete [ "services" "sshd" "ports" ] [ "services" "openssh" "ports" ]
|
++ obsolete [ "services" "sshd" "ports" ] [ "services" "openssh" "ports" ]
|
||||||
++ alias [ "services" "sshd" "enable" ] [ "services" "openssh" "enable" ]
|
++ alias [ "services" "sshd" "enable" ] [ "services" "openssh" "enable" ]
|
||||||
|
|
|
@ -4,8 +4,66 @@ with lib;
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
|
host = config.networking.hostName or "unknown"
|
||||||
|
+ optionalString (config.networking.domain != null) ".${config.networking.domain}";
|
||||||
|
|
||||||
cfg = config.services.smartd;
|
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, ... }: {
|
smartdOpts = { name, ... }: {
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
@ -22,34 +80,11 @@ let
|
||||||
type = types.separatedString " ";
|
type = types.separatedString " ";
|
||||||
description = "Options that determine how smartd monitors the device.";
|
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
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -59,26 +94,104 @@ in
|
||||||
|
|
||||||
services.smartd = {
|
services.smartd = {
|
||||||
|
|
||||||
enable = mkOption {
|
enable = mkEnableOption "smartd daemon from <literal>smartmontools</literal> package";
|
||||||
default = false;
|
|
||||||
|
autodetect = mkOption {
|
||||||
|
default = true;
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
example = true;
|
|
||||||
description = ''
|
description = ''
|
||||||
Run smartd from the smartmontools package. Note that e-mail
|
Whenever smartd should monitor all devices connected to the
|
||||||
notifications will not be enabled unless you configure the list of
|
machine at the time it's being started (the default).
|
||||||
devices with <varname>services.smartd.devices</varname> as well.
|
|
||||||
|
Set to false to monitor the devices listed in
|
||||||
|
<option>services.smartd.devices</option> only.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
deviceOpts = mkOption {
|
notifications = {
|
||||||
default = "";
|
|
||||||
type = types.string;
|
mail = {
|
||||||
example = "-o on -s (S/../.././02|L/../../7/04)";
|
enable = mkOption {
|
||||||
description = ''
|
default = config.services.mail.sendmailSetuidWrapper != null;
|
||||||
Additional options for each device that is monitored. The example
|
type = types.bool;
|
||||||
turns on SMART Automatic Offline Testing on startup, and schedules short
|
description = "Whenever to send e-mail notifications.";
|
||||||
self-tests daily, and long self-tests weekly.
|
};
|
||||||
'';
|
|
||||||
|
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 {
|
devices = mkOption {
|
||||||
|
@ -86,14 +199,9 @@ in
|
||||||
example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ];
|
example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ];
|
||||||
type = types.listOf types.optionSet;
|
type = types.listOf types.optionSet;
|
||||||
options = [ smartdOpts ];
|
options = [ smartdOpts ];
|
||||||
description = ''
|
description = "List of devices to monitor.";
|
||||||
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.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -103,12 +211,19 @@ in
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
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 = {
|
systemd.services.smartd = {
|
||||||
description = "S.M.A.R.T. Daemon";
|
description = "S.M.A.R.T. Daemon";
|
||||||
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
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}";
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue