biglybt: 3.5.0.0 -> 3.6.0.0
[NixPkgs.git] / pkgs / development / tools / misc / saleae-logic / preload.c
blobb4451e4d99f7356f63fe9211b6a8efbc58f85e90
1 /*
2 * LD_PRELOAD trick to make Saleae Logic work from a read-only installation
3 * directory.
5 * Saleae Logic tries to write to a few directories inside its installation
6 * directory. Because the Nix store is read-only, we have to redirect access to
7 * this file somewhere else. Here's the map:
9 * $out/Settings/ => $HOME/.saleae-logic/Settings/
10 * $out/Databases/ => $HOME/.saleae-logic/Databases/
11 * $out/Errors/ => $HOME/.saleae-logic/Errors/
12 * $out/Calibration/ => $HOME/.saleae-logic/Calibration/
14 * This also makes the software multi-user aware :-)
16 * NOTE: AFAIK (Bjørn Forsman), Logic version 1.2+ was supposed to have a
17 * command line parameter for redirecting these write operations, but
18 * apparently that feature got postponed.
20 * Usage:
21 * gcc -shared -fPIC -DOUT="$out" preload.c -o preload.so -ldl
22 * LD_PRELOAD=$PWD/preload.so ./result/Logic
24 * To see the paths that are modified at runtime, set the environment variable
25 * PRELOAD_DEBUG to 1 (or anything really; debugging is on as long as the
26 * variable exists).
29 #define _GNU_SOURCE
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <dlfcn.h>
36 #include <limits.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <errno.h>
41 #ifndef OUT
42 #error Missing OUT define - path to the installation directory.
43 #endif
46 * TODO: How to properly wrap "open", which is declared as a variadic function
47 * in glibc? The man page lists these signatures:
49 * int open(const char *pathname, int flags);
50 * int open(const char *pathname, int flags, mode_t mode);
52 * But using either signature in this code cause a compile error, because
53 * glibc has declared the function as "int open(const char *, int, ...)".
54 * Same thing with "openat".
56 * For now we discard the variadic args. It seems to work.
58 * Relevant:
59 * http://stackoverflow.com/questions/28462523/how-to-wrap-ioctlint-d-unsigned-long-request-using-ld-preload
61 typedef FILE *(*fopen_func_t)(const char *path, const char *mode);
62 typedef FILE *(*fopen64_func_t)(const char *path, const char *mode);
63 typedef int (*open_func_t)(const char *pathname, int flags, ...);
64 typedef int (*open64_func_t)(const char *pathname, int flags, ...);
65 typedef int (*openat_func_t)(int dirfd, const char *pathname, int flags, ...);
66 typedef int (*openat64_func_t)(int dirfd, const char *pathname, int flags, ...);
67 typedef int (*xstat_func_t)(int vers, const char *pathname, struct stat *buf);
68 typedef int (*xstat64_func_t)(int vers, const char *pathname, struct stat64 *buf);
69 typedef int (*access_func_t)(const char *pathname, int mode);
70 typedef int (*faccessat_func_t)(int dirfd, const char *pathname, int mode, int flags);
71 typedef int (*unlink_func_t)(const char *pathname);
74 * Redirect $out/{Settings,Databases,Errors,Calibration}/ => $HOME/.saleae-logic/.
75 * Path is truncated if bigger than PATH_MAX.
77 * @param pathname Original file path.
78 * @param buffer Pointer to a buffer of size PATH_MAX bytes that this function
79 * will write the new redirected path to (if needed).
81 * @return Pointer to the resulting path. It will either be equal to the
82 * pathname (no redirect) or buffer argument (was redirected).
84 static const char *redirect(const char *pathname, char *buffer)
86 const char *homepath;
87 const char *new_path;
88 static char have_warned;
89 const char *remainder;
90 static char have_initialized;
91 static size_t out_strlen;
92 static size_t settings_strlen;
93 static size_t databases_strlen;
94 static size_t errors_strlen;
95 static size_t calibration_strlen;
96 int ret;
97 int i;
99 homepath = getenv("HOME");
100 if (!homepath) {
101 homepath = "/";
102 if (!have_warned && getenv("PRELOAD_DEBUG")) {
103 fprintf(stderr, "preload_debug: WARNING: HOME is unset, using \"/\" (root) instead.\n");
104 have_warned = 1;
108 if (!have_initialized) {
110 * The directories that Saleae Logic expects to find.
111 * The first element is intentionally empty (create base dir).
113 char *dirs[] = {"", "/Settings", "/Databases", "/Errors", "/Calibration"};
114 char old_settings_path[PATH_MAX];
115 access_func_t orig_access;
117 out_strlen = strlen(OUT);
118 settings_strlen = out_strlen + strlen("/Settings");
119 databases_strlen = out_strlen + strlen("/Databases");
120 errors_strlen = out_strlen + strlen("/Errors");
121 calibration_strlen = out_strlen + strlen("/Calibration");
122 for (i = 0; i < sizeof dirs / sizeof dirs[0]; i++) {
123 snprintf(buffer, PATH_MAX, "%s/.saleae-logic%s", homepath, dirs[i]);
124 buffer[PATH_MAX-1] = '\0';
125 ret = mkdir(buffer, 0755);
126 if (0 != ret && errno != EEXIST) {
127 fprintf(stderr, "ERROR: Failed to create directory \"%s\": %s\n",
128 buffer, strerror(errno));
129 return NULL;
134 * Automatic migration of the settings file:
135 * ~/.saleae-logic-settings.xml => ~/.saleae-logic/Settings/settings.xml
137 snprintf(old_settings_path, PATH_MAX, "%s/.saleae-logic-settings.xml", homepath);
138 old_settings_path[PATH_MAX-1] = '\0';
139 orig_access = (access_func_t)dlsym(RTLD_NEXT, "access");
140 if (orig_access(old_settings_path, F_OK) == 0) {
141 snprintf(buffer, PATH_MAX, "%s/.saleae-logic/Settings/settings.xml", homepath);
142 buffer[PATH_MAX-1] = '\0';
143 ret = rename(old_settings_path, buffer);
144 if (ret != 0) {
145 fprintf(stderr, "WARN: Failed to move %s to %s",
146 old_settings_path, buffer);
150 have_initialized = 1;
153 new_path = pathname;
154 remainder = pathname + out_strlen;
156 if ((strncmp(OUT "/Settings", pathname, settings_strlen) == 0) ||
157 (strncmp(OUT "/Databases", pathname, databases_strlen) == 0) ||
158 (strncmp(OUT "/Errors", pathname, errors_strlen) == 0) ||
159 (strncmp(OUT "/Calibration", pathname, calibration_strlen) == 0)) {
160 snprintf(buffer, PATH_MAX, "%s/.saleae-logic%s", homepath, remainder);
161 buffer[PATH_MAX-1] = '\0';
162 new_path = buffer;
165 return new_path;
168 FILE *fopen(const char *pathname, const char *mode)
170 const char *path;
171 char buffer[PATH_MAX];
172 fopen_func_t orig_fopen;
174 orig_fopen = (fopen_func_t)dlsym(RTLD_NEXT, "fopen");
175 path = redirect(pathname, buffer);
176 if (path != pathname && getenv("PRELOAD_DEBUG")) {
177 fprintf(stderr, "preload_debug: fopen(\"%s\", \"%s\") => \"%s\"\n", pathname, mode, path);
180 return orig_fopen(path, mode);
183 FILE *fopen64(const char *pathname, const char *mode)
185 const char *path;
186 char buffer[PATH_MAX];
187 fopen64_func_t orig_fopen64;
189 orig_fopen64 = (fopen64_func_t)dlsym(RTLD_NEXT, "fopen64");
190 path = redirect(pathname, buffer);
191 if (path != pathname && getenv("PRELOAD_DEBUG")) {
192 fprintf(stderr, "preload_debug: fopen64(\"%s\", \"%s\") => \"%s\"\n", pathname, mode, path);
195 return orig_fopen64(path, mode);
198 int open(const char *pathname, int flags, ...)
200 const char *path;
201 char buffer[PATH_MAX];
202 open_func_t orig_open;
204 orig_open = (open_func_t)dlsym(RTLD_NEXT, "open");
205 path = redirect(pathname, buffer);
206 if (path != pathname && getenv("PRELOAD_DEBUG")) {
207 fprintf(stderr, "preload_debug: open(\"%s\", ...) => \"%s\"\n", pathname, path);
210 return orig_open(path, flags);
213 int open64(const char *pathname, int flags, ...)
215 const char *path;
216 char buffer[PATH_MAX];
217 open64_func_t orig_open64;
219 orig_open64 = (open64_func_t)dlsym(RTLD_NEXT, "open64");
220 path = redirect(pathname, buffer);
221 if (path != pathname && getenv("PRELOAD_DEBUG")) {
222 fprintf(stderr, "preload_debug: open64(\"%s\", ...) => \"%s\"\n", pathname, path);
225 return orig_open64(path, flags);
228 int openat(int dirfd, const char *pathname, int flags, ...)
230 const char *path;
231 char buffer[PATH_MAX];
232 openat_func_t orig_openat;
234 orig_openat = (openat_func_t)dlsym(RTLD_NEXT, "openat");
235 path = redirect(pathname, buffer);
236 if (path != pathname && getenv("PRELOAD_DEBUG")) {
237 fprintf(stderr, "preload_debug: openat(%d, \"%s\", %#x) => \"%s\"\n", dirfd, pathname, flags, path);
240 return orig_openat(dirfd, path, flags);
243 int openat64(int dirfd, const char *pathname, int flags, ...)
245 const char *path;
246 char buffer[PATH_MAX];
247 openat64_func_t orig_openat64;
249 orig_openat64 = (openat64_func_t)dlsym(RTLD_NEXT, "openat64");
250 path = redirect(pathname, buffer);
251 if (path != pathname && getenv("PRELOAD_DEBUG")) {
252 fprintf(stderr, "preload_debug: openat64(%d, \"%s\", %#x) => \"%s\"\n", dirfd, pathname, flags, path);
255 return orig_openat64(dirfd, path, flags);
259 * Notes about "stat".
261 * The stat function is special, at least in glibc, in that it cannot be
262 * directly overridden by LD_PRELOAD, due to it being inline wrapper around
263 * __xstat. The __xstat functions take one extra parameter, a version number,
264 * to indicate what "struct stat" should look like. This trick allows changing
265 * the contents of mode_t without changing the shared library major number. See
266 * sys/stat.h header for more info.
268 int __xstat(int vers, const char *pathname, struct stat *buf)
270 const char *path;
271 char buffer[PATH_MAX];
272 xstat_func_t orig_xstat;
274 orig_xstat = (xstat_func_t)dlsym(RTLD_NEXT, "__xstat");
275 path = redirect(pathname, buffer);
276 if (path != pathname && getenv("PRELOAD_DEBUG")) {
277 fprintf(stderr, "preload_debug: (__x)stat(\"%s\", ...) => \"%s\"\n", pathname, path);
280 return orig_xstat(vers, path, buf);
283 int __xstat64(int vers, const char *pathname, struct stat64 *buf)
285 const char *path;
286 char buffer[PATH_MAX];
287 xstat64_func_t orig_xstat64;
289 orig_xstat64 = (xstat64_func_t)dlsym(RTLD_NEXT, "__xstat64");
290 path = redirect(pathname, buffer);
291 if (path != pathname && getenv("PRELOAD_DEBUG")) {
292 fprintf(stderr, "preload_debug: (__x)stat64(\"%s\", ...) => \"%s\"\n", pathname, path);
295 return orig_xstat64(vers, path, buf);
298 int access(const char *pathname, int mode)
300 const char *path;
301 char buffer[PATH_MAX];
302 access_func_t orig_access;
304 orig_access = (access_func_t)dlsym(RTLD_NEXT, "access");
305 path = redirect(pathname, buffer);
306 if (path != pathname && getenv("PRELOAD_DEBUG")) {
307 fprintf(stderr, "preload_debug: access(\"%s\", ...) => \"%s\"\n", pathname, path);
310 return orig_access(path, mode);
313 int faccessat(int dirfd, const char *pathname, int mode, int flags)
315 const char *path;
316 char buffer[PATH_MAX];
317 faccessat_func_t orig_faccessat;
319 orig_faccessat = (faccessat_func_t)dlsym(RTLD_NEXT, "faccessat");
320 path = redirect(pathname, buffer);
321 if (path != pathname && getenv("PRELOAD_DEBUG")) {
322 fprintf(stderr, "preload_debug: faccessat(\"%s\", ...) => \"%s\"\n", pathname, path);
325 return orig_faccessat(dirfd, path, mode, flags);
328 int unlink(const char *pathname)
330 const char *path;
331 char buffer[PATH_MAX];
332 unlink_func_t orig_unlink;
334 orig_unlink = (unlink_func_t)dlsym(RTLD_NEXT, "unlink");
335 path = redirect(pathname, buffer);
336 if (path != pathname && getenv("PRELOAD_DEBUG")) {
337 fprintf(stderr, "preload_debug: unlink(\"%s\") => \"%s\"\n", pathname, path);
340 return orig_unlink(path);