1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 host = config.networking.hostName or "unknown"
8 + optionalString (config.networking.domain != null) ".${config.networking.domain}";
9
10 cfg = config.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.stdenv.shell}
18 ${optionalString nm.enable ''
19 {
20 ${pkgs.coreutils}/bin/cat << EOF
21 From: smartd on ${host} <root>
22 To: undisclosed-recipients:;
23 Subject: SMART error on $SMARTD_DEVICESTRING: $SMARTD_FAILTYPE
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.utillinux}/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 = { name, ... }: {
68
69 options = {
70
71 device = mkOption {
72 example = "/dev/sda";
73 type = types.str;
74 description = "Location of the device.";
75 };
76
77 options = mkOption {
78 default = "";
79 example = "-d sat";
80 type = types.separatedString " ";
81 description = "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 "smartd daemon from <literal>smartmontools</literal> package";
98
99 autodetect = mkOption {
100 default = true;
101 type = types.bool;
102 description = ''
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</option> 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 = ''
116 Extra command-line options passed to the <literal>smartd</literal>
117 daemon on startup.
118
119 (See <literal>man 8 smartd</literal>.)
120 '';
121 };
122
123 notifications = {
124
125 mail = {
126 enable = mkOption {
127 default = config.services.mail.sendmailSetuidWrapper != null;
128 type = types.bool;
129 description = "Whenever to send e-mail notifications.";
130 };
131
132 recipient = mkOption {
133 default = "root";
134 type = types.str;
135 description = "Recipient of the notification messages.";
136 };
137
138 mailer = mkOption {
139 default = "/run/wrappers/bin/sendmail";
140 type = types.path;
141 description = ''
142 Sendmail-compatible binary to be used to send the messages.
143
144 You should probably enable
145 <option>services.postfix</option> or some other MTA for
146 this to work.
147 '';
148 };
149 };
150
151 wall = {
152 enable = mkOption {
153 default = true;
154 type = types.bool;
155 description = "Whenever to send wall notifications to all users.";
156 };
157 };
158
159 x11 = {
160 enable = mkOption {
161 default = config.services.xserver.enable;
162 type = types.bool;
163 description = "Whenever to send X11 xmessage notifications.";
164 };
165
166 display = mkOption {
167 default = ":${toString config.services.xserver.display}";
168 type = types.str;
169 description = "DISPLAY to send X11 notifications to.";
170 };
171 };
172
173 test = mkOption {
174 default = false;
175 type = types.bool;
176 description = "Whenever to send a test notification on startup.";
177 };
178
179 };
180
181 defaults = {
182 monitored = mkOption {
183 default = "-a";
184 type = types.separatedString " ";
185 example = "-a -o on -s (S/../.././02|L/../../7/04)";
186 description = ''
187 Common default options for explicitly monitored (listed in
188 <option>services.smartd.devices</option>) devices.
189
190 The default value turns on monitoring of all the things (see
191 <literal>man 5 smartd.conf</literal>).
192
193 The example also turns on SMART Automatic Offline Testing on
194 startup, and schedules short self-tests daily, and long
195 self-tests weekly.
196 '';
197 };
198
199 autodetected = mkOption {
200 default = cfg.defaults.monitored;
201 type = types.separatedString " ";
202 description = ''
203 Like <option>services.smartd.defaults.monitored</option>, but for the
204 autodetected devices.
205 '';
206 };
207 };
208
209 devices = mkOption {
210 default = [];
211 example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ];
212 type = with types; listOf (submodule smartdDeviceOpts);
213 description = "List of devices to monitor.";
214 };
215
216 };
217
218 };
219
220
221 ###### implementation
222
223 config = mkIf cfg.enable {
224
225 assertions = [ {
226 assertion = cfg.autodetect || cfg.devices != [];
227 message = "smartd can't run with both disabled autodetect and an empty list of devices to monitor.";
228 } ];
229
230 systemd.services.smartd = {
231 description = "S.M.A.R.T. Daemon";
232
233 wantedBy = [ "multi-user.target" ];
234
235 path = [ pkgs.nettools ]; # for hostname and dnsdomanname calls in smartd
236
237 serviceConfig.ExecStart = "${pkgs.smartmontools}/sbin/smartd ${lib.concatStringsSep " " cfg.extraOptions} --no-fork --configfile=${smartdConf}";
238 };
239
240 };
241
242}