nixpkgs/nixos/modules/services/misc/zoneminder.nix
Silvan Mosberger 4f0dadbf38 treewide: format all inactive Nix files
After final improvements to the official formatter implementation,
this commit now performs the first treewide reformat of Nix files using it.
This is part of the implementation of RFC 166.

Only "inactive" files are reformatted, meaning only files that
aren't being touched by any PR with activity in the past 2 months.
This is to avoid conflicts for PRs that might soon be merged.
Later we can do a full treewide reformat to get the rest,
which should not cause as many conflicts.

A CI check has already been running for some time to ensure that new and
already-formatted files are formatted, so the files being reformatted here
should also stay formatted.

This commit was automatically created and can be verified using

    nix-build a08b3a4d19.tar.gz \
      --argstr baseRev b32a094368
    result/bin/apply-formatting $NIXPKGS_PATH
2024-12-10 20:26:33 +01:00

412 lines
11 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.
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.
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 = ''
The hostname on which to listen.
'';
};
port = mkOption {
type = types.port;
default = 8095;
description = ''
The port on which to listen.
'';
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Open the firewall port(s).
'';
};
database = {
createLocally = mkOption {
type = types.bool;
default = false;
description = ''
Create the database and database user locally.
'';
};
host = mkOption {
type = types.str;
default = "localhost";
description = ''
Hostname hosting the database.
'';
};
name = mkOption {
type = types.str;
default = "zm";
description = ''
Name of database.
'';
};
username = mkOption {
type = types.str;
default = "zmuser";
description = ''
Username for accessing the database.
'';
};
password = mkOption {
type = types.str;
default = "zmpass";
description = ''
Username for accessing the database.
Not used if `createLocally` is set.
'';
};
};
cameras = mkOption {
type = types.int;
default = 1;
description = ''
Set this to the number of cameras you expect to support.
'';
};
storageDir = mkOption {
type = types.nullOr types.str;
default = null;
example = "/storage/tank";
description = ''
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 = ''
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.instances.zoneminder = lib.mkIf useNginx {
process.prefork = cfg.cameras;
process.user = user;
process.group = group;
socket = { inherit (config.services.nginx) 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 = ''
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 unix:${config.services.fcgiwrap.instances.zoneminder.socket.address};
}
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
all.sysvsem
]
);
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
${zoneminder}/bin/zmupdate.pl --nointeractive -f
# Update ZM's Nix store path in the configuration table. Do nothing if the config doesn't
# contain ZM's Nix store path.
${config.services.mysql.package}/bin/mysql -u zoneminder zm << EOF
UPDATE Config
SET Value = REGEXP_REPLACE(Value, "^/nix/store/[^-/]+-zoneminder-[^/]+", "${pkgs.zoneminder}")
WHERE Name = "ZM_FONT_FILE_LOCATION";
EOF
'';
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;
ReadWritePaths = lib.mkIf useCustomDir [ cfg.storageDir ];
StateDirectory = dirs (lib.optionals (!useCustomDir) 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 = [ ];
}