nixpkgs/nixos/modules/services/misc/zoneminder.nix
pennae 2e751c0772 treewide: automatically md-convert option descriptions
the conversion procedure is simple:

 - find all things that look like options, ie calls to either `mkOption`
   or `lib.mkOption` that take an attrset. remember the attrset as the
   option
 - for all options, find a `description` attribute who's value is not a
   call to `mdDoc` or `lib.mdDoc`
 - textually convert the entire value of the attribute to MD with a few
   simple regexes (the set from mdize-module.sh)
 - if the change produced a change in the manual output, discard
 - if the change kept the manual unchanged, add some text to the
   description to make sure we've actually found an option. if the
   manual changes this time, keep the converted description

this procedure converts 80% of nixos options to markdown. around 2000
options remain to be inspected, but most of those fail the "does not
change the manual output check": currently the MD conversion process
does not faithfully convert docbook tags like <code> and <package>, so
any option using such tags will not be converted at all.
2022-07-30 15:16:34 +02:00

371 lines
10 KiB
Nix

{ config, lib, pkgs, ... }:
let
cfg = config.services.zoneminder;
fpm = config.services.phpfpm.pools.zoneminder;
pkg = pkgs.zoneminder;
dirName = pkg.dirName;
user = "zoneminder";
group = {
nginx = config.services.nginx.group;
none = user;
}.${cfg.webserver};
useNginx = cfg.webserver == "nginx";
defaultDir = "/var/lib/${user}";
home = if useCustomDir then cfg.storageDir else defaultDir;
useCustomDir = cfg.storageDir != null;
zms = "/cgi-bin/zms";
dirs = dirList: [ dirName ] ++ map (e: "${dirName}/${e}") dirList;
cacheDirs = [ "swap" ];
libDirs = [ "events" "exports" "images" "sounds" ];
dirStanzas = baseDir:
lib.concatStringsSep "\n" (map (e:
"ZM_DIR_${lib.toUpper e}=${baseDir}/${e}"
) libDirs);
defaultsFile = pkgs.writeText "60-defaults.conf" ''
# 01-system-paths.conf
${dirStanzas home}
ZM_PATH_ARP=${lib.getBin pkgs.nettools}/bin/arp
ZM_PATH_LOGS=/var/log/${dirName}
ZM_PATH_MAP=/dev/shm
ZM_PATH_SOCKS=/run/${dirName}
ZM_PATH_SWAP=/var/cache/${dirName}/swap
ZM_PATH_ZMS=${zms}
# 02-multiserver.conf
ZM_SERVER_HOST=
# Database
ZM_DB_TYPE=mysql
ZM_DB_HOST=${cfg.database.host}
ZM_DB_NAME=${cfg.database.name}
ZM_DB_USER=${cfg.database.username}
ZM_DB_PASS=${cfg.database.password}
# Web
ZM_WEB_USER=${user}
ZM_WEB_GROUP=${group}
'';
configFile = pkgs.writeText "80-nixos.conf" ''
# You can override defaults here
${cfg.extraConfig}
'';
in {
options = {
services.zoneminder = with lib; {
enable = lib.mkEnableOption ''
ZoneMinder
</para><para>
If you intend to run the database locally, you should set
`config.services.zoneminder.database.createLocally` to true. Otherwise,
when set to `false` (the default), you will have to create the database
and database user as well as populate the database yourself.
Additionally, you will need to run `zmupdate.pl` yourself when
upgrading to a newer version.
'';
webserver = mkOption {
type = types.enum [ "nginx" "none" ];
default = "nginx";
description = ''
The webserver to configure for the PHP frontend.
</para>
<para>
Set it to `none` if you want to configure it yourself. PRs are welcome
for support for other web servers.
'';
};
hostname = mkOption {
type = types.str;
default = "localhost";
description = lib.mdDoc ''
The hostname on which to listen.
'';
};
port = mkOption {
type = types.int;
default = 8095;
description = lib.mdDoc ''
The port on which to listen.
'';
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Open the firewall port(s).
'';
};
database = {
createLocally = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Create the database and database user locally.
'';
};
host = mkOption {
type = types.str;
default = "localhost";
description = lib.mdDoc ''
Hostname hosting the database.
'';
};
name = mkOption {
type = types.str;
default = "zm";
description = lib.mdDoc ''
Name of database.
'';
};
username = mkOption {
type = types.str;
default = "zmuser";
description = lib.mdDoc ''
Username for accessing the database.
'';
};
password = mkOption {
type = types.str;
default = "zmpass";
description = lib.mdDoc ''
Username for accessing the database.
Not used if `createLocally` is set.
'';
};
};
cameras = mkOption {
type = types.int;
default = 1;
description = lib.mdDoc ''
Set this to the number of cameras you expect to support.
'';
};
storageDir = mkOption {
type = types.nullOr types.str;
default = null;
example = "/storage/tank";
description = lib.mdDoc ''
ZoneMinder can generate quite a lot of data, so in case you don't want
to use the default ${defaultDir}, you can override the path here.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = lib.mdDoc ''
Additional configuration added verbatim to the configuration file.
'';
};
};
};
config = lib.mkIf cfg.enable {
assertions = [
{ assertion = cfg.database.createLocally -> cfg.database.username == user;
message = "services.zoneminder.database.username must be set to ${user} if services.zoneminder.database.createLocally is set true";
}
];
environment.etc = {
"zoneminder/60-defaults.conf".source = defaultsFile;
"zoneminder/80-nixos.conf".source = configFile;
};
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
cfg.port
6802 # zmtrigger
];
services = {
fcgiwrap = lib.mkIf useNginx {
enable = true;
preforkProcesses = cfg.cameras;
inherit user group;
};
mysql = lib.mkIf cfg.database.createLocally {
enable = true;
package = lib.mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [{
name = cfg.database.username;
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
}];
};
nginx = lib.mkIf useNginx {
enable = true;
virtualHosts = {
${cfg.hostname} = {
default = true;
root = "${pkg}/share/zoneminder/www";
listen = [ { addr = "0.0.0.0"; inherit (cfg) port; } ];
extraConfig = let
fcgi = config.services.fcgiwrap;
in ''
index index.php;
location / {
try_files $uri $uri/ /index.php?$args =404;
rewrite ^/skins/.*/css/fonts/(.*)$ /fonts/$1 permanent;
location ~ /api/(css|img|ico) {
rewrite ^/api(.+)$ /api/app/webroot/$1 break;
try_files $uri $uri/ =404;
}
location ~ \.(gif|ico|jpg|jpeg|png)$ {
access_log off;
expires 30d;
}
location /api {
rewrite ^/api(.+)$ /api/app/webroot/index.php?p=$1 last;
}
location /cgi-bin {
gzip off;
include ${config.services.nginx.package}/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME ${pkg}/libexec/zoneminder/${zms};
fastcgi_param HTTP_PROXY "";
fastcgi_intercept_errors on;
fastcgi_pass ${fcgi.socketType}:${fcgi.socketAddress};
}
location /cache/ {
alias /var/cache/${dirName}/;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_index index.php;
include ${config.services.nginx.package}/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param HTTP_PROXY "";
fastcgi_pass unix:${fpm.socket};
}
}
'';
};
};
};
phpfpm = lib.mkIf useNginx {
pools.zoneminder = {
inherit user group;
phpPackage = pkgs.php.withExtensions ({ enabled, all }: enabled ++ [ all.apcu ]);
phpOptions = ''
date.timezone = "${config.time.timeZone}"
'';
settings = lib.mapAttrs (name: lib.mkDefault) {
"listen.owner" = user;
"listen.group" = group;
"listen.mode" = "0660";
"pm" = "dynamic";
"pm.start_servers" = 1;
"pm.min_spare_servers" = 1;
"pm.max_spare_servers" = 2;
"pm.max_requests" = 500;
"pm.max_children" = 5;
"pm.status_path" = "/$pool-status";
"ping.path" = "/$pool-ping";
};
};
};
};
systemd.services = {
zoneminder = with pkgs; {
inherit (zoneminder.meta) description;
documentation = [ "https://zoneminder.readthedocs.org/en/latest/" ];
path = [
coreutils
procps
psmisc
];
after = [ "nginx.service" ] ++ lib.optional cfg.database.createLocally "mysql.service";
wantedBy = [ "multi-user.target" ];
restartTriggers = [ defaultsFile configFile ];
preStart = lib.optionalString useCustomDir ''
install -dm775 -o ${user} -g ${group} ${cfg.storageDir}/{${lib.concatStringsSep "," libDirs}}
'' + lib.optionalString cfg.database.createLocally ''
if ! test -e "/var/lib/${dirName}/db-created"; then
${config.services.mysql.package}/bin/mysql < ${pkg}/share/zoneminder/db/zm_create.sql
touch "/var/lib/${dirName}/db-created"
fi
${zoneminder}/bin/zmupdate.pl -nointeractive
'';
serviceConfig = {
User = user;
Group = group;
SupplementaryGroups = [ "video" ];
ExecStart = "${zoneminder}/bin/zmpkg.pl start";
ExecStop = "${zoneminder}/bin/zmpkg.pl stop";
ExecReload = "${zoneminder}/bin/zmpkg.pl restart";
PIDFile = "/run/${dirName}/zm.pid";
Type = "forking";
Restart = "on-failure";
RestartSec = "10s";
CacheDirectory = dirs cacheDirs;
RuntimeDirectory = dirName;
ReadWriteDirectories = lib.mkIf useCustomDir [ cfg.storageDir ];
StateDirectory = dirs (if useCustomDir then [] else libDirs);
LogsDirectory = dirName;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectKernelTunables = true;
SystemCallArchitectures = "native";
NoNewPrivileges = true;
};
};
};
users.groups.${user} = {
gid = config.ids.gids.zoneminder;
};
users.users.${user} = {
uid = config.ids.uids.zoneminder;
group = user;
inherit home;
inherit (pkgs.zoneminder.meta) description;
};
};
meta.maintainers = with lib.maintainers; [ ];
}