1 { config, lib, pkgs, ... }:
6 cfg = config.services.smokeping;
7 smokepingHome = "/var/lib/smokeping";
8 smokepingPidDir = "/run";
14 cgiurl = ${cfg.cgiUrl}
15 contact = ${cfg.ownerEmail}
16 datadir = ${smokepingHome}/data
17 imgcache = ${smokepingHome}/cache
18 imgurl = ${cfg.imgUrl}
19 linkstyle = ${cfg.linkStyle}
20 ${lib.optionalString (cfg.mailHost != "") "mailhost = ${cfg.mailHost}"}
22 pagedir = ${smokepingHome}/cache
23 piddir = ${smokepingPidDir}
24 ${lib.optionalString (cfg.sendmail != null) "sendmail = ${cfg.sendmail}"}
25 smokemail = ${cfg.smokeMailTemplate}
27 template = ${cfg.presentationTemplate}
28 ${cfg.presentationConfig}
42 configPath = pkgs.writeText "smokeping.conf" configFile;
43 cgiHome = pkgs.writeScript "smokeping.fcgi" ''
44 #!${pkgs.bash}/bin/bash
45 ${cfg.package}/bin/smokeping_cgi /etc/smokeping.conf
51 (mkRemovedOptionModule [ "services" "smokeping" "port" ] ''
52 The smokeping web service is now served by nginx.
53 In order to change the port, you need to change the nginx configuration under `services.nginx.virtualHosts.smokeping.listen.*.port`.
58 services.smokeping = {
59 enable = mkEnableOption "smokeping service";
61 alertConfig = mkOption {
65 from = smokeping@localhost
68 to = alertee@address.somewhere
69 from = smokealert@company.xy
74 pattern = >0%,*12*,>0%,*12*,>0%
75 comment = loss 3 times in a row;
77 description = "Configuration for alerts.";
81 default = "http://${cfg.hostName}/smokeping.cgi";
82 defaultText = literalExpression ''"http://''${hostName}/smokeping.cgi"'';
83 example = "https://somewhere.example.com/smokeping.cgi";
84 description = "URL to the smokeping cgi.";
87 type = types.nullOr types.lines;
90 Full smokeping config supplied by the user. Overrides
91 and replaces any other configuration supplied.
94 databaseConfig = mkOption {
99 # consfn mrhb steps total
110 # near constant pings.
113 # consfn mrhb steps total
122 description = ''Configure the ping frequency and retention of the rrd files.
123 Once set, changing the interval will require deletion or migration of all
124 the collected data.'';
126 extraConfig = mkOption {
129 description = "Any additional customization not already included.";
131 hostName = mkOption {
133 default = config.networking.fqdn;
134 defaultText = literalExpression "config.networking.fqdn";
135 example = "somewhere.example.com";
136 description = "DNS name for the urls generated in the cgi.";
141 defaultText = literalExpression ''"cache"'';
142 example = "https://somewhere.example.com/cache";
144 Base url for images generated in the cgi.
146 The default is a relative URL to ensure it works also when e.g. forwarding
147 the GUI port via SSH.
150 linkStyle = mkOption {
151 type = types.enum [ "original" "absolute" "relative" ];
152 default = "relative";
153 example = "absolute";
154 description = "DNS name for the urls generated in the cgi.";
156 mailHost = mkOption {
159 example = "localhost";
160 description = "Use this SMTP server to send alerts";
165 example = "Bob Foobawr";
166 description = "Real name of the owner of the instance";
168 ownerEmail = mkOption {
170 default = "no-reply@${cfg.hostName}";
171 defaultText = literalExpression ''"no-reply@''${hostName}"'';
172 example = "no-reply@yourdomain.com";
173 description = "Email contact for owner";
175 package = mkPackageOption pkgs "smokeping" { };
177 type = types.nullOr types.str;
178 default = "localhost";
179 example = "192.0.2.1"; # rfc5737 example IP for documentation
181 Host/IP to bind to for the web server.
183 Setting it to `null` skips passing the -h option to thttpd,
184 which makes it bind to all interfaces.
187 presentationConfig = mkOption {
192 title = The most interesting destinations
194 sorter = StdDev(entries=>4)
195 title = Top Standard Deviation
197 format = Standard Deviation %f
199 sorter = Max(entries=>5)
200 title = Top Max Roundtrip Time
202 format = Max Roundtrip Time %f seconds
204 sorter = Loss(entries=>5)
205 title = Top Packet Loss
207 format = Packets Lost %f
209 sorter = Median(entries=>5)
210 title = Top Median Roundtrip Time
212 format = Median RTT %f seconds
226 description = "presentation graph style";
228 presentationTemplate = mkOption {
230 default = "${pkgs.smokeping}/etc/basepage.html.dist";
231 defaultText = literalExpression ''"''${pkgs.smokeping}/etc/basepage.html.dist"'';
232 description = "Default page layout for the web UI.";
234 probeConfig = mkOption {
238 binary = ${config.security.wrapperDir}/fping
240 defaultText = literalExpression ''
243 binary = ''${config.security.wrapperDir}/fping
246 description = "Probe configuration";
248 sendmail = mkOption {
249 type = types.nullOr types.path;
251 example = "/run/wrappers/bin/sendmail";
252 description = "Use this sendmail compatible script to deliver alerts";
254 smokeMailTemplate = mkOption {
256 default = "${cfg.package}/etc/smokemail.dist";
257 defaultText = literalExpression ''"''${package}/etc/smokemail.dist"'';
258 description = "Specify the smokemail template for alerts.";
260 targetConfig = mkOption {
265 title = Network Latency Grapher
266 remark = Welcome to the SmokePing website of xxx Company. \
267 Here you will learn all about the latency of our network.
270 title = Local Network
276 description = "Target configuration";
280 default = "smokeping";
281 description = "User that runs smokeping and (optionally) thttpd. A group of the same name will be created as well.";
283 webService = mkOption {
286 description = "Enable a smokeping web interface";
292 config = mkIf cfg.enable {
295 assertion = !(cfg.sendmail != null && cfg.mailHost != "");
296 message = "services.smokeping: sendmail and Mailhost cannot both be enabled.";
299 security.wrappers = {
305 source = "${pkgs.fping}/bin/fping";
308 environment.etc."smokeping.conf".source = configPath;
309 environment.systemPackages = [ pkgs.fping ];
310 users.users.${cfg.user} = {
311 isNormalUser = false;
314 description = "smokeping daemon user";
315 home = smokepingHome;
318 users.users.${config.services.nginx.user} = mkIf cfg.webService {
320 cfg.user ## user == group in this module
324 users.groups.${cfg.user} = { };
326 systemd.services.smokeping = {
327 reloadTriggers = [ configPath ];
328 requiredBy = [ "multi-user.target" ];
331 Restart = "on-failure";
332 ExecStart = "${cfg.package}/bin/smokeping --config=/etc/smokeping.conf --nodaemon";
335 ${cfg.package}/bin/smokeping --check --config=${configPath}
336 ${cfg.package}/bin/smokeping --static --config=${configPath}
340 systemd.tmpfiles.rules = [
341 # create cache and data directories
342 "d ${smokepingHome}/cache 0750 ${cfg.user} ${cfg.user}"
343 "d ${smokepingHome}/data 0750 ${cfg.user} ${cfg.user}"
345 "L+ ${smokepingHome}/css - - - - ${cfg.package}/htdocs/css"
346 "L+ ${smokepingHome}/js - - - - ${cfg.package}/htdocs/js"
347 "L+ ${smokepingHome}/smokeping.fcgi - - - - ${cgiHome}"
348 # recursively adjust access mode and ownership (in case config change)
349 "Z ${smokepingHome} 0750 ${cfg.user} ${cfg.user}"
352 # use nginx to serve the smokeping web service
353 services.fcgiwrap.instances.smokeping = mkIf cfg.webService {
354 process.user = cfg.user;
355 process.group = cfg.user;
356 socket = { inherit (config.services.nginx) user group; };
358 services.nginx = mkIf cfg.webService {
360 virtualHosts."smokeping" = {
361 serverName = mkDefault cfg.host;
363 root = smokepingHome;
364 index = "smokeping.fcgi";
366 locations."/smokeping.fcgi" = {
368 include ${config.services.nginx.package}/conf/fastcgi_params;
369 fastcgi_pass unix:${config.services.fcgiwrap.instances.smokeping.socket.address};
370 fastcgi_param SCRIPT_FILENAME ${smokepingHome}/smokeping.fcgi;
371 fastcgi_param DOCUMENT_ROOT ${smokepingHome};
378 meta.maintainers = with lib.maintainers; [