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