GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
binreloc.c
1 /*
2  * BinReloc - a library for creating relocatable executables
3  * Written by: Hongli Lai <[email protected]>
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 #ifndef __BINRELOC_C__
14 #define __BINRELOC_C__
15 #include "config.h"
16 
17 #ifdef ENABLE_BINRELOC
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #ifdef MAC_INTEGRATION
22 #include <gtkmacintegration/gtkosxapplication.h>
23 #endif
24 #endif /* ENABLE_BINRELOC */
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <limits.h>
28 #include <string.h>
29 #include "binreloc.h"
30 #include <glib.h>
31 
32 G_BEGIN_DECLS
33 
39 static char *
40 _br_find_exe (Gnc_GbrInitError *error)
41 {
42 #ifndef ENABLE_BINRELOC
43  if (error)
44  *error = GNC_GBR_INIT_ERROR_DISABLED;
45  return NULL;
46 #else
47 #ifdef G_OS_WIN32
48  /* I *thought* this program code already included the
49  relocation code for windows. Unfortunately this is not
50  the case and we have to add this manually. This is only
51  one possibility; other ways of looking up the full path
52  of gnucash.exe probably exist.*/
53  gchar *prefix;
54  gchar *result;
55 
56  /* From the glib docs: When passed NULL, this function looks
57  up installation the directory of the main executable of
58  the current process */
59  prefix = g_win32_get_package_installation_directory_of_module (NULL);
60  result = g_build_filename (prefix,
61  "bin", "gnucash.exe",
62  (char*)NULL);
63  g_free (prefix);
64  return result;
65 #elif defined MAC_INTEGRATION
66  gchar *path = gtkosx_application_get_executable_path();
67  g_print ("Application Path %s\n", path);
68  return path;
69 #else
70  char *path, *path2, *line, *result;
71  size_t buf_size;
72  ssize_t size;
73  struct stat stat_buf;
74  FILE *f;
75 
76  /* Read from /proc/self/exe (symlink) */
77  if (sizeof (path) > SSIZE_MAX)
78  buf_size = SSIZE_MAX - 1;
79  else
80  buf_size = PATH_MAX - 1;
81  path = (char *) g_try_malloc (buf_size);
82  if (path == NULL)
83  {
84  /* Cannot allocate memory. */
85  if (error)
86  *error = GNC_GBR_INIT_ERROR_NOMEM;
87  return NULL;
88  }
89  path2 = (char *) g_try_malloc (buf_size);
90  if (path2 == NULL)
91  {
92  /* Cannot allocate memory. */
93  if (error)
94  *error = GNC_GBR_INIT_ERROR_NOMEM;
95  g_free (path);
96  return NULL;
97  }
98 
99  strncpy (path2, "/proc/self/exe", buf_size - 1);
100 
101  while (1)
102  {
103  int i;
104 
105  size = readlink (path2, path, buf_size - 1);
106  if (size == -1)
107  {
108  /* Error. */
109  g_free (path2);
110  break;
111  }
112 
113  /* readlink() success. */
114  path[size] = '\0';
115 
116  /* Check whether the symlink's target is also a symlink.
117  * We want to get the final target. */
118  i = stat (path, &stat_buf);
119  if (i == -1)
120  {
121  /* Error. */
122  g_free (path2);
123  break;
124  }
125 
126  /* stat() success. */
127  if (!S_ISLNK (stat_buf.st_mode))
128  {
129  /* path is not a symlink. Done. */
130  g_free (path2);
131  return path;
132  }
133 
134  /* path is a symlink. Continue loop and resolve this. */
135  strncpy (path, path2, buf_size - 1);
136  }
137 
138 
139  /* readlink() or stat() failed; this can happen when the program is
140  * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
141 
142  buf_size = PATH_MAX + 128;
143  line = (char *) g_try_realloc (path, buf_size);
144  if (line == NULL)
145  {
146  /* Cannot allocate memory. */
147  g_free (path);
148  if (error)
149  *error = GNC_GBR_INIT_ERROR_NOMEM;
150  return NULL;
151  }
152 
153  f = fopen ("/proc/self/maps", "r");
154  if (f == NULL)
155  {
156  g_free (line);
157  if (error)
158  *error = GNC_GBR_INIT_ERROR_OPEN_MAPS;
159  return NULL;
160  }
161 
162  /* The first entry should be the executable name. */
163  result = fgets (line, (int) buf_size, f);
164  if (result == NULL)
165  {
166  fclose (f);
167  g_free (line);
168  if (error)
169  *error = GNC_GBR_INIT_ERROR_READ_MAPS;
170  return NULL;
171  }
172 
173  /* Get rid of newline character. */
174  buf_size = strlen (line);
175  if (buf_size <= 0)
176  {
177  /* Huh? An empty string? */
178  fclose (f);
179  g_free (line);
180  if (error)
181  *error = GNC_GBR_INIT_ERROR_INVALID_MAPS;
182  return NULL;
183  }
184  if (line[buf_size - 1] == 10)
185  line[buf_size - 1] = 0;
186 
187  /* Extract the filename; it is always an absolute path. */
188  path = strchr (line, '/');
189 
190  /* Sanity check. */
191  if (strstr (line, " r-xp ") == NULL || path == NULL)
192  {
193  fclose (f);
194  g_free (line);
195  if (error)
196  *error = GNC_GBR_INIT_ERROR_INVALID_MAPS;
197  return NULL;
198  }
199 
200  path = g_strdup (path);
201  g_free (line);
202  fclose (f);
203  return path;
204 #endif /* G_OS_WINDOWS */
205 #endif /* ENABLE_BINRELOC */
206 }
207 
208 
209 
210 static gchar *exe = NULL;
211 
212 static void set_gerror (GError **error, Gnc_GbrInitError errcode);
213 
214 
215 void gnc_gbr_set_exe (const gchar* default_exe)
216 {
217  if (exe != NULL)
218  g_free(exe);
219  exe = NULL;
220 
221  if (default_exe != NULL)
222  exe = g_strdup(default_exe);
223 }
224 
225 
241 gboolean
242 gnc_gbr_init (GError **error)
243 {
244  Gnc_GbrInitError errcode = 0;
245 
246  /* Locate the application's filename. */
247  exe = _br_find_exe (&errcode);
248  if (exe != NULL)
249  /* Success! */
250  return TRUE;
251  else
252  {
253  /* Failed :-( */
254  set_gerror (error, errcode);
255  return FALSE;
256  }
257 }
258 
259 
260 static void
261 set_gerror (GError **error, Gnc_GbrInitError errcode)
262 {
263  gchar *error_message;
264 
265  if (error == NULL)
266  return;
267 
268  switch (errcode)
269  {
270  case GNC_GBR_INIT_ERROR_NOMEM:
271  error_message = "Cannot allocate memory.";
272  break;
273  case GNC_GBR_INIT_ERROR_OPEN_MAPS:
274  error_message = "Unable to open /proc/self/maps for reading.";
275  break;
276  case GNC_GBR_INIT_ERROR_READ_MAPS:
277  error_message = "Unable to read from /proc/self/maps.";
278  break;
279  case GNC_GBR_INIT_ERROR_INVALID_MAPS:
280  error_message = "The file format of /proc/self/maps is invalid.";
281  break;
282  case GNC_GBR_INIT_ERROR_DISABLED:
283  error_message = "Binary relocation support is disabled.";
284  break;
285  default:
286  error_message = "Unknown error.";
287  break;
288  };
289  g_set_error (error, g_quark_from_static_string ("GBinReloc"),
290  errcode, "%s", error_message);
291 }
292 
293 
303 gchar *
304 gnc_gbr_find_exe (const gchar *default_exe)
305 {
306  if (exe == NULL)
307  {
308  /* BinReloc is not initialized. */
309  if (default_exe != NULL)
310  return g_strdup (default_exe);
311  else
312  return NULL;
313  }
314  return g_strdup (exe);
315 }
316 
317 
332 gchar *
333 gnc_gbr_find_exe_dir (const gchar *default_dir)
334 {
335  if (exe == NULL)
336  {
337  /* BinReloc not initialized. */
338  if (default_dir != NULL)
339  return g_strdup (default_dir);
340  else
341  return NULL;
342  }
343 
344  return g_path_get_dirname (exe);
345 }
346 
347 
362 gchar *
363 gnc_gbr_find_prefix (const gchar *default_prefix)
364 {
365 #if defined ENABLE_BINRELOC && defined MAC_INTEGRATION
366  gchar *id = gtkosx_application_get_bundle_id ();
367  gchar *path = gtkosx_application_get_resource_path ();
368  if (id == NULL)
369  {
370  gchar *dirname = g_path_get_dirname (path);
371  g_free (path);
372  g_free (id);
373  return dirname;
374  }
375  g_free (id);
376  return path;
377 #else
378  gchar *dir1, *dir2;
379 
380  if (exe == NULL)
381  {
382  /* BinReloc not initialized. */
383  if (default_prefix != NULL)
384  return g_strdup (default_prefix);
385  else
386  return NULL;
387  }
388  dir1 = g_path_get_dirname (exe);
389  dir2 = g_path_get_dirname (dir1);
390  g_free (dir1);
391  return dir2;
392 #endif //ENABLE_BINRELOC && defined MAC_INTEGRATION
393 }
394 
395 
409 gchar *
410 gnc_gbr_find_bin_dir (const gchar *default_bin_dir)
411 {
412  gchar *prefix, *dir;
413  prefix = gnc_gbr_find_prefix (NULL);
414  if (prefix == NULL)
415  {
416  /* BinReloc not initialized. */
417  if (default_bin_dir != NULL)
418  return g_strdup (default_bin_dir);
419  else
420  return NULL;
421  }
422 
423  dir = g_build_filename (prefix, "bin", NULL);
424  g_free (prefix);
425  return dir;
426 }
427 
428 
442 gchar *
443 gnc_gbr_find_sbin_dir (const gchar *default_sbin_dir)
444 {
445  gchar *prefix, *dir;
446 
447  prefix = gnc_gbr_find_prefix (NULL);
448  if (prefix == NULL)
449  {
450  /* BinReloc not initialized. */
451  if (default_sbin_dir != NULL)
452  return g_strdup (default_sbin_dir);
453  else
454  return NULL;
455  }
456 
457  dir = g_build_filename (prefix, "sbin", NULL);
458  g_free (prefix);
459  return dir;
460 }
461 
462 
477 gchar *
478 gnc_gbr_find_data_dir (const gchar *default_data_dir)
479 {
480  gchar *prefix, *dir;
481 
482  prefix = gnc_gbr_find_prefix (NULL);
483  if (prefix == NULL)
484  {
485  /* BinReloc not initialized. */
486  if (default_data_dir != NULL)
487  return g_strdup (default_data_dir);
488  else
489  return NULL;
490  }
491 
492  dir = g_build_filename (prefix, "share", NULL);
493  g_free (prefix);
494  return dir;
495 }
496 
497 
511 gchar *
512 gnc_gbr_find_lib_dir (const gchar *default_lib_dir)
513 {
514  gchar *prefix, *dir;
515 
516  prefix = gnc_gbr_find_prefix (NULL);
517  if (prefix == NULL)
518  {
519  /* BinReloc not initialized. */
520  if (default_lib_dir != NULL)
521  return g_strdup (default_lib_dir);
522  else
523  return NULL;
524  }
525 
526  dir = g_build_filename (prefix, "lib", NULL);
527  g_free (prefix);
528  return dir;
529 }
530 
531 
545 gchar *
546 gnc_gbr_find_libexec_dir (const gchar *default_libexec_dir)
547 {
548  gchar *prefix, *dir;
549 
550  prefix = gnc_gbr_find_prefix (NULL);
551  if (prefix == NULL)
552  {
553  /* BinReloc not initialized. */
554  if (default_libexec_dir != NULL)
555  return g_strdup (default_libexec_dir);
556  else
557  return NULL;
558  }
559 
560  dir = g_build_filename (prefix, "libexec", NULL);
561  g_free (prefix);
562  return dir;
563 }
564 
565 
579 gchar *
580 gnc_gbr_find_etc_dir (const gchar *default_etc_dir)
581 {
582  gchar *prefix, *dir;
583 
584  prefix = gnc_gbr_find_prefix (NULL);
585  if (prefix == NULL)
586  {
587  /* BinReloc not initialized. */
588  if (default_etc_dir != NULL)
589  return g_strdup (default_etc_dir);
590  else
591  return NULL;
592  }
593 
594  dir = g_build_filename (prefix, "etc", NULL);
595  g_free (prefix);
596  return dir;
597 }
598 
599 
600 G_END_DECLS
601 
602 #endif /* __BINRELOC_C__ */