From a0072b4d2d4233d29ba6456d20f1d87070fcfad2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 27 May 2014 01:00:04 +0200 Subject: [PATCH] hipchat: Fix access to /usr/share/X11/xkb HipChat (or rather its copy of Qt) expects to find keyboard data in /usr/share/X11/xkb. So use a LD_PRELOAD library to intercept and rewrite the Glibc calls that access those paths. We've been doing the same thing with packages like Spotify, but now this functionality has been abstracted into a reusable library, libredirect.so. It uses an environment variable $NIX_REDIRECTS containing a colon-separated list of path prefixes to be rewritten, e.g. "/foo=bar:/xyzzy=/fnord". --- .../instant-messengers/hipchat/default.nix | 13 ++- pkgs/build-support/libredirect/default.nix | 14 +++ pkgs/build-support/libredirect/libredirect.c | 104 ++++++++++++++++++ pkgs/top-level/all-packages.nix | 2 + 4 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 pkgs/build-support/libredirect/default.nix create mode 100644 pkgs/build-support/libredirect/libredirect.c diff --git a/pkgs/applications/networking/instant-messengers/hipchat/default.nix b/pkgs/applications/networking/instant-messengers/hipchat/default.nix index 857f162d7e4..6da12905f56 100644 --- a/pkgs/applications/networking/instant-messengers/hipchat/default.nix +++ b/pkgs/applications/networking/instant-messengers/hipchat/default.nix @@ -1,6 +1,7 @@ { stdenv, fetchurl, libtool, xlibs, freetype, fontconfig, openssl, glib , mesa, gstreamer, gst_plugins_base, dbus, alsaLib, zlib, libuuid -, libxml2, libxslt, sqlite, libogg, libvorbis, xz, libcanberra, makeWrapper }: +, libxml2, libxslt, sqlite, libogg, libvorbis, xz, libcanberra +, makeWrapper, libredirect, xkeyboard_config }: let @@ -67,8 +68,8 @@ stdenv.mkDerivation { buildCommand = '' tar xf ${src} - d=$out/libexec/hipchat - mkdir -p $out/libexec + mkdir -p $out/libexec/hipchat/bin + d=$out/libexec/hipchat/lib rm -rfv opt/HipChat/lib/{libstdc++*,libz*,libuuid*,libxml2*,libxslt*,libsqlite*,libogg*,libvorbis*,liblzma*,libcanberra.*,libcanberra-*} mv opt/HipChat/lib/ $d mv usr/share $out @@ -85,9 +86,11 @@ stdenv.mkDerivation { makeWrapper $d/hipchat.bin $out/bin/hipchat \ --set HIPCHAT_LD_LIBRARY_PATH '"$LD_LIBRARY_PATH"' \ - --set HIPCHAT_QT_PLUGIN_PATH '"$QT_PLUGIN_PATH"' + --set HIPCHAT_QT_PLUGIN_PATH '"$QT_PLUGIN_PATH"' \ + --set LD_PRELOAD "${libredirect}/lib/libredirect.so" \ + --set NIX_REDIRECTS /usr/share/X11/xkb=${xkeyboard_config}/share/X11/xkb - mv opt/HipChat/bin/linuxbrowserlaunch $out/bin + mv opt/HipChat/bin/linuxbrowserlaunch $out/libexec/hipchat/bin/ ''; meta = { diff --git a/pkgs/build-support/libredirect/default.nix b/pkgs/build-support/libredirect/default.nix new file mode 100644 index 00000000000..a8a497d46d7 --- /dev/null +++ b/pkgs/build-support/libredirect/default.nix @@ -0,0 +1,14 @@ +{ stdenv }: + +stdenv.mkDerivation { + name = "libredirect-0"; + + unpackPhase = "cp ${./libredirect.c} libredirect.c"; + + buildPhase = + '' + gcc -Wall -std=c99 -O3 -shared libredirect.c -o libredirect.so -fPIC -ldl + ''; + + installPhase = "mkdir -p $out/lib; cp libredirect.so $out/lib"; +} diff --git a/pkgs/build-support/libredirect/libredirect.c b/pkgs/build-support/libredirect/libredirect.c new file mode 100644 index 00000000000..4afed3add75 --- /dev/null +++ b/pkgs/build-support/libredirect/libredirect.c @@ -0,0 +1,104 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_REDIRECTS 128 + +static int nrRedirects = 0; +static char * from[MAX_REDIRECTS]; +static char * to[MAX_REDIRECTS]; + +// FIXME: might run too late. +static void init() __attribute__((constructor)); + +static void init() +{ + char * spec = getenv("NIX_REDIRECTS"); + if (!spec) return; + + unsetenv("NIX_REDIRECTS"); + + char * spec2 = malloc(strlen(spec) + 1); + strcpy(spec2, spec); + + char * pos = spec2, * eq; + while ((eq = strchr(pos, '='))) { + *eq = 0; + from[nrRedirects] = pos; + pos = eq + 1; + to[nrRedirects] = pos; + nrRedirects++; + if (nrRedirects == MAX_REDIRECTS) break; + char * end = strchr(pos, ':'); + if (!end) break; + *end = 0; + pos = end + 1; + } + +} + +static const char * rewrite(const char * path, char * buf) +{ + for (int n = 0; n < nrRedirects; ++n) { + int len = strlen(from[n]); + if (strncmp(path, from[n], len) != 0) continue; + if (snprintf(buf, PATH_MAX, "%s%s", to[n], path + len) >= PATH_MAX) + abort(); + return buf; + } + + return path; +} + +/* The following set of Glibc library functions is very incomplete - + it contains only what we needed for programs in Nixpkgs. Just add + more functions as needed. */ + +int open(const char * path, int flags, ...) +{ + int (*open_real) (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); + } + char buf[PATH_MAX]; + return open_real(rewrite(path, buf), flags, mode); +} + +int open64(const char * path, int flags, ...) +{ + int (*open64_real) (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); + } + char buf[PATH_MAX]; + return open64_real(rewrite(path, buf), flags, mode); +} + +FILE * fopen(const char * path, const char * mode) +{ + FILE * (*fopen_real) (const char *, const char *) = dlsym(RTLD_NEXT, "fopen"); + char buf[PATH_MAX]; + return fopen_real(rewrite(path, buf), mode); +} + +int __xstat(int ver, const char * path, struct stat * st) +{ + int (*__xstat_real) (int ver, const char *, struct stat *) = dlsym(RTLD_NEXT, "__xstat"); + char buf[PATH_MAX]; + return __xstat_real(ver, rewrite(path, buf), st); +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index fd4161190b6..f27efd104df 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -373,6 +373,8 @@ let inherit url; }; + libredirect = callPackage ../build-support/libredirect { }; + makeDesktopItem = import ../build-support/make-desktopitem { inherit stdenv; };