1 /* Copyright © 2013, Intel Corporation
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to deal
5 * in the Software without restriction, including without limitation the rights
6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 * copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
34 #include <sys/types.h>
40 #include <linux/limits.h>
43 glaze_lookup (char *function)
45 static void *libgl_handle = NULL;
48 if (libgl_handle == NULL) {
51 path = getenv ("GLAZE_LIBGL");
53 fprintf (stderr, "GLAZE_LIBGL unset. "
54 "Please set to path of libGL.so under glaze.\n"
59 libgl_handle = dlopen (path, RTLD_LAZY | RTLD_GLOBAL);
60 if (libgl_handle == NULL) {
61 fprintf (stderr, "glaze_lookup: Error: Failed to dlopen %s\n", path);
66 ret = dlsym (libgl_handle, function);
69 fprintf (stderr, "Error: glaze_lookup failed to dlsym %s\n",
77 /* Terminate a string representing a filename at the final '/' to
78 * eliminate the final filename component, (leaving only the directory
79 * portions of the original path).
81 * Notes: A path containing no '/' character will not be modified.
82 * A path consisting only of "/" will not be modified.
85 chop_trailing_path_component (char *path)
89 slash = strrchr (path, '/');
100 /* Find the absolute path of the currently executing binary.
102 * Returns: a string talloc'ed to 'ctx'
105 get_bin_name (void *ctx)
107 const char *link = "/proc/self/exe";
110 /* Yes, PATH_MAX is cheesy. I would have preferred to have
111 * used lstat and read the resulting st_size, but everytime I
112 * did that with /proc/self/exe I got a value of 0, (whereas
113 * with a "real" symbolic link I make myself I get the length
114 * of the filename being linked to). Go figure. */
115 int name_len = PATH_MAX + 1;
117 name = talloc_size (ctx, name_len);
119 fprintf (stderr, "Out of memory.\n");
123 name_len = readlink (link, name, name_len - 1);
125 fprintf (stderr, "Failed to readlink %s: %s\n", link,
130 name[name_len] = '\0';
135 /* Does path exist? */
137 exists (const char *path)
142 err = stat (path, &st);
144 /* Failed to stat. It either doesn't exist, or might as well not. */
151 /* Execute "program" in a pipe, reads its first line of output on
152 * stdout, and returns that as a string (discarding any further
155 * Returns NULL if the program failed to execute for any reason.
157 * NOTE: The caller should free() the returned string when done with
161 read_process_output_one_line (const char *program)
169 process = popen (program, "r");
173 bytes_read = getline (&line, &len, process);
175 status = pclose (process);
176 if (! WIFEXITED (status))
179 if (WEXITSTATUS (status))
182 if (bytes_read == -1)
186 if (line[strlen(line)-1] == '\n')
187 line[strlen(line)-1] = '\0';
195 /* Look for "wrapper" library next to currently executing binary.
197 * If "wrapper" is an absolute path, return it directly.
199 * Otherwise, ("wrapper" is relative), look for an existing file named
200 * "wrapper" in the following locations in order:
202 * 1. The current working directory
204 * 2. The same directory as the currently executing binary, (as
205 * determined by /proc/self/exe).
207 * If either of those files exist, return its path.
209 * Otherwise, return the original, relative "wrapper".
212 resolve_wrapper_path (void *ctx, const char *wrapper)
214 char *cwd_path, *bin_path, *lib_path;
216 /* Return absolute wrapper path immediately. */
220 /* Look for wrapper in current working directory. */
221 cwd_path = get_current_dir_name ();
223 lib_path = talloc_asprintf (ctx, "%s/%s", cwd_path, wrapper);
227 if (exists (lib_path))
230 talloc_free (lib_path);
232 /* Look for wrapper next to current executable. */
233 bin_path = get_bin_name (ctx);
235 chop_trailing_path_component (bin_path);
237 lib_path = talloc_asprintf (ctx, "%s/%s", bin_path, wrapper);
239 talloc_free (bin_path);
241 if (exists (lib_path))
244 talloc_free (lib_path);
246 /* Allow relative wrapper path to remain as-is. */
250 /* Return path to directory containing Glaze wrapper's libGL.so.1
251 * suitable for use in LD_PRELOAD or LD_LIBRARY_PATH. Note that the
252 * path returned may not be a full path to the directory but may end
253 * with "$LIB" which will be expanded by the Linux dynamic linker to
254 * an architecture specific string (such as "lib/i386-linux-gnu" or
255 * "lib/x86_64-linux-gnu"). */
257 find_glaze_libgl_dir (void)
259 return CONFIG_LIBDIR "/glaze/$LIB";
263 glaze_execute (int argc, char *argv[], const char *wrapper)
265 void *ctx = talloc_new (NULL);
268 /* Set GLAZE_WRAPPER to absolute path of wrapper library */
269 if (wrapper == NULL || *wrapper == '\0') {
270 fprintf (stderr, "Error: glaze_execute called with empty wrapper library.\n");
274 wrapper = resolve_wrapper_path (ctx, wrapper);
276 setenv ("GLAZE_WRAPPER", wrapper, 1);
278 /* Ensure GLAZE_LIBGL is set. If not, set GLAZE_LIBGL_32_AUTO
279 * and GLAZE_LIBGL_64_AUTO
281 * Note that we must do this before setting LD_LIBRARY_PATH,
282 * since after that, of course we would find Glaze's wrapper
284 if (getenv ("GLAZE_LIBGL") == NULL) {
287 libgl_path = read_process_output_one_line ("glaze-find-libgl-32");
289 setenv ("GLAZE_LIBGL_32_AUTO", libgl_path, 1);
293 libgl_path = read_process_output_one_line ("glaze-find-libgl-64");
295 setenv ("GLAZE_LIBGL_64_AUTO", libgl_path, 1);
300 /* Set LD_LIBRARY_PATH to include glaze's own libGL.so */
301 const char *glaze_libgl_dir, *ld_library_path;
303 glaze_libgl_dir = find_glaze_libgl_dir ();
305 ld_library_path = getenv ("LD_LIBRARY_PATH");
307 if (ld_library_path == NULL)
308 ld_library_path = glaze_libgl_dir;
310 ld_library_path = talloc_asprintf (ctx, "%s:%s",
314 setenv ("LD_LIBRARY_PATH", ld_library_path, 1);
318 /* Execute program */
319 execvp (argv[0], argv);
321 /* If execvp returns, something went wrong. */
322 fprintf (stderr, "Error: Failed to exec:");
323 for (i = 0; i < argc; i++)
324 fprintf (stderr, " %s", argv[i]);
325 fprintf (stderr, "\n");
331 glaze_set_first_gl_call_callback (const char *function_name)
333 setenv ("GLAZE_FIRST_GL_CALL_CALLBACK", function_name, 1);