Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at devShellTools-shell 134 lines 3.9 kB view raw
1/* 2 * LD_PRELOAD trick to make Eagle (schematic editor and PCB layout tool from 3 * CadSoft) work from a read-only installation directory. 4 * 5 * When Eagle starts, it looks for the license file in <eagle>/bin/eagle.key 6 * (where <eagle> is the install path). If eagle.key is not found, Eagle checks 7 * for write access to <eagle>/bin/, shows a license dialog to the user and 8 * then attempts to write a license file to <eagle>/bin/. 9 * 10 * This will of course fail when Eagle is installed in the read-only Nix store. 11 * Hence this library that redirects accesses to the those paths in the 12 * following way: 13 * 14 * <eagle>/bin => $HOME 15 * <eagle>/bin/eagle.key => $HOME/.eagle.key 16 * 17 * Also, if copying an example project to ~/eagle/ (in the Eagle GUI), Eagle 18 * chmod's the destination with read-only permission bits (presumably because 19 * the source is read-only) and fails to complete the copy operation. 20 * Therefore, the mode argument in calls to chmod() is OR'ed with the S_IWUSR 21 * bit (write by owner). 22 * 23 * Usage: 24 * gcc -shared -fPIC -DEAGLE_PATH="$out/eagle-${version}" eagle_fixer.c -o eagle_fixer.so -ldl 25 * LD_PRELOAD=$PWD/eagle_fixer.so ./result/bin/eagle 26 * 27 * To see the paths that are modified at runtime, set the environment variable 28 * EAGLE_FIXER_DEBUG to 1. 29 */ 30 31#define _GNU_SOURCE 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <unistd.h> 36#include <fcntl.h> 37#include <dlfcn.h> 38#include <limits.h> 39#include <sys/stat.h> 40 41#ifndef EAGLE_PATH 42#error Missing EAGLE_PATH, path to the eagle-${version} installation directory. 43#endif 44 45typedef FILE *(*fopen_func_t)(const char *path, const char *mode); 46typedef int (*access_func_t)(const char *pathname, int mode); 47typedef int (*chmod_func_t)(const char *path, mode_t mode); 48 49/* 50 * Map <eagle>/bin to $HOME and <eagle>/bin/eagle.key to $HOME/.eagle.key 51 * 52 * Path is truncated if bigger than PATH_MAX. It's not threadsafe, but that's 53 * OK. 54 */ 55static const char *redirect(const char *pathname) 56{ 57 static char buffer[PATH_MAX]; 58 const char *homepath; 59 const char *new_path; 60 static int have_warned; 61 62 homepath = getenv("HOME"); 63 if (!homepath) { 64 homepath = "/"; 65 if (!have_warned && getenv("EAGLE_FIXER_DEBUG")) { 66 fprintf(stderr, "eagle_fixer: HOME is unset, using \"/\" (root) instead.\n"); 67 have_warned = 1; 68 } 69 } 70 71 new_path = pathname; 72 if (strcmp(EAGLE_PATH "/bin", pathname) == 0) { 73 /* redirect to $HOME */ 74 new_path = homepath; 75 } else if (strcmp(EAGLE_PATH "/bin/eagle.key", pathname) == 0) { 76 /* redirect to $HOME/.eagle.key */ 77 snprintf(buffer, PATH_MAX, "%s/.eagle.key", homepath); 78 buffer[PATH_MAX-1] = '\0'; 79 new_path = buffer; 80 } 81 82 return new_path; 83} 84 85FILE *fopen(const char *pathname, const char *mode) 86{ 87 FILE *fp; 88 const char *path; 89 fopen_func_t orig_fopen; 90 91 orig_fopen = (fopen_func_t)dlsym(RTLD_NEXT, "fopen"); 92 path = redirect(pathname); 93 fp = orig_fopen(path, mode); 94 95 if (path != pathname && getenv("EAGLE_FIXER_DEBUG")) { 96 fprintf(stderr, "eagle_fixer: fopen(\"%s\", \"%s\") => \"%s\": fp=%p\n", pathname, mode, path, fp); 97 } 98 99 return fp; 100} 101 102int access(const char *pathname, int mode) 103{ 104 int ret; 105 const char *path; 106 access_func_t orig_access; 107 108 orig_access = (access_func_t)dlsym(RTLD_NEXT, "access"); 109 path = redirect(pathname); 110 ret = orig_access(path, mode); 111 112 if (path != pathname && getenv("EAGLE_FIXER_DEBUG")) { 113 fprintf(stderr, "eagle_fixer: access(\"%s\", %d) => \"%s\": ret=%d\n", pathname, mode, path, ret); 114 } 115 116 return ret; 117} 118 119int chmod(const char *pathname, mode_t mode) 120{ 121 int ret; 122 mode_t new_mode; 123 chmod_func_t orig_chmod; 124 125 orig_chmod = (chmod_func_t)dlsym(RTLD_NEXT, "chmod"); 126 new_mode = mode | S_IWUSR; 127 ret = orig_chmod(pathname, new_mode); 128 129 if (getenv("EAGLE_FIXER_DEBUG")) { 130 fprintf(stderr, "eagle_fixer: chmod(\"%s\", %o) => %o: ret=%d\n", pathname, mode, new_mode, ret); 131 } 132 133 return ret; 134}