at 23.11-pre 7.3 kB view raw
1{ config, lib, options, pkgs, ... }: 2 3with lib; 4 5let 6 7 host = config.networking.fqdnOrHostName; 8 9 cfg = config.services.smartd; 10 opt = options.services.smartd; 11 12 nm = cfg.notifications.mail; 13 nw = cfg.notifications.wall; 14 nx = cfg.notifications.x11; 15 16 smartdNotify = pkgs.writeScript "smartd-notify.sh" '' 17 #! ${pkgs.runtimeShell} 18 ${optionalString nm.enable '' 19 { 20 ${pkgs.coreutils}/bin/cat << EOF 21 From: smartd on ${host} <${nm.sender}> 22 To: undisclosed-recipients:; 23 Subject: $SMARTD_SUBJECT 24 25 $SMARTD_FULLMESSAGE 26 EOF 27 28 ${pkgs.smartmontools}/sbin/smartctl -a -d "$SMARTD_DEVICETYPE" "$SMARTD_DEVICE" 29 } | ${nm.mailer} -i "${nm.recipient}" 30 ''} 31 ${optionalString nw.enable '' 32 { 33 ${pkgs.coreutils}/bin/cat << EOF 34 Problem detected with disk: $SMARTD_DEVICESTRING 35 Warning message from smartd is: 36 37 $SMARTD_MESSAGE 38 EOF 39 } | ${pkgs.util-linux}/bin/wall 2>/dev/null 40 ''} 41 ${optionalString nx.enable '' 42 export DISPLAY=${nx.display} 43 { 44 ${pkgs.coreutils}/bin/cat << EOF 45 Problem detected with disk: $SMARTD_DEVICESTRING 46 Warning message from smartd is: 47 48 $SMARTD_FULLMESSAGE 49 EOF 50 } | ${pkgs.xorg.xmessage}/bin/xmessage -file - 2>/dev/null & 51 ''} 52 ''; 53 54 notifyOpts = optionalString (nm.enable || nw.enable || nx.enable) 55 ("-m <nomailer> -M exec ${smartdNotify} " + optionalString cfg.notifications.test "-M test "); 56 57 smartdConf = pkgs.writeText "smartd.conf" '' 58 # Autogenerated smartd startup config file 59 DEFAULT ${notifyOpts}${cfg.defaults.monitored} 60 61 ${concatMapStringsSep "\n" (d: "${d.device} ${d.options}") cfg.devices} 62 63 ${optionalString cfg.autodetect 64 "DEVICESCAN ${notifyOpts}${cfg.defaults.autodetected}"} 65 ''; 66 67 smartdDeviceOpts = { ... }: { 68 69 options = { 70 71 device = mkOption { 72 example = "/dev/sda"; 73 type = types.str; 74 description = lib.mdDoc "Location of the device."; 75 }; 76 77 options = mkOption { 78 default = ""; 79 example = "-d sat"; 80 type = types.separatedString " "; 81 description = lib.mdDoc "Options that determine how smartd monitors the device."; 82 }; 83 84 }; 85 86 }; 87 88in 89 90{ 91 ###### interface 92 93 options = { 94 95 services.smartd = { 96 97 enable = mkEnableOption (lib.mdDoc "smartd daemon from `smartmontools` package"); 98 99 autodetect = mkOption { 100 default = true; 101 type = types.bool; 102 description = lib.mdDoc '' 103 Whenever smartd should monitor all devices connected to the 104 machine at the time it's being started (the default). 105 106 Set to false to monitor the devices listed in 107 {option}`services.smartd.devices` only. 108 ''; 109 }; 110 111 extraOptions = mkOption { 112 default = []; 113 type = types.listOf types.str; 114 example = ["-A /var/log/smartd/" "--interval=3600"]; 115 description = lib.mdDoc '' 116 Extra command-line options passed to the `smartd` 117 daemon on startup. 118 119 (See `man 8 smartd`.) 120 ''; 121 }; 122 123 notifications = { 124 125 mail = { 126 enable = mkOption { 127 default = config.services.mail.sendmailSetuidWrapper != null; 128 defaultText = literalExpression "config.services.mail.sendmailSetuidWrapper != null"; 129 type = types.bool; 130 description = lib.mdDoc "Whenever to send e-mail notifications."; 131 }; 132 133 sender = mkOption { 134 default = "root"; 135 example = "example@domain.tld"; 136 type = types.str; 137 description = lib.mdDoc '' 138 Sender of the notification messages. 139 Acts as the value of `email` in the emails' `From: ...` field. 140 ''; 141 }; 142 143 recipient = mkOption { 144 default = "root"; 145 type = types.str; 146 description = lib.mdDoc "Recipient of the notification messages."; 147 }; 148 149 mailer = mkOption { 150 default = "/run/wrappers/bin/sendmail"; 151 type = types.path; 152 description = lib.mdDoc '' 153 Sendmail-compatible binary to be used to send the messages. 154 155 You should probably enable 156 {option}`services.postfix` or some other MTA for 157 this to work. 158 ''; 159 }; 160 }; 161 162 wall = { 163 enable = mkOption { 164 default = true; 165 type = types.bool; 166 description = lib.mdDoc "Whenever to send wall notifications to all users."; 167 }; 168 }; 169 170 x11 = { 171 enable = mkOption { 172 default = config.services.xserver.enable; 173 defaultText = literalExpression "config.services.xserver.enable"; 174 type = types.bool; 175 description = lib.mdDoc "Whenever to send X11 xmessage notifications."; 176 }; 177 178 display = mkOption { 179 default = ":${toString config.services.xserver.display}"; 180 defaultText = literalExpression ''":''${toString config.services.xserver.display}"''; 181 type = types.str; 182 description = lib.mdDoc "DISPLAY to send X11 notifications to."; 183 }; 184 }; 185 186 test = mkOption { 187 default = false; 188 type = types.bool; 189 description = lib.mdDoc "Whenever to send a test notification on startup."; 190 }; 191 192 }; 193 194 defaults = { 195 monitored = mkOption { 196 default = "-a"; 197 type = types.separatedString " "; 198 example = "-a -o on -s (S/../.././02|L/../../7/04)"; 199 description = lib.mdDoc '' 200 Common default options for explicitly monitored (listed in 201 {option}`services.smartd.devices`) devices. 202 203 The default value turns on monitoring of all the things (see 204 `man 5 smartd.conf`). 205 206 The example also turns on SMART Automatic Offline Testing on 207 startup, and schedules short self-tests daily, and long 208 self-tests weekly. 209 ''; 210 }; 211 212 autodetected = mkOption { 213 default = cfg.defaults.monitored; 214 defaultText = literalExpression "config.${opt.defaults.monitored}"; 215 type = types.separatedString " "; 216 description = lib.mdDoc '' 217 Like {option}`services.smartd.defaults.monitored`, but for the 218 autodetected devices. 219 ''; 220 }; 221 }; 222 223 devices = mkOption { 224 default = []; 225 example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ]; 226 type = with types; listOf (submodule smartdDeviceOpts); 227 description = lib.mdDoc "List of devices to monitor."; 228 }; 229 230 }; 231 232 }; 233 234 235 ###### implementation 236 237 config = mkIf cfg.enable { 238 239 assertions = [ { 240 assertion = cfg.autodetect || cfg.devices != []; 241 message = "smartd can't run with both disabled autodetect and an empty list of devices to monitor."; 242 } ]; 243 244 systemd.services.smartd = { 245 description = "S.M.A.R.T. Daemon"; 246 wantedBy = [ "multi-user.target" ]; 247 serviceConfig.ExecStart = "${pkgs.smartmontools}/sbin/smartd ${lib.concatStringsSep " " cfg.extraOptions} --no-fork --configfile=${smartdConf}"; 248 }; 249 250 }; 251 252}