summary refs log tree commit diff
diff options
context:
space:
mode:
authorRaymond Gauthier <jraygauthier@gmail.com>2016-04-29 21:45:21 -0400
committerRaymond Gauthier <jraygauthier@gmail.com>2016-05-01 14:42:25 -0400
commit758e8bd1a1ae9a77a33f4168a778158446179e93 (patch)
tree547bdff29df9a2b3961a03d302c34a48b9f1b42f
parentec6b547b17d102c44f8817bae875418bd2797786 (diff)
downloadnixpkgs-758e8bd1a1ae9a77a33f4168a778158446179e93.tar
nixpkgs-758e8bd1a1ae9a77a33f4168a778158446179e93.tar.gz
nixpkgs-758e8bd1a1ae9a77a33f4168a778158446179e93.tar.bz2
nixpkgs-758e8bd1a1ae9a77a33f4168a778158446179e93.tar.lz
nixpkgs-758e8bd1a1ae9a77a33f4168a778158446179e93.tar.xz
nixpkgs-758e8bd1a1ae9a77a33f4168a778158446179e93.tar.zst
nixpkgs-758e8bd1a1ae9a77a33f4168a778158446179e93.zip
brscan4: init at 0.4.3-3
A sane backend for recent brother scanners.

Depends on the presence of etc files generated by the
nixos module of the same name.

Supports network scanner specification through the
nixos module.
-rw-r--r--nixos/modules/services/hardware/sane_extra_backends/brscan4.nix116
-rw-r--r--nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix71
-rw-r--r--pkgs/applications/graphics/sane/backends/brscan4/default.nix97
-rw-r--r--pkgs/applications/graphics/sane/backends/brscan4/preload.c170
-rw-r--r--pkgs/applications/graphics/sane/backends/brscan4/udev_rules_type1.nix60
-rw-r--r--pkgs/top-level/all-packages.nix2
6 files changed, 516 insertions, 0 deletions
diff --git a/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix b/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix
new file mode 100644
index 00000000000..3ec74458cd2
--- /dev/null
+++ b/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix
@@ -0,0 +1,116 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.sane.brscan4;
+
+  netDeviceList = attrValues cfg.netDevices;
+
+  etcFiles = pkgs.callPackage ./brscan4_etc_files.nix { netDevices = netDeviceList; };
+
+  netDeviceOpts = { name, config, ... }: {
+
+    options = {
+
+      name = mkOption {
+        type = types.str;
+        description = ''
+          The friendly name you give to the network device. If undefined,
+          the name of attribute will be used.
+        '';
+
+        example = literalExample "office1";
+      };
+
+      model = mkOption {
+        type = types.str;
+        description = ''
+          The model of the network device.
+        '';
+
+        example = literalExample "MFC-7860DW";
+      };
+
+      ip = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          The ip address of the device. If undefined, you will have to
+          provide a nodename.
+        '';
+
+        example = literalExample "192.168.1.2";
+      };
+
+      nodename = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          The node name of the device. If undefined, you will have to
+          provide an ip.
+        '';
+
+        example = literalExample "BRW0080927AFBCE";
+      };
+
+    };
+
+
+    config =
+      { name = mkDefault name;
+      };
+  };
+
+in
+
+{
+  options = {
+
+    hardware.sane.brscan4.enable = 
+      mkEnableOption "Brother's brscan4 scan backend" // {
+      description = ''
+        When enabled, will automatically register the "brscan4" sane
+        backend and bring configuration files to their expected location. 
+      '';
+    };
+
+    hardware.sane.brscan4.netDevices = mkOption {
+      default = {};
+      example =
+        { office1 = { model = "MFC-7860DW"; ip = "192.168.1.2"; };
+          office2 = { model = "MFC-7860DW"; nodename = "BRW0080927AFBCE"; };
+        };
+      type = types.loaOf types.optionSet;
+      description = ''
+        The list of network devices that will be registered against the brscan4
+        sane backend.
+      '';
+      options = [ netDeviceOpts ];
+    };
+  };
+
+  config = mkIf (config.hardware.sane.enable && cfg.enable) {
+
+    hardware.sane.extraBackends = [
+      pkgs.brscan4
+    ];
+
+    environment.etc = singleton {
+      target = "opt/brother/scanner/brscan4";
+      source = "${etcFiles}/etc/opt/brother/scanner/brscan4";
+    };
+
+    assertions = [
+      { assertion = all (x: !(null != x.ip && null != x.nodename)) netDeviceList;
+          
+        message = ''
+          When describing a network device as part of the attribute list
+          `hardware.sane.brscan4.netDevices`, only one of its `ip` or `nodename`
+          attribute should be specified, not both!
+        '';
+      }
+    ];
+
+  };
+}
\ No newline at end of file
diff --git a/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix b/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix
new file mode 100644
index 00000000000..bd114f0d2cc
--- /dev/null
+++ b/nixos/modules/services/hardware/sane_extra_backends/brscan4_etc_files.nix
@@ -0,0 +1,71 @@
+{ stdenv, lib, brscan4, netDevices ? [] }:
+
+/*
+
+Testing
+-------
+
+No net devices:
+
+~~~
+nix-shell -E 'with import <nixpkgs> { }; brscan4-etc-files'
+~~~
+
+Two net devices:
+
+~~~
+nix-shell -E 'with import <nixpkgs> { }; brscan4-etc-files.override{netDevices=[{name="a"; model="MFC-7860DW"; nodename="BRW0080927AFBCE";} {name="b"; model="MFC-7860DW"; ip="192.168.1.2";}];}'
+~~~
+
+*/
+
+with lib; 
+
+let
+
+  addNetDev = nd: ''
+    brsaneconfig4 -a \
+    name="${nd.name}" \
+    model="${nd.model}" \
+    ${if (hasAttr "nodename" nd && nd.nodename != null) then
+      ''nodename="${nd.nodename}"'' else
+      ''ip="${nd.ip}"''}'';
+  addAllNetDev = xs: concatStringsSep "\n" (map addNetDev xs);
+in
+
+stdenv.mkDerivation rec {
+
+  name = "brscan4-etc-files-0.4.3-3";
+  src = "${brscan4}/opt/brother/scanner/brscan4";
+
+  nativeBuildInputs = [ brscan4 ];
+
+  configurePhase = ":";
+
+  buildPhase = ''
+    TARGET_DIR="$out/etc/opt/brother/scanner/brscan4"
+    mkdir -p "$TARGET_DIR"
+    cp -rp "./models4" "$TARGET_DIR"
+    cp -rp "./Brsane4.ini" "$TARGET_DIR"
+    cp -rp "./brsanenetdevice4.cfg" "$TARGET_DIR"
+
+    export BRSANENETDEVICE4_CFG_FILENAME="$TARGET_DIR/brsanenetdevice4.cfg"
+
+    printf '${addAllNetDev netDevices}\n'
+
+    ${addAllNetDev netDevices}
+  '';
+
+  installPhase = ":";
+
+  dontStrip = true;
+  dontPatchELF = true;
+
+  meta = {
+    description = "Brother brscan4 sane backend driver etc files";
+    homepage = http://www.brother.com;
+    platforms = stdenv.lib.platforms.linux;
+    license = stdenv.lib.licenses.unfree;
+    maintainers = with stdenv.lib.maintainers; [ jraygauthier ];
+  };
+}
diff --git a/pkgs/applications/graphics/sane/backends/brscan4/default.nix b/pkgs/applications/graphics/sane/backends/brscan4/default.nix
new file mode 100644
index 00000000000..7b22e88bb84
--- /dev/null
+++ b/pkgs/applications/graphics/sane/backends/brscan4/default.nix
@@ -0,0 +1,97 @@
+{ stdenv, fetchurl, callPackage, patchelf, makeWrapper, coreutils, libusb }:
+
+/*
+
+
+*/
+
+let
+
+  myPatchElf = file: with stdenv.lib; ''
+    patchelf --set-interpreter \
+      ${stdenv.glibc}/lib/ld-linux${optionalString stdenv.is64bit "-x86-64"}.so.2 \
+      ${file}
+  '';
+
+  udevRules = callPackage ./udev_rules_type1.nix {};
+
+in
+
+stdenv.mkDerivation rec {
+
+  name = "brscan4-0.4.3-3";
+  src = fetchurl {
+    url = "http://download.brother.com/welcome/dlf006645/${name}.amd64.deb";
+    sha256 = "1nccyjl0b195pn6ya4q0zijb075q8r31v9z9a0hfzipfyvcj57n2";
+  };
+
+  unpackPhase = ''
+    ar x $src
+    tar xfvz data.tar.gz
+  '';
+
+  nativeBuildInputs = [ makeWrapper patchelf coreutils udevRules ];
+  buildInputs = [ libusb ];
+  buildPhase = ":";
+
+
+  patchPhase = ''
+    ${myPatchElf "opt/brother/scanner/brscan4/brsaneconfig4"}
+
+    RPATH=${libusb}/lib
+    for a in usr/lib64/sane/*.so*; do
+      if ! test -L $a; then
+        patchelf --set-rpath $RPATH $a
+      fi
+    done
+  '';
+
+  installPhase = ''
+
+    PATH_TO_BRSCAN4="opt/brother/scanner/brscan4"
+    mkdir -p $out/$PATH_TO_BRSCAN4
+    cp -rp $PATH_TO_BRSCAN4/* $out/$PATH_TO_BRSCAN4
+    mkdir -p $out/lib/sane
+    cp -rp usr/lib64/sane/* $out/lib/sane
+
+    # Symbolic links were absolute. Fix them so that they point to $out.
+    pushd "$out/lib/sane" > /dev/null
+    for a in *.so*; do
+      if test -L $a; then
+        fixedTargetFileName="$(basename $(readlink $a))"
+        unlink "$a"
+        ln -s -T "$fixedTargetFileName" "$a"
+      fi
+    done
+    popd > /dev/null
+
+    # Generate an LD_PRELOAD wrapper to redirect execvp(), open() and open64()
+    # calls to `/opt/brother/scanner/brscan4`.
+    preload=$out/libexec/brother/scanner/brscan4/libpreload.so
+    mkdir -p $(dirname $preload)
+    gcc -shared ${./preload.c} -o $preload -ldl -DOUT=\"$out\" -fPIC
+
+    makeWrapper \
+      "$out/$PATH_TO_BRSCAN4/brsaneconfig4" \
+      "$out/bin/brsaneconfig4" \
+      --set LD_PRELOAD $preload
+
+    mkdir -p $out/etc/sane.d
+    echo "brother4" > $out/etc/sane.d/dll.conf
+
+    mkdir -p $out/etc/udev/rules.d
+    cp -p ${udevRules}/etc/udev/rules.d/*.rules \
+      $out/etc/udev/rules.d
+  '';
+
+  dontStrip = true;
+  dontPatchELF = true;
+
+  meta = {
+    description = "Brother brscan4 sane backend driver";
+    homepage = http://www.brother.com;
+    platforms = stdenv.lib.platforms.linux;
+    license = stdenv.lib.licenses.unfree;
+    maintainers = with stdenv.lib.maintainers; [ jraygauthier ];
+  };
+}
diff --git a/pkgs/applications/graphics/sane/backends/brscan4/preload.c b/pkgs/applications/graphics/sane/backends/brscan4/preload.c
new file mode 100644
index 00000000000..01616277093
--- /dev/null
+++ b/pkgs/applications/graphics/sane/backends/brscan4/preload.c
@@ -0,0 +1,170 @@
+/* Brgen4 search for configuration under `/etc/opt/brother/scanner/brscan4`. This
+   LD_PRELOAD library intercepts execvp(), open and open64 calls to redirect them to
+   the corresponding location in $out. Also support specifying an alternate
+   file name for `brsanenetdevice4.cfg` which otherwise is invariable
+   created at `/etc/opt/brother/scanner/brscan4`*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>
+#include <dirent.h>
+
+char origDir [] = "/etc/opt/brother/scanner/brscan4";
+char realDir [] = OUT "/opt/brother/scanner/brscan4";
+
+char devCfgFileNameEnvVar [] = "BRSANENETDEVICE4_CFG_FILENAME";
+char devCfgFileName [] = "/etc/opt/brother/scanner/brscan4//brsanenetdevice4.cfg";
+
+const char * rewrite(const char * path, char * buf)
+{
+    if (strncmp(path, devCfgFileName, sizeof(devCfgFileName)) == 0) {
+
+      const char* newCfgFileName = getenv(devCfgFileNameEnvVar);
+      if (!newCfgFileName) return path;
+
+      if (snprintf(buf, PATH_MAX, "%s", newCfgFileName) >= PATH_MAX)
+          abort();
+      return buf;
+    }
+
+    if (strncmp(path, origDir, sizeof(origDir) - 1) != 0) return path;
+    if (snprintf(buf, PATH_MAX, "%s%s", realDir, path + sizeof(origDir) - 1) >= PATH_MAX)
+        abort();
+    return buf;
+}
+
+const char* findAndReplaceFirstOccurence(const char* inStr, const char* subStr, 
+                                         const char* replaceStr, 
+                                         char* buf, unsigned maxBuf)
+{
+    const char* foundStr = strstr(inStr, subStr);
+    if (!foundStr)
+      return inStr;
+
+    const unsigned inStrLen = strlen(inStr);
+    const unsigned subStrLen = strlen(subStr);
+    const unsigned replaceStrLen = strlen(replaceStr);
+
+    const unsigned precedingStrLen = foundStr - inStr;
+    if (precedingStrLen + 1 > maxBuf)
+      return NULL;
+
+    const unsigned followingStrPos = precedingStrLen + subStrLen;
+    const unsigned followingStrLen = inStrLen - followingStrPos;
+
+    strncpy(buf, inStr, precedingStrLen);
+    unsigned outLength = precedingStrLen;
+
+    if (outLength + replaceStrLen + 1 > maxBuf)
+      return NULL;
+
+    strncpy(buf + outLength, replaceStr, replaceStrLen);
+    outLength += replaceStrLen;
+    
+    if (outLength + followingStrLen + 1 > maxBuf)
+      return NULL;
+
+    strncpy(buf + outLength, inStr + followingStrPos, followingStrLen);
+    outLength += followingStrLen;
+    
+    buf[outLength] = '\0';
+
+    return buf;
+}
+
+const char* rewriteSystemCall(const char* command, char* buf, unsigned maxBuf)
+{
+
+    const char* foundStr = strstr(command, devCfgFileName);
+    if (!foundStr)
+      return command;
+
+    const char* replaceStr = getenv(devCfgFileNameEnvVar);
+    if (!replaceStr) return command;
+
+    const char* result = 
+      findAndReplaceFirstOccurence(command, devCfgFileName, replaceStr, buf, maxBuf);
+
+    if (!result)
+      abort();
+
+    return result;
+}
+
+int execvp(const char * path, char * const argv[])
+{
+    int (*_execvp) (const char *, char * const argv[]) = dlsym(RTLD_NEXT, "execvp");
+    char buf[PATH_MAX];
+    return _execvp(rewrite(path, buf), argv);
+}
+
+
+int open(const char *path, int flags, ...)
+{
+    char buf[PATH_MAX];
+    int (*_open) (const char *, int, mode_t) = dlsym(RTLD_NEXT, "open");
+    mode_t mode = 0;
+    if (flags & O_CREAT) {
+        va_list ap;
+        va_start(ap, flags);
+        mode = va_arg(ap, mode_t);
+        va_end(ap);
+    }
+    return _open(rewrite(path, buf), flags, mode);
+}
+
+int open64(const char *path, int flags, ...)
+{
+    char buf[PATH_MAX];
+    int (*_open64) (const char *, int, mode_t) = dlsym(RTLD_NEXT, "open64");
+    mode_t mode = 0;
+    if (flags & O_CREAT) {
+        va_list ap;
+        va_start(ap, flags);
+        mode = va_arg(ap, mode_t);
+        va_end(ap);
+    }
+    return _open64(rewrite(path, buf), flags, mode);
+}
+
+FILE* fopen(const char* path, const char* mode)
+{
+  char buf[PATH_MAX];
+	FILE* (*_fopen) (const char*, const char*) = dlsym(RTLD_NEXT, "fopen");
+
+	return _fopen(rewrite(path, buf), mode);
+}
+
+FILE *fopen64(const char *path, const char *mode)
+{
+  char buf[PATH_MAX];
+	FILE* (*_fopen64) (const char*, const char*) = dlsym(RTLD_NEXT, "fopen64");
+
+	return _fopen64(rewrite(path, buf), mode);
+}
+
+DIR* opendir(const char* path)
+{
+  char buf[PATH_MAX];
+	DIR* (*_opendir) (const char*) = dlsym(RTLD_NEXT, "opendir");
+
+	return _opendir(rewrite(path, buf));
+}
+
+#define SYSTEM_CMD_MAX 512
+
+int system(const char *command)
+{
+    char buf[SYSTEM_CMD_MAX];
+    int (*_system) (const char*) = dlsym(RTLD_NEXT, "system");
+
+    const char* newCommand = rewriteSystemCall(command, buf, SYSTEM_CMD_MAX);
+    return _system(newCommand);
+}
diff --git a/pkgs/applications/graphics/sane/backends/brscan4/udev_rules_type1.nix b/pkgs/applications/graphics/sane/backends/brscan4/udev_rules_type1.nix
new file mode 100644
index 00000000000..873240e81fc
--- /dev/null
+++ b/pkgs/applications/graphics/sane/backends/brscan4/udev_rules_type1.nix
@@ -0,0 +1,60 @@
+{ stdenv, fetchurl, libsaneUDevRuleNumber ? "49"}:
+
+
+stdenv.mkDerivation rec {
+
+  name = "brother-udev-rule-type1-1.0.0-1";
+
+  src = fetchurl {
+    url = "http://download.brother.com/welcome/dlf006654/${name}.all.deb";
+    sha256 = "0i0x5jw135pli4jl9mgnr5n2rrdvml57nw84yq2999r4frza53xi";
+  };
+
+  buildInputs = [ ];
+
+  unpackPhase = ''
+    ar x $src
+    tar xfvz data.tar.gz
+  '';
+
+  /*
+    Fix the following error:
+
+    ~~~
+    invalid rule 49-brother-libsane-type1.rules
+    unknown key 'SYSFS{idVendor}'
+    ~~~
+
+    Apparently the udev rules syntax has change and the SYSFS key has to
+    be changed to ATTR.
+
+    See:
+
+     -  <http://ubuntuforums.org/showthread.php?t=1496878>
+     -  <http://www.planet-libre.org/index.php?post_id=10937>
+  */
+  patchPhase = ''
+    sed -i -e s/SYSFS/ATTR/g opt/brother/scanner/udev-rules/type1/*.rules
+  '';
+
+
+  buildPhase = ":";
+
+  installPhase = ''
+    mkdir -p $out/etc/udev/rules.d
+    cp opt/brother/scanner/udev-rules/type1/NN-brother-mfp-type1.rules \
+      $out/etc/udev/rules.d/${libsaneUDevRuleNumber}-brother-libsane-type1.rules
+    chmod 644 $out/etc/udev/rules.d/${libsaneUDevRuleNumber}-brother-libsane-type1.rules
+  '';
+
+  dontStrip = true;
+  dontPatchELF = true;
+
+  meta = {
+    description = "Brother type1 scanners udev rules";
+    homepage = http://www.brother.com;
+    platforms = stdenv.lib.platforms.linux;
+    license = stdenv.lib.licenses.unfree;
+    maintainers = with stdenv.lib.maintainers; [ jraygauthier ];
+  };
+}
\ No newline at end of file
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 586248d04ee..2b922f330bd 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -16436,6 +16436,8 @@ in
     snapscanFirmware = config.sane.snapscanFirmware or null;
   };
 
+  brscan4 = callPackage ../applications/graphics/sane/backends/brscan4 { };
+
   mkSaneConfig = callPackage ../applications/graphics/sane/config.nix { };
 
   sane-frontends = callPackage ../applications/graphics/sane/frontends.nix { };