about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Hoeg <peter@hoeg.com>2019-01-18 15:49:28 +0800
committerGitHub <noreply@github.com>2019-01-18 15:49:28 +0800
commiteaa665e2435ab3dfc356d9799ba5938aabcd75d8 (patch)
treeea94ec10d117346835e6b299df97d9861a7d2baf
parent3ba707d228eb0646c8f367ca7e6282c6900162e2 (diff)
parent982354284d36a906400beec79b6894ba5d4ee4f2 (diff)
downloadnixlib-eaa665e2435ab3dfc356d9799ba5938aabcd75d8.tar
nixlib-eaa665e2435ab3dfc356d9799ba5938aabcd75d8.tar.gz
nixlib-eaa665e2435ab3dfc356d9799ba5938aabcd75d8.tar.bz2
nixlib-eaa665e2435ab3dfc356d9799ba5938aabcd75d8.tar.lz
nixlib-eaa665e2435ab3dfc356d9799ba5938aabcd75d8.tar.xz
nixlib-eaa665e2435ab3dfc356d9799ba5938aabcd75d8.tar.zst
nixlib-eaa665e2435ab3dfc356d9799ba5938aabcd75d8.zip
Merge pull request #53495 from peterhoeg/p/zm
zoneminder: init at 1.32.3 and add NixOS module
-rw-r--r--nixos/modules/misc/ids.nix2
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/misc/zoneminder.nix362
-rw-r--r--pkgs/servers/zoneminder/default-to-http-1dot1.patch13
-rw-r--r--pkgs/servers/zoneminder/default.nix192
-rw-r--r--pkgs/top-level/all-packages.nix2
-rw-r--r--pkgs/top-level/perl-packages.nix21
7 files changed, 593 insertions, 0 deletions
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index d9ba2efa0c8a..49f30dc85a0c 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -338,6 +338,7 @@
       minetest = 311;
       rss2email = 312;
       cockroachdb = 313;
+      zoneminder = 314;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -636,6 +637,7 @@
       minetest = 311;
       rss2email = 312;
       cockroachdb = 313;
+      zoneminder = 314;
 
       # When adding a gid, make sure it doesn't match an existing
       # uid. Users and groups with the same name should have equal
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 1ae96f427ad8..a597485120c4 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -432,6 +432,7 @@
   ./services/misc/uhub.nix
   ./services/misc/weechat.nix
   ./services/misc/xmr-stak.nix
+  ./services/misc/zoneminder.nix
   ./services/misc/zookeeper.nix
   ./services/monitoring/alerta.nix
   ./services/monitoring/apcupsd.nix
diff --git a/nixos/modules/services/misc/zoneminder.nix b/nixos/modules/services/misc/zoneminder.nix
new file mode 100644
index 000000000000..a40e9e846137
--- /dev/null
+++ b/nixos/modules/services/misc/zoneminder.nix
@@ -0,0 +1,362 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.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 = !(builtins.isNull cfg.storageDir);
+
+  socket = "/run/phpfpm/${dirName}.sock";
+
+  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}
+  '';
+
+  phpExtensions = with pkgs.phpPackages; [
+    { pkg = apcu; name = "apcu"; }
+  ];
+
+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.
+      '';
+
+      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 = ''
+          The hostname on which to listen.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        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.
+          '';
+        };
+      };
+
+      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 ${home}, 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 {
+
+    environment.etc = {
+      "zoneminder/60-defaults.conf".source = defaultsFile;
+      "zoneminder/80-nixos.conf".source    = configFile;
+    };
+
+    networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ];
+
+    services = {
+      fcgiwrap = lib.mkIf useNginx {
+        enable = true;
+        preforkProcesses = cfg.cameras;
+        inherit user group;
+      };
+
+      mysql = lib.mkIf cfg.database.createLocally {
+        ensureDatabases = [ cfg.database.name ];
+        ensureUsers = {
+          name = cfg.database.username;
+          ensurePermissions = [
+            { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }
+          ];
+          initialDatabases = [
+            { inherit (cfg.database) name; schema = "${pkg}/share/zoneminder/db/zm_create.sql"; }
+          ];
+        };
+      };
+
+      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;
+
+                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 ${pkgs.nginx}/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 ${pkgs.nginx}/conf/fastcgi_params;
+                  fastcgi_param SCRIPT_FILENAME $request_filename;
+                  fastcgi_param HTTP_PROXY "";
+
+                  fastcgi_pass unix:${socket};
+                }
+              }
+            '';
+          };
+        };
+      };
+
+      phpfpm = lib.mkIf useNginx {
+        phpOptions = ''
+          date.timezone = "${config.time.timeZone}"
+
+          ${lib.concatStringsSep "\n" (map (e:
+          "extension=${e.pkg}/lib/php/extensions/${e.name}.so") phpExtensions)}
+        '';
+        pools.zoneminder = {
+          listen = socket;
+          extraConfig = ''
+            user = ${user}
+            group = ${group}
+
+            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; rec {
+        inherit (zoneminder.meta) description;
+        documentation = [ "https://zoneminder.readthedocs.org/en/latest/" ];
+        path = [
+          coreutils
+          procps
+          psmisc
+        ];
+        after = [ "mysql.service" "nginx.service" ];
+        wantedBy = [ "multi-user.target" ];
+        restartTriggers = [ defaultsFile configFile ];
+        preStart = lib.mkIf useCustomDir ''
+          install -dm775 -o ${user} -g ${group} ${cfg.storageDir}/{${lib.concatStringsSep "," libDirs}}
+        '';
+        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; [ peterhoeg ];
+}
diff --git a/pkgs/servers/zoneminder/default-to-http-1dot1.patch b/pkgs/servers/zoneminder/default-to-http-1dot1.patch
new file mode 100644
index 000000000000..abd7ffccbb5f
--- /dev/null
+++ b/pkgs/servers/zoneminder/default-to-http-1dot1.patch
@@ -0,0 +1,13 @@
+diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in
+index fa7b86079..c9d3c6f6c 100644
+--- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in
++++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in
+@@ -877,7 +877,7 @@ our @options = (
+   },
+   {
+     name        => 'ZM_HTTP_VERSION',
+-    default     => '1.0',
++    default     => '1.1',
+     description => 'The version of HTTP that ZoneMinder will use to connect',
+     help        => q`
+       ZoneMinder can communicate with network cameras using either of
diff --git a/pkgs/servers/zoneminder/default.nix b/pkgs/servers/zoneminder/default.nix
new file mode 100644
index 000000000000..d9fd7d27ee5a
--- /dev/null
+++ b/pkgs/servers/zoneminder/default.nix
@@ -0,0 +1,192 @@
+{ stdenv, lib, fetchFromGitHub, fetchurl, cmake, makeWrapper, pkgconfig
+, curl, ffmpeg, glib, libjpeg, libselinux, libsepol, mp4v2, mysql, nettools, pcre, perl, perlPackages
+, polkit, utillinuxMinimal, x264, zlib
+, avahi, dbus, gettext, git, gnutar, gzip, bzip2, libiconv, openssl, python
+, coreutils, procps, psmisc }:
+
+# NOTES:
+#
+# 1. ZM_CONFIG_DIR is set to $out/etc/zoneminder as the .conf file distributed
+# by upstream contains defaults and is not supposed to be edited so it is fine
+# to keep it read-only.
+#
+# 2. ZM_CONFIG_SUBDIR is where we place our configuration from the NixOS module
+# but as the installer will try to put files there, we patch Config.pm after the
+# install.
+#
+# 3. ZoneMinder is run with -T passed to the perl interpreter which makes perl
+# ignore PERL5LIB. We therefore have to do the substitution into -I parameters
+# ourselves which results in ugly wrappers.
+#
+# 4. The makefile for the perl modules needs patching to put things into the
+# right place. That also means we have to not run "make install" for them.
+#
+# 5. In principal the various ZM_xx variables should be overridable from the
+# config file but some of them are baked into the perl scripts, so we *have* to
+# set them here instead of in the configuration in the NixOS module.
+#
+# 6. I am no PolicyKit expert but the .policy file looks fishy:
+#   a. The user needs to be known at build-time so we should probably throw
+#   upstream's policy file away and generate it from the NixOS module
+#   b. I *think* we may have to substitute the store paths with
+#   /run/current-system/sw/bin paths for it to work.
+#
+# 7. we manually fix up the perl paths in the scripts as fixupPhase will only
+# handle pkexec and not perl if both are present.
+#
+# 8. There are several perl modules needed at runtime which are not checked when
+# building so if a new version stops working, check if there is a missing
+# dependency by running the failing component manually.
+#
+# 9. Parts of the web UI has a hardcoded /zm path so we create a symlink to work
+# around it.
+
+let
+  modules = [
+    {
+      path = "web/api/app/Plugin/Crud";
+      src = fetchFromGitHub {
+        owner = "ZoneMinder";
+        repo = "crud";
+        rev = "3.1.0-zm";
+        sha256 = "061avzyml7mla4hlx057fm8a9yjh6m6qslgyzn74cv5p2y7f463l";
+      };
+    }
+    {
+      path = "web/api/app/Plugin/CakePHP-Enum-Behavior";
+      src = fetchFromGitHub {
+        owner = "ZoneMinder";
+        repo = "CakePHP-Enum-Behavior";
+        rev = "1.0-zm";
+        sha256 = "0zsi6s8xymb183kx3szspbrwfjqcgga7786zqvydy6hc8c909cgx";
+      };
+    }
+  ];
+
+  addons = [
+    {
+      path = "scripts/ZoneMinder/lib/ZoneMinder/Control/Xiaomi.pm";
+      src = fetchurl {
+        url = "https://gist.githubusercontent.com/joshstrange/73a2f24dfaf5cd5b470024096ce2680f/raw/e964270c5cdbf95e5b7f214f7f0fc6113791530e/Xiaomi.pm";
+        sha256 = "04n1ap8fx66xfl9q9rypj48pzbgzikq0gisfsfm8wdsmflarz43v";
+      };
+    }
+  ];
+
+  user    = "zoneminder";
+  dirName = "zoneminder";
+  perlBin = "${perl}/bin/perl";
+
+in stdenv.mkDerivation rec {
+  name = "zoneminder-${version}";
+  version = "1.32.3";
+
+  src = fetchFromGitHub {
+    owner  = "ZoneMinder";
+    repo   = "zoneminder";
+    rev    = version;
+    sha256 = "1sx2fn99861zh0gp8g53ynr1q6yfmymxamn82y54jqj6nv475njz";
+  };
+
+  patches = [
+    ./default-to-http-1dot1.patch
+  ];
+
+  postPatch = ''
+    ${lib.concatStringsSep "\n" (map (e: ''
+      rm -rf ${e.path}/*
+      cp -r ${e.src}/* ${e.path}/
+    '') modules)}
+
+    rm -rf web/api/lib/Cake/Test
+
+    ${lib.concatStringsSep "\n" (map (e: ''
+      cp ${e.src} ${e.path}
+    '') addons)}
+
+    for d in scripts/ZoneMinder onvif/{modules,proxy} ; do
+      substituteInPlace $d/CMakeLists.txt \
+        --replace 'DESTDIR="''${CMAKE_CURRENT_BINARY_DIR}/output"' "PREFIX=$out INSTALLDIRS=site"
+      sed -i '/^install/d' $d/CMakeLists.txt
+    done
+
+    substituteInPlace misc/CMakeLists.txt \
+      --replace '"''${PC_POLKIT_PREFIX}/''${CMAKE_INSTALL_DATAROOTDIR}' "\"$out/share"
+
+    for f in misc/*.policy.in \
+             scripts/*.pl* \
+             scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in ; do
+      substituteInPlace $f \
+        --replace '/usr/bin/perl' '${perlBin}' \
+        --replace '/bin:/usr/bin' "$out/bin:${lib.makeBinPath [ coreutils procps psmisc ]}"
+    done
+
+    substituteInPlace scripts/zmdbbackup.in \
+      --replace /usr/bin/mysqldump ${mysql}/bin/mysqldump
+
+    for f in scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in \
+             scripts/zmupdate.pl.in \
+             src/zm_config.h.in \
+             web/api/app/Config/bootstrap.php.in \
+             web/includes/config.php.in ; do
+      substituteInPlace $f --replace @ZM_CONFIG_SUBDIR@ /etc/zoneminder
+    done
+
+   for f in includes/Event.php views/image.php skins/classic/views/image-ffmpeg.php ; do
+     substituteInPlace web/$f \
+       --replace "'ffmpeg " "'${ffmpeg}/bin/ffmpeg "
+   done
+  '';
+
+  buildInputs = [
+    curl ffmpeg glib libjpeg libselinux libsepol mp4v2 mysql pcre perl polkit x264 zlib
+    utillinuxMinimal # for libmount
+  ] ++ (with perlPackages; [
+    DateManip DBI DBDmysql LWP SysMmap
+    # runtime dependencies not checked at build-time
+    JSONMaybeXS LWPProtocolHttps NumberBytesHuman SysCPU SysMemInfo TimeDate
+  ]);
+
+  nativeBuildInputs = [ cmake makeWrapper pkgconfig ];
+
+  enableParallelBuilding = true;
+
+  cmakeFlags = [
+    "-DWITH_SYSTEMD=ON"
+    "-DZM_LOGDIR=/var/log/${dirName}"
+    "-DZM_RUNDIR=/run/${dirName}"
+    "-DZM_SOCKDIR=/run/${dirName}"
+    "-DZM_TMPDIR=/tmp/${dirName}"
+    "-DZM_CONFIG_DIR=${placeholder "out"}/etc/zoneminder"
+    "-DZM_WEB_USER=${user}"
+    "-DZM_WEB_GROUP=${user}"
+  ];
+
+  passthru = { inherit dirName; };
+
+  postInstall = ''
+    PERL5LIB="$PERL5LIB''${PERL5LIB:+:}$out/${perl.libPrefix}"
+
+    perlFlags="-wT"
+    for i in $(IFS=$'\n'; echo $PERL5LIB | tr ':' "\n" | sort -u); do
+      perlFlags="$perlFlags -I$i"
+    done
+
+    for f in $out/bin/*.pl ; do
+      mv $f $out/libexec/
+      makeWrapper ${perlBin} $f \
+        --prefix PATH : $out/bin \
+        --add-flags "$perlFlags $out/libexec/$(basename $f)"
+    done
+
+    ln -s $out/share/zoneminder/www $out/share/zoneminder/www/zm
+  '';
+
+  meta = with stdenv.lib; {
+    description = "Video surveillance software system";
+    homepage = https://zoneminder.com;
+    license = licenses.gpl3;
+    maintainers = with maintainers; [ peterhoeg ];
+    platforms = platforms.unix;
+  };
+}
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 6c18c3158665..25c0457c247d 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -23100,6 +23100,8 @@ in
     callPackage ../applications/networking/znc/modules.nix { }
   );
 
+  zoneminder = callPackage ../servers/zoneminder { };
+
   zsnes = pkgsi686Linux.callPackage ../misc/emulators/zsnes { };
 
   xcpc = callPackage ../misc/emulators/xcpc { };
diff --git a/pkgs/top-level/perl-packages.nix b/pkgs/top-level/perl-packages.nix
index 4885b2439003..fd0cc917954b 100644
--- a/pkgs/top-level/perl-packages.nix
+++ b/pkgs/top-level/perl-packages.nix
@@ -11497,6 +11497,14 @@ let
     };
   };
 
+  NumberBytesHuman = buildPerlPackage rec {
+    name = "Number-Bytes-Human-0.11";
+    src = fetchurl {
+      url = "mirror://cpan/authors/id/F/FE/FERREIRA/${name}.tar.gz";
+      sha256 = "0b3gprpbcrdwc2gqalpys5m2ngilh5injhww8y0gf3dln14rrisz";
+    };
+  };
+
   NumberCompare = buildPerlPackage rec {
     name = "Number-Compare-0.03";
     src = fetchurl {
@@ -14318,6 +14326,19 @@ let
     };
   };
 
+  SysMmap = buildPerlPackage rec {
+    name = "Sys-Mmap-0.19";
+    src = fetchurl {
+      url = "mirror://cpan/authors/id/S/SW/SWALTERS/${name}.tar.gz";
+      sha256 = "1yh0170xfw3z7n3lynffcb6axv7wi6zb46cx03crj1cvrhjmwa89";
+    };
+    meta = with stdenv.lib; {
+      description = "Use mmap to map in a file as a Perl variable";
+      maintainers = with maintainers; [ peterhoeg ];
+      license = with licenses; [ gpl2Plus ];
+    };
+  };
+
   SysMemInfo = buildPerlPackage rec {
     name = "Sys-MemInfo-0.99";
     src = fetchurl {