GnuCash  5.6-150-g038405b370+
binreloc.c
1 /*
2  * BinReloc - a library for creating relocatable executables
3  * Written by: Hongli Lai <h.lai@chello.nl>
4  * http://autopackage.org/
5  *
6  * This source code is public domain. You can relicense this code
7  * under whatever license you want.
8  *
9  * See http://autopackage.org/docs/binreloc/ for
10  * more information and how to use this.
11  */
12 /********************************************************************\
13  * This program is free software; you can redistribute it and/or *
14  * modify it under the terms of the GNU General Public License as *
15  * published by the Free Software Foundation; either version 2 of *
16  * the License, or (at your option) any later version. *
17  * *
18  * This program is distributed in the hope that it will be useful, *
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
21  * GNU General Public License for more details. *
22  * *
23  * You should have received a copy of the GNU General Public License*
24  * along with this program; if not, contact: *
25  * *
26  * Free Software Foundation Voice: +1-617-542-5942 *
27  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
28  * Boston, MA 02110-1301, USA gnu@gnu.org *
29  * *
30 \********************************************************************/
31 
32 
33 #ifndef __BINRELOC_C__
34 #define __BINRELOC_C__
35 #include <config.h>
36 
37 #include <platform.h>
38 #if PLATFORM(WINDOWS)
39 #include <windows.h>
40 #endif
41 
42 #ifdef ENABLE_BINRELOC
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <unistd.h>
46 #include <stdint.h>
47 #ifdef MAC_INTEGRATION
48 #include <gtkmacintegration/gtkosxapplication.h>
49 #elif GNC_PLATFORM_OSX
50 #include <mach-o/dyld.h>
51 #endif
52 #endif /* ENABLE_BINRELOC */
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <limits.h>
56 #include <string.h>
57 #include "binreloc.h"
58 #include "gnc-filepath-utils.h"
59 #include <glib.h>
60 #include "gncla-dir.h"
61 
62 G_BEGIN_DECLS
63 
69 static char *
70 _br_find_exe (Gnc_GbrInitError *error)
71 {
72 #ifndef ENABLE_BINRELOC
73  if (error)
74  *error = GNC_GBR_INIT_ERROR_DISABLED;
75  return NULL;
76 #elif defined G_OS_WIN32
77  /* N.B. g_win32_get_package_installation_directory_of_module returns the
78  * parent if the last element of the directory is "bin" or "lib", but
79  * otherwise the directory itself. We assume that gnucash.exe isn't in lib.
80  */
81  gchar *prefix = g_win32_get_package_installation_directory_of_module (NULL);
82  gchar *result = g_build_filename (prefix, "bin", "gnucash.exe", NULL);
83  if (prefix == NULL)
84  {
85  if (error)
86  *error = GNC_GBR_INIT_WIN32_NO_EXE_DIR;
87  return NULL;
88  }
89  if (!g_file_test (result, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE))
90  {
91  g_free (result);
92  result = g_build_filename (prefix, "gnucash.exe", NULL);
93  if (!g_file_test (result,
94  G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE))
95  {
96  g_free (result);
97  result = NULL;
98  if (error)
99  *error = GNC_GBR_INIT_WIN32_NO_EXE_DIR;
100  }
101  }
102  g_free (prefix);
103  return result;
104 #else
105  char path[PATH_MAX + 1], path2[PATH_MAX + 1];
106  char *line, *result;
107  size_t buf_size = PATH_MAX + 1;
108  FILE *f;
109 
110 #ifdef MAC_INTEGRATION
111  result = gtkosx_application_get_executable_path();
112  strncpy (path2, result, buf_size - 1);
113  g_free (result);
114  g_print ("Application Path %s\n", path2);
115 #elif defined GNC_PLATFORM_OSX
116  /* Native Mac, but not Aqua */
117  uint32_t size2 = buf_size;
118  if (_NSGetExecutablePath (path2, &size2) != 0)
119  {
120  /* buffer not big enough or some other error */
121  if (error)
122  *error = GNC_GBR_INIT_ERROR_NOMEM;
123  return NULL;
124  }
125 #else
126  strncpy (path2, "/proc/self/exe", buf_size - 1);
127 #endif
128 
129  /* Follow all sym links */
130  if (realpath (path2, path) != NULL)
131  {
132  return g_strdup (path);
133  }
134 
135  /* realpath() failed; this can happen when the program is
136  * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
137 
138  buf_size = PATH_MAX + 128;
139  line = (char *) g_try_malloc (buf_size);
140  if (line == NULL)
141  {
142  /* Cannot allocate memory. */
143  if (error)
144  *error = GNC_GBR_INIT_ERROR_NOMEM;
145  return NULL;
146  }
147 
148  f = fopen ("/proc/self/maps", "r");
149  if (f == NULL)
150  {
151  g_free (line);
152  if (error)
153  *error = GNC_GBR_INIT_ERROR_OPEN_MAPS;
154  return NULL;
155  }
156 
157  /* The first entry should be the executable name. */
158  result = fgets (line, (int) buf_size, f);
159  if (result == NULL)
160  {
161  fclose (f);
162  g_free (line);
163  if (error)
164  *error = GNC_GBR_INIT_ERROR_READ_MAPS;
165  return NULL;
166  }
167 
168  /* Get rid of newline character. */
169  buf_size = strlen (line);
170  if (buf_size <= 0)
171  {
172  /* Huh? An empty string? */
173  fclose (f);
174  g_free (line);
175  if (error)
176  *error = GNC_GBR_INIT_ERROR_INVALID_MAPS;
177  return NULL;
178  }
179  if (line[buf_size - 1] == 10)
180  line[buf_size - 1] = 0;
181 
182  /* Extract the filename; it is always an absolute path. */
183  result = strchr (line, '/');
184 
185  /* Sanity check. */
186  if (strstr (line, " r-xp ") == NULL || result == NULL)
187  {
188  fclose (f);
189  g_free (line);
190  if (error)
191  *error = GNC_GBR_INIT_ERROR_INVALID_MAPS;
192  return NULL;
193  }
194 
195  result = g_strdup (result);
196  fclose (f);
197  g_free (line);
198  return result;
199 #endif /* ENABLE_BINRELOC */
200 }
201 
202 
203 
204 static gchar *exe = NULL;
205 
206 static void set_gerror (GError **error, Gnc_GbrInitError errcode);
207 
208 
209 void gnc_gbr_set_exe (const gchar* default_exe)
210 {
211  if (exe != NULL)
212  g_free(exe);
213  exe = NULL;
214 
215  if (default_exe != NULL)
216  exe = g_strdup(default_exe);
217 }
218 
219 
235 gboolean
236 gnc_gbr_init (GError **error)
237 {
238  Gnc_GbrInitError errcode = 0;
239 
240  /* Locate the application's filename. */
241  exe = _br_find_exe (&errcode);
242  if (exe != NULL)
243  /* Success! */
244  return TRUE;
245  else
246  {
247  /* Failed :-( */
248  set_gerror (error, errcode);
249  return FALSE;
250  }
251 }
252 
253 
254 static void
255 set_gerror (GError **error, Gnc_GbrInitError errcode)
256 {
257  gchar *error_message;
258 
259  if (error == NULL)
260  return;
261 
262  switch (errcode)
263  {
264  case GNC_GBR_INIT_ERROR_NOMEM:
265  error_message = "Cannot allocate memory.";
266  break;
267  case GNC_GBR_INIT_ERROR_OPEN_MAPS:
268  error_message = "Unable to open /proc/self/maps for reading.";
269  break;
270  case GNC_GBR_INIT_ERROR_READ_MAPS:
271  error_message = "Unable to read from /proc/self/maps.";
272  break;
273  case GNC_GBR_INIT_ERROR_INVALID_MAPS:
274  error_message = "The file format of /proc/self/maps is invalid.";
275  break;
276  case GNC_GBR_INIT_ERROR_DISABLED:
277  error_message = "Binary relocation support is disabled.";
278  break;
279  case GNC_GBR_INIT_ERROR_MAC_NOT_BUNDLE:
280  error_message = "BinReloc determined that gnucash is not running from a bundle";
281  break;
282  case GNC_GBR_INIT_ERROR_MAC_NOT_APP_BUNDLE:
283  error_message = "Binreloc determined that the bundle is not an app bundle";
284  break;
285  case GNC_GBR_INIT_WIN32_NO_EXE_DIR:
286  error_message = "Binreloc was unable to determine the location of gnucash.exe.";
287  break;
288  default:
289  error_message = "Unknown error.";
290  break;
291  };
292  g_set_error (error, g_quark_from_static_string ("GBinReloc"),
293  errcode, "%s", error_message);
294 }
295 
296 
306 gchar *
307 gnc_gbr_find_exe (const gchar *default_exe)
308 {
309  if (exe == NULL)
310  {
311  /* BinReloc is not initialized. */
312  if (default_exe != NULL)
313  return g_strdup (default_exe);
314  else
315  return NULL;
316  }
317  return g_strdup (exe);
318 }
319 
320 
335 gchar *
336 gnc_gbr_find_exe_dir (const gchar *default_dir)
337 {
338  if (exe == NULL)
339  {
340  /* BinReloc not initialized. */
341  if (default_dir != NULL)
342  return g_strdup (default_dir);
343  else
344  return NULL;
345  }
346 
347  return g_path_get_dirname (exe);
348 }
349 
350 
366 static inline gchar *
367 get_mac_bundle_prefix()
368 {
369 #if defined ENABLE_BINRELOC && defined MAC_INTEGRATION
370  gchar *id = gtkosx_application_get_bundle_id ();
371  gchar *path = gtkosx_application_get_resource_path ();
372  gchar *basename = g_path_get_basename (path);
373  /* If id is nullthe app is unbundled and the path
374  is just the path to the application directory.
375  We already have that and our version is better.
376  If GNC_UNINSTALLED is set then we're running from
377  GNC_BUILDDIR.
378  */
379  if (id == NULL || g_getenv ("GNC_UNINSTALLED"))
380  {
381  g_free (basename);
382  g_free (path);
383  g_free (id);
384  return NULL;
385  }
386 
387  g_free (id);
388 
389  if (g_strcmp0 ("bin", basename) == 0)
390  {
391  g_free (path);
392  g_free (basename);
393  return NULL;
394  }
395 
396  g_free (basename);
397 
398  return path;
399 #endif
400  return NULL;
401 }
402 
403 static inline gchar*
404 get_builddir_prefix()
405 {
406  if (g_getenv ("GNC_UNINSTALLED"))
407  return g_strdup (g_getenv ("GNC_BUILDDIR"));
408  return NULL;
409 }
410 
411 gchar *
412 gnc_gbr_find_prefix (const gchar *default_prefix)
413 {
414  gchar *dir1, *dir2;
415  if ((dir2 = get_builddir_prefix()) || (dir2 = get_mac_bundle_prefix()))
416  return dir2;
417  if (exe == NULL)
418  {
419  /* BinReloc not initialized. */
420  if (default_prefix != NULL)
421  return g_strdup (default_prefix);
422  else
423  return NULL;
424  }
425  dir1 = g_path_get_dirname (exe);
426  dir2 = g_path_get_dirname (dir1);
427  g_free (dir1);
428  return dir2;
429 }
430 
431 /* Locate a specified component directory.
432  *
433  * E.g., <prefix>/share
434 
435  * default_dir is passed in from the wrapper function, compiled_dir is the corresponding constant from gncla-dir.h.
436  *
437  * If compiled_dir exists and is an absolute path then we check the dynamic
438  * prefix and if it's NULL fall back first on the passed-in default and then on
439  * compiled_dir;
440  * otherwise if the dynamic prefix turns out to be the compile time defined PREFIX
441  * just use that
442  * otherwise we pass the compiled PREFIX value as a default to
443  * gnc_gbr_find_prefix, remove the PREFIX part (if any) from the compiled_dir
444  * and append that to the retrieved prefix.
445  */
446 static gchar*
447 find_component_directory (const gchar *default_dir, const gchar* compiled_dir)
448 {
449  gchar *prefix = NULL, *dir = NULL;
450  gchar *subdir = gnc_file_path_relative_part(PREFIX, compiled_dir);
451 
452  prefix = gnc_gbr_find_prefix (NULL);
453  if (prefix == NULL)
454  {
455  g_free (subdir);
456  return g_strdup (default_dir ? default_dir : compiled_dir);
457  }
458  if (!g_getenv("GNC_UNINSTALLED"))
459  {
460  if (!g_strcmp0 (prefix, PREFIX))
461  {
462  g_free (subdir);
463  g_free (prefix);
464  return g_strdup (compiled_dir);
465  }
466 
467  if (g_strcmp0 (compiled_dir, subdir) == 0)
468  {
469  /* compiled_dir isn't a subdir of PREFIX. This isn't relocatable so
470  * return compiled_dir.
471  */
472  g_free (subdir);
473  g_free (prefix);
474  return g_strdup (compiled_dir);
475  }
476  }
477  dir = g_build_filename (prefix, subdir, NULL);
478  g_free (subdir);
479  g_free (prefix);
480  return dir;
481 }
482 
483 
497 gchar *
498 gnc_gbr_find_bin_dir (const gchar *default_bin_dir)
499 {
500  return find_component_directory (default_bin_dir, BINDIR);
501 }
502 
517 gchar *
518 gnc_gbr_find_data_dir (const gchar *default_data_dir)
519 {
520  return find_component_directory (default_data_dir, DATADIR);
521 }
522 
536 gchar *
537 gnc_gbr_find_lib_dir (const gchar *default_lib_dir)
538 {
539  return find_component_directory (default_lib_dir, LIBDIR);
540 }
541 
555 gchar *
556 gnc_gbr_find_etc_dir (const gchar *default_etc_dir)
557 {
558  return find_component_directory (default_etc_dir, SYSCONFDIR);
559 }
560 
561 
562 G_END_DECLS
563 
564 #endif /* __BINRELOC_C__ */
gchar * gnc_file_path_relative_part(const gchar *prefix, const gchar *path)
Given a prefix and a path return the relative portion of the path.
File path resolution utility functions.