diff options
author | Mx Kookie <kookie@spacekookie.de> | 2020-10-31 19:35:09 +0100 |
---|---|---|
committer | Mx Kookie <kookie@spacekookie.de> | 2020-10-31 19:35:09 +0100 |
commit | c4625b175f8200f643fd6e11010932ea44c78433 (patch) | |
tree | bce3f89888c8ac3991fa5569a878a9eab6801ccc /infra/libkookie/nixpkgs/pkgs/development/tools/misc/saleae-logic | |
parent | 49f735974dd103039ddc4cb576bb76555164a9e7 (diff) | |
parent | d661aa56a8843e991261510c1bb28fdc2f6975ae (diff) |
Add 'infra/libkookie/' from commit 'd661aa56a8843e991261510c1bb28fdc2f6975ae'
git-subtree-dir: infra/libkookie
git-subtree-mainline: 49f735974dd103039ddc4cb576bb76555164a9e7
git-subtree-split: d661aa56a8843e991261510c1bb28fdc2f6975ae
Diffstat (limited to 'infra/libkookie/nixpkgs/pkgs/development/tools/misc/saleae-logic')
-rw-r--r-- | infra/libkookie/nixpkgs/pkgs/development/tools/misc/saleae-logic/default.nix | 97 | ||||
-rw-r--r-- | infra/libkookie/nixpkgs/pkgs/development/tools/misc/saleae-logic/preload.c | 341 |
2 files changed, 438 insertions, 0 deletions
diff --git a/infra/libkookie/nixpkgs/pkgs/development/tools/misc/saleae-logic/default.nix b/infra/libkookie/nixpkgs/pkgs/development/tools/misc/saleae-logic/default.nix new file mode 100644 index 000000000000..b9f84edc964a --- /dev/null +++ b/infra/libkookie/nixpkgs/pkgs/development/tools/misc/saleae-logic/default.nix @@ -0,0 +1,97 @@ +# Saleae logic analyzer software +# +# Suggested udev rules to be able to access the Logic device without being root: +# SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="0925", ATTR{idProduct}=="3881", MODE="0666" +# SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="21a9", ATTR{idProduct}=="1001", MODE="0666" +# +# In NixOS, simply add this package to services.udev.packages. + +{ stdenv, fetchurl, unzip, glib, libSM, libICE, gtk2, libXext, libXft +, fontconfig, libXrender, libXfixes, libX11, libXi, libXrandr, libXcursor +, freetype, libXinerama, libxcb, zlib, pciutils +, makeDesktopItem, xkeyboardconfig, dbus, runtimeShell, libGL +}: + +let + + libPath = stdenv.lib.makeLibraryPath [ + glib libSM libICE gtk2 libXext libXft fontconfig libXrender libXfixes libX11 + libXi libXrandr libXcursor freetype libXinerama libxcb zlib stdenv.cc.cc.lib + dbus libGL + ]; + +in + +assert stdenv.hostPlatform.system == "x86_64-linux"; + +stdenv.mkDerivation rec { + pname = "saleae-logic"; + version = "1.2.18"; + + src = fetchurl { + name = "saleae-logic-${version}-64bit.zip"; + url = "http://downloads.saleae.com/logic/${version}/Logic%20${version}%20(64-bit).zip"; + sha256 = "0lhair2vsg8sjvzicvfcjfmvy30q7i01xj4z02iqh7pgzpb025h8"; + }; + + desktopItem = makeDesktopItem { + name = "saleae-logic"; + exec = "saleae-logic"; + icon = ""; # the package contains no icon + comment = "Software for Saleae logic analyzers"; + desktopName = "Saleae Logic"; + genericName = "Logic analyzer"; + categories = "Development"; + }; + + buildInputs = [ unzip ]; + + installPhase = '' + # Copy prebuilt app to $out + mkdir "$out" + cp -r * "$out" + + # Patch it + patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" "$out/Logic" + for bin in "$out/Logic" \ + "$out/libQt5Widgets.so.5" \ + "$out/libQt5Gui.so.5" \ + "$out/libQt5Core.so.5" \ + "$out/libQt5Network.so.5" ; do + patchelf --set-rpath "${stdenv.cc.cc.lib}/lib:${stdenv.cc.cc.lib}/lib64:${libPath}:\$ORIGIN/Analyzers:\$ORIGIN" "$bin" + done + + patchelf --set-rpath "${stdenv.cc.cc.lib}/lib:${stdenv.cc.cc.lib}/lib64:${libPath}:\$ORIGIN/../" "$out/platforms/libqxcb.so" + + # Build the LD_PRELOAD library that makes Logic work from a read-only directory + mkdir -p "$out/lib" + gcc -shared -fPIC -DOUT=\"$out\" "${./preload.c}" -o "$out/lib/preload.so" -ldl + + # Make wrapper script that uses the LD_PRELOAD library + mkdir -p "$out/bin" + cat > "$out/bin/saleae-logic" << EOF + #!${runtimeShell} + export LD_PRELOAD="$out/lib/preload.so" + export QT_XKB_CONFIG_ROOT="${xkeyboardconfig}/share/X11/xkb" + export PATH="${pciutils}/bin:\$PATH" + exec "$out/Logic" "\$@" + EOF + chmod a+x "$out"/bin/saleae-logic + + # Copy the generated .desktop file + mkdir -p "$out/share/applications" + cp "$desktopItem"/share/applications/* "$out/share/applications/" + + # Install provided udev rules + mkdir -p "$out/etc/udev/rules.d" + cp Drivers/99-SaleaeLogic.rules "$out/etc/udev/rules.d/" + ''; + + meta = with stdenv.lib; { + description = "Software for Saleae logic analyzers"; + homepage = "https://www.saleae.com/"; + license = licenses.unfree; + platforms = platforms.linux; + maintainers = [ maintainers.bjornfor ]; + }; +} diff --git a/infra/libkookie/nixpkgs/pkgs/development/tools/misc/saleae-logic/preload.c b/infra/libkookie/nixpkgs/pkgs/development/tools/misc/saleae-logic/preload.c new file mode 100644 index 000000000000..b4451e4d99f7 --- /dev/null +++ b/infra/libkookie/nixpkgs/pkgs/development/tools/misc/saleae-logic/preload.c @@ -0,0 +1,341 @@ +/* + * LD_PRELOAD trick to make Saleae Logic work from a read-only installation + * directory. + * + * Saleae Logic tries to write to a few directories inside its installation + * directory. Because the Nix store is read-only, we have to redirect access to + * this file somewhere else. Here's the map: + * + * $out/Settings/ => $HOME/.saleae-logic/Settings/ + * $out/Databases/ => $HOME/.saleae-logic/Databases/ + * $out/Errors/ => $HOME/.saleae-logic/Errors/ + * $out/Calibration/ => $HOME/.saleae-logic/Calibration/ + * + * This also makes the software multi-user aware :-) + * + * NOTE: AFAIK (Bjørn Forsman), Logic version 1.2+ was supposed to have a + * command line parameter for redirecting these write operations, but + * apparently that feature got postponed. + * + * Usage: + * gcc -shared -fPIC -DOUT="$out" preload.c -o preload.so -ldl + * LD_PRELOAD=$PWD/preload.so ./result/Logic + * + * To see the paths that are modified at runtime, set the environment variable + * PRELOAD_DEBUG to 1 (or anything really; debugging is on as long as the + * variable exists). + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <dlfcn.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> + +#ifndef OUT +#error Missing OUT define - path to the installation directory. +#endif + +/* + * TODO: How to properly wrap "open", which is declared as a variadic function + * in glibc? The man page lists these signatures: + * + * int open(const char *pathname, int flags); + * int open(const char *pathname, int flags, mode_t mode); + * + * But using either signature in this code cause a compile error, because + * glibc has declared the function as "int open(const char *, int, ...)". + * Same thing with "openat". + * + * For now we discard the variadic args. It seems to work. + * + * Relevant: + * http://stackoverflow.com/questions/28462523/how-to-wrap-ioctlint-d-unsigned-long-request-using-ld-preload + */ +typedef FILE *(*fopen_func_t)(const char *path, const char *mode); +typedef FILE *(*fopen64_func_t)(const char *path, const char *mode); +typedef int (*open_func_t)(const char *pathname, int flags, ...); +typedef int (*open64_func_t)(const char *pathname, int flags, ...); +typedef int (*openat_func_t)(int dirfd, const char *pathname, int flags, ...); +typedef int (*openat64_func_t)(int dirfd, const char *pathname, int flags, ...); +typedef int (*xstat_func_t)(int vers, const char *pathname, struct stat *buf); +typedef int (*xstat64_func_t)(int vers, const char *pathname, struct stat64 *buf); +typedef int (*access_func_t)(const char *pathname, int mode); +typedef int (*faccessat_func_t)(int dirfd, const char *pathname, int mode, int flags); +typedef int (*unlink_func_t)(const char *pathname); + +/* + * Redirect $out/{Settings,Databases,Errors,Calibration}/ => $HOME/.saleae-logic/. + * Path is truncated if bigger than PATH_MAX. + * + * @param pathname Original file path. + * @param buffer Pointer to a buffer of size PATH_MAX bytes that this function + * will write the new redirected path to (if needed). + * + * @return Pointer to the resulting path. It will either be equal to the + * pathname (no redirect) or buffer argument (was redirected). + */ +static const char *redirect(const char *pathname, char *buffer) +{ + const char *homepath; + const char *new_path; + static char have_warned; + const char *remainder; + static char have_initialized; + static size_t out_strlen; + static size_t settings_strlen; + static size_t databases_strlen; + static size_t errors_strlen; + static size_t calibration_strlen; + int ret; + int i; + + homepath = getenv("HOME"); + if (!homepath) { + homepath = "/"; + if (!have_warned && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: WARNING: HOME is unset, using \"/\" (root) instead.\n"); + have_warned = 1; + } + } + + if (!have_initialized) { + /* + * The directories that Saleae Logic expects to find. + * The first element is intentionally empty (create base dir). + */ + char *dirs[] = {"", "/Settings", "/Databases", "/Errors", "/Calibration"}; + char old_settings_path[PATH_MAX]; + access_func_t orig_access; + + out_strlen = strlen(OUT); + settings_strlen = out_strlen + strlen("/Settings"); + databases_strlen = out_strlen + strlen("/Databases"); + errors_strlen = out_strlen + strlen("/Errors"); + calibration_strlen = out_strlen + strlen("/Calibration"); + for (i = 0; i < sizeof dirs / sizeof dirs[0]; i++) { + snprintf(buffer, PATH_MAX, "%s/.saleae-logic%s", homepath, dirs[i]); + buffer[PATH_MAX-1] = '\0'; + ret = mkdir(buffer, 0755); + if (0 != ret && errno != EEXIST) { + fprintf(stderr, "ERROR: Failed to create directory \"%s\": %s\n", + buffer, strerror(errno)); + return NULL; + } + } + + /* + * Automatic migration of the settings file: + * ~/.saleae-logic-settings.xml => ~/.saleae-logic/Settings/settings.xml + */ + snprintf(old_settings_path, PATH_MAX, "%s/.saleae-logic-settings.xml", homepath); + old_settings_path[PATH_MAX-1] = '\0'; + orig_access = (access_func_t)dlsym(RTLD_NEXT, "access"); + if (orig_access(old_settings_path, F_OK) == 0) { + snprintf(buffer, PATH_MAX, "%s/.saleae-logic/Settings/settings.xml", homepath); + buffer[PATH_MAX-1] = '\0'; + ret = rename(old_settings_path, buffer); + if (ret != 0) { + fprintf(stderr, "WARN: Failed to move %s to %s", + old_settings_path, buffer); + } + } + + have_initialized = 1; + } + + new_path = pathname; + remainder = pathname + out_strlen; + + if ((strncmp(OUT "/Settings", pathname, settings_strlen) == 0) || + (strncmp(OUT "/Databases", pathname, databases_strlen) == 0) || + (strncmp(OUT "/Errors", pathname, errors_strlen) == 0) || + (strncmp(OUT "/Calibration", pathname, calibration_strlen) == 0)) { + snprintf(buffer, PATH_MAX, "%s/.saleae-logic%s", homepath, remainder); + buffer[PATH_MAX-1] = '\0'; + new_path = buffer; + } + + return new_path; +} + +FILE *fopen(const char *pathname, const char *mode) +{ + const char *path; + char buffer[PATH_MAX]; + fopen_func_t orig_fopen; + + orig_fopen = (fopen_func_t)dlsym(RTLD_NEXT, "fopen"); + path = redirect(pathname, buffer); + if (path != pathname && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: fopen(\"%s\", \"%s\") => \"%s\"\n", pathname, mode, path); + } + + return orig_fopen(path, mode); +} + +FILE *fopen64(const char *pathname, const char *mode) +{ + const char *path; + char buffer[PATH_MAX]; + fopen64_func_t orig_fopen64; + + orig_fopen64 = (fopen64_func_t)dlsym(RTLD_NEXT, "fopen64"); + path = redirect(pathname, buffer); + if (path != pathname && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: fopen64(\"%s\", \"%s\") => \"%s\"\n", pathname, mode, path); + } + + return orig_fopen64(path, mode); +} + +int open(const char *pathname, int flags, ...) +{ + const char *path; + char buffer[PATH_MAX]; + open_func_t orig_open; + + orig_open = (open_func_t)dlsym(RTLD_NEXT, "open"); + path = redirect(pathname, buffer); + if (path != pathname && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: open(\"%s\", ...) => \"%s\"\n", pathname, path); + } + + return orig_open(path, flags); +} + +int open64(const char *pathname, int flags, ...) +{ + const char *path; + char buffer[PATH_MAX]; + open64_func_t orig_open64; + + orig_open64 = (open64_func_t)dlsym(RTLD_NEXT, "open64"); + path = redirect(pathname, buffer); + if (path != pathname && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: open64(\"%s\", ...) => \"%s\"\n", pathname, path); + } + + return orig_open64(path, flags); +} + +int openat(int dirfd, const char *pathname, int flags, ...) +{ + const char *path; + char buffer[PATH_MAX]; + openat_func_t orig_openat; + + orig_openat = (openat_func_t)dlsym(RTLD_NEXT, "openat"); + path = redirect(pathname, buffer); + if (path != pathname && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: openat(%d, \"%s\", %#x) => \"%s\"\n", dirfd, pathname, flags, path); + } + + return orig_openat(dirfd, path, flags); +} + +int openat64(int dirfd, const char *pathname, int flags, ...) +{ + const char *path; + char buffer[PATH_MAX]; + openat64_func_t orig_openat64; + + orig_openat64 = (openat64_func_t)dlsym(RTLD_NEXT, "openat64"); + path = redirect(pathname, buffer); + if (path != pathname && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: openat64(%d, \"%s\", %#x) => \"%s\"\n", dirfd, pathname, flags, path); + } + + return orig_openat64(dirfd, path, flags); +} + +/* + * Notes about "stat". + * + * The stat function is special, at least in glibc, in that it cannot be + * directly overridden by LD_PRELOAD, due to it being inline wrapper around + * __xstat. The __xstat functions take one extra parameter, a version number, + * to indicate what "struct stat" should look like. This trick allows changing + * the contents of mode_t without changing the shared library major number. See + * sys/stat.h header for more info. + */ +int __xstat(int vers, const char *pathname, struct stat *buf) +{ + const char *path; + char buffer[PATH_MAX]; + xstat_func_t orig_xstat; + + orig_xstat = (xstat_func_t)dlsym(RTLD_NEXT, "__xstat"); + path = redirect(pathname, buffer); + if (path != pathname && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: (__x)stat(\"%s\", ...) => \"%s\"\n", pathname, path); + } + + return orig_xstat(vers, path, buf); +} + +int __xstat64(int vers, const char *pathname, struct stat64 *buf) +{ + const char *path; + char buffer[PATH_MAX]; + xstat64_func_t orig_xstat64; + + orig_xstat64 = (xstat64_func_t)dlsym(RTLD_NEXT, "__xstat64"); + path = redirect(pathname, buffer); + if (path != pathname && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: (__x)stat64(\"%s\", ...) => \"%s\"\n", pathname, path); + } + + return orig_xstat64(vers, path, buf); +} + +int access(const char *pathname, int mode) +{ + const char *path; + char buffer[PATH_MAX]; + access_func_t orig_access; + + orig_access = (access_func_t)dlsym(RTLD_NEXT, "access"); + path = redirect(pathname, buffer); + if (path != pathname && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: access(\"%s\", ...) => \"%s\"\n", pathname, path); + } + + return orig_access(path, mode); +} + +int faccessat(int dirfd, const char *pathname, int mode, int flags) +{ + const char *path; + char buffer[PATH_MAX]; + faccessat_func_t orig_faccessat; + + orig_faccessat = (faccessat_func_t)dlsym(RTLD_NEXT, "faccessat"); + path = redirect(pathname, buffer); + if (path != pathname && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: faccessat(\"%s\", ...) => \"%s\"\n", pathname, path); + } + + return orig_faccessat(dirfd, path, mode, flags); +} + +int unlink(const char *pathname) +{ + const char *path; + char buffer[PATH_MAX]; + unlink_func_t orig_unlink; + + orig_unlink = (unlink_func_t)dlsym(RTLD_NEXT, "unlink"); + path = redirect(pathname, buffer); + if (path != pathname && getenv("PRELOAD_DEBUG")) { + fprintf(stderr, "preload_debug: unlink(\"%s\") => \"%s\"\n", pathname, path); + } + + return orig_unlink(path); +} |