Tizen 2.1 base
[external/enchant.git] / src / prefix.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 #ifndef __BINRELOC_C__
14 #define __BINRELOC_C__
15
16 #ifdef ENABLE_BINRELOC
17         #include <sys/types.h>
18         #include <sys/stat.h>
19         #include <unistd.h>
20 #endif /* ENABLE_BINRELOC */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <limits.h>
24 #include <string.h>
25 #include "prefix.h"
26
27 G_BEGIN_DECLS
28
29
30 /** @internal
31  * Find the canonical filename of the executable. Returns the filename
32  * (which must be freed) or NULL on error. If the parameter 'error' is
33  * not NULL, the error code will be stored there, if an error occured.
34  */
35 static char *
36 _br_find_exe (GbrInitError *error)
37 {
38 #ifndef ENABLE_BINRELOC
39         if (error)
40                 *error = GBR_INIT_ERROR_DISABLED;
41         return NULL;
42 #else
43         char *path, *path2, *line, *result;
44         size_t buf_size;
45         ssize_t size;
46         struct stat stat_buf;
47         FILE *f;
48
49         /* Read from /proc/self/exe (symlink) */
50         if (sizeof (path) > SSIZE_MAX)
51                 buf_size = SSIZE_MAX - 1;
52         else
53                 buf_size = PATH_MAX - 1;
54         path = (char *) g_try_malloc (buf_size);
55         if (path == NULL) {
56                 /* Cannot allocate memory. */
57                 if (error)
58                         *error = GBR_INIT_ERROR_NOMEM;
59                 return NULL;
60         }
61         path2 = (char *) g_try_malloc (buf_size);
62         if (path2 == NULL) {
63                 /* Cannot allocate memory. */
64                 if (error)
65                         *error = GBR_INIT_ERROR_NOMEM;
66                 g_free (path);
67                 return NULL;
68         }
69
70         strncpy (path2, "/proc/self/exe", buf_size - 1);
71
72         while (1) {
73                 int i;
74
75                 size = readlink (path2, path, buf_size - 1);
76                 if (size == -1) {
77                         /* Error. */
78                         g_free (path2);
79                         break;
80                 }
81
82                 /* readlink() success. */
83                 path[size] = '\0';
84
85                 /* Check whether the symlink's target is also a symlink.
86                  * We want to get the final target. */
87                 i = stat (path, &stat_buf);
88                 if (i == -1) {
89                         /* Error. */
90                         g_free (path2);
91                         break;
92                 }
93
94                 /* stat() success. */
95                 if (!S_ISLNK (stat_buf.st_mode)) {
96                         /* path is not a symlink. Done. */
97                         g_free (path2);
98                         return path;
99                 }
100
101                 /* path is a symlink. Continue loop and resolve this. */
102                 strncpy (path, path2, buf_size - 1);
103         }
104
105
106         /* readlink() or stat() failed; this can happen when the program is
107          * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
108
109         buf_size = PATH_MAX + 128;
110         line = (char *) g_try_realloc (path, buf_size);
111         if (line == NULL) {
112                 /* Cannot allocate memory. */
113                 g_free (path);
114                 if (error)
115                         *error = GBR_INIT_ERROR_NOMEM;
116                 return NULL;
117         }
118
119         f = fopen ("/proc/self/maps", "r");
120         if (f == NULL) {
121                 g_free (line);
122                 if (error)
123                         *error = GBR_INIT_ERROR_OPEN_MAPS;
124                 return NULL;
125         }
126
127         /* The first entry should be the executable name. */
128         result = fgets (line, (int) buf_size, f);
129         if (result == NULL) {
130                 fclose (f);
131                 g_free (line);
132                 if (error)
133                         *error = GBR_INIT_ERROR_READ_MAPS;
134                 return NULL;
135         }
136
137         /* Get rid of newline character. */
138         buf_size = strlen (line);
139         if (buf_size <= 0) {
140                 /* Huh? An empty string? */
141                 fclose (f);
142                 g_free (line);
143                 if (error)
144                         *error = GBR_INIT_ERROR_INVALID_MAPS;
145                 return NULL;
146         }
147         if (line[buf_size - 1] == 10)
148                 line[buf_size - 1] = 0;
149
150         /* Extract the filename; it is always an absolute path. */
151         path = strchr (line, '/');
152
153         /* Sanity check. */
154         if (strstr (line, " r-xp ") == NULL || path == NULL) {
155                 fclose (f);
156                 g_free (line);
157                 if (error)
158                         *error = GBR_INIT_ERROR_INVALID_MAPS;
159                 return NULL;
160         }
161
162         path = g_strdup (path);
163         g_free (line);
164         fclose (f);
165         return path;
166 #endif /* ENABLE_BINRELOC */
167 }
168
169
170 /** @internal
171  * Find the canonical filename of the executable which owns symbol.
172  * Returns a filename which must be freed, or NULL on error.
173  */
174 static char *
175 _br_find_exe_for_symbol (const void *symbol, GbrInitError *error)
176 {
177 #ifndef ENABLE_BINRELOC
178         if (error)
179                 *error = GBR_INIT_ERROR_DISABLED;
180         return (char *) NULL;
181 #else
182         #define SIZE PATH_MAX + 100
183         FILE *f;
184         size_t address_string_len;
185         char *address_string, line[SIZE], *found;
186
187         if (symbol == NULL)
188                 return (char *) NULL;
189
190         f = fopen ("/proc/self/maps", "r");
191         if (f == NULL)
192                 return (char *) NULL;
193
194         address_string_len = 4;
195         address_string = (char *) g_try_malloc (address_string_len);
196         found = (char *) NULL;
197
198         while (!feof (f)) {
199                 char *start_addr, *end_addr, *end_addr_end, *file;
200                 void *start_addr_p, *end_addr_p;
201                 size_t len;
202
203                 if (fgets (line, SIZE, f) == NULL)
204                         break;
205
206                 /* Sanity check. */
207                 if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
208                         continue;
209
210                 /* Parse line. */
211                 start_addr = line;
212                 end_addr = strchr (line, '-');
213                 file = strchr (line, '/');
214
215                 /* More sanity check. */
216                 if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
217                         continue;
218
219                 end_addr[0] = '\0';
220                 end_addr++;
221                 end_addr_end = strchr (end_addr, ' ');
222                 if (end_addr_end == NULL)
223                         continue;
224
225                 end_addr_end[0] = '\0';
226                 len = strlen (file);
227                 if (len == 0)
228                         continue;
229                 if (file[len - 1] == '\n')
230                         file[len - 1] = '\0';
231
232                 /* Get rid of "(deleted)" from the filename. */
233                 len = strlen (file);
234                 if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
235                         file[len - 10] = '\0';
236
237                 /* I don't know whether this can happen but better safe than sorry. */
238                 len = strlen (start_addr);
239                 if (len != strlen (end_addr))
240                         continue;
241
242
243                 /* Transform the addresses into a string in the form of 0xdeadbeef,
244                  * then transform that into a pointer. */
245                 if (address_string_len < len + 3) {
246                         address_string_len = len + 3;
247                         address_string = (char *) g_try_realloc (address_string, address_string_len);
248                 }
249
250                 memcpy (address_string, "0x", 2);
251                 memcpy (address_string + 2, start_addr, len);
252                 address_string[2 + len] = '\0';
253                 sscanf (address_string, "%p", &start_addr_p);
254
255                 memcpy (address_string, "0x", 2);
256                 memcpy (address_string + 2, end_addr, len);
257                 address_string[2 + len] = '\0';
258                 sscanf (address_string, "%p", &end_addr_p);
259
260
261                 if (symbol >= start_addr_p && symbol < end_addr_p) {
262                         found = file;
263                         break;
264                 }
265         }
266
267         g_free (address_string);
268         fclose (f);
269
270         if (found == NULL)
271                 return (char *) NULL;
272         else
273                 return g_strdup (found);
274 #endif /* ENABLE_BINRELOC */
275 }
276
277
278 static gchar *exe = NULL;
279
280 static void set_gerror (GError **error, GbrInitError errcode);
281
282
283 /** Initialize the BinReloc library (for applications).
284  *
285  * This function must be called before using any other BinReloc functions.
286  * It attempts to locate the application's canonical filename.
287  *
288  * @note If you want to use BinReloc for a library, then you should call
289  *       gbr_init_lib() instead.
290  *
291  * @param error  If BinReloc failed to initialize, then the error report will
292  *               be stored in this variable. Set to NULL if you don't want an
293  *               error report. See the #GbrInitError for a list of error
294  *               codes.
295  *
296  * @returns TRUE on success, FALSE if BinReloc failed to initialize.
297  */
298 gboolean
299 gbr_init (GError **error)
300 {
301         GbrInitError errcode;
302
303         /* Locate the application's filename. */
304         exe = _br_find_exe (&errcode);
305         if (exe != NULL)
306                 /* Success! */
307                 return TRUE;
308         else {
309                 /* Failed :-( */
310                 set_gerror (error, errcode);
311                 return FALSE;
312         }
313 }
314
315
316 /** Initialize the BinReloc library (for libraries).
317  *
318  * This function must be called before using any other BinReloc functions.
319  * It attempts to locate the calling library's canonical filename.
320  *
321  * @note The BinReloc source code MUST be included in your library, or this
322  *       function won't work correctly.
323  *
324  * @returns TRUE on success, FALSE if a filename cannot be found.
325  */
326 gboolean
327 gbr_init_lib (GError **error)
328 {
329         GbrInitError errcode;
330
331         exe = _br_find_exe_for_symbol ((const void *) "", &errcode);
332         if (exe != NULL)
333                 /* Success! */
334                 return TRUE;
335         else {
336                 /* Failed :-( */
337                 set_gerror (error, errcode);
338                 return exe != NULL;
339         }
340 }
341
342
343 static void
344 set_gerror (GError **error, GbrInitError errcode)
345 {
346         const gchar *error_message;
347
348         if (error == NULL)
349                 return;
350
351         switch (errcode) {
352         case GBR_INIT_ERROR_NOMEM:
353                 error_message = "Cannot allocate memory.";
354                 break;
355         case GBR_INIT_ERROR_OPEN_MAPS:
356                 error_message = "Unable to open /proc/self/maps for reading.";
357                 break;
358         case GBR_INIT_ERROR_READ_MAPS:
359                 error_message = "Unable to read from /proc/self/maps.";
360                 break;
361         case GBR_INIT_ERROR_INVALID_MAPS:
362                 error_message = "The file format of /proc/self/maps is invalid.";
363                 break;
364         case GBR_INIT_ERROR_DISABLED:
365                 error_message = "Binary relocation support is disabled.";
366                 break;
367         default:
368                 error_message = "Unknown error.";
369                 break;
370         };
371         g_set_error (error, g_quark_from_static_string ("GBinReloc"),
372                      errcode, "%s", error_message);
373 }
374
375
376 /** Find the canonical filename of the current application.
377  *
378  * @param default_exe  A default filename which will be used as fallback.
379  * @returns A string containing the application's canonical filename,
380  *          which must be freed when no longer necessary. If BinReloc is
381  *          not initialized, or if the initialization function failed,
382  *          then a copy of default_exe will be returned. If default_exe
383  *          is NULL, then NULL will be returned.
384  */
385 gchar *
386 gbr_find_exe (const gchar *default_exe)
387 {
388         if (exe == NULL) {
389                 /* BinReloc is not initialized. */
390                 if (default_exe != NULL)
391                         return g_strdup (default_exe);
392                 else
393                         return NULL;
394         }
395         return g_strdup (exe);
396 }
397
398
399 /** Locate the directory in which the current application is installed.
400  *
401  * The prefix is generated by the following pseudo-code evaluation:
402  * \code
403  * dirname(exename)
404  * \endcode
405  *
406  * @param default_dir  A default directory which will used as fallback.
407  * @return A string containing the directory, which must be freed when no
408  *         longer necessary. If BinReloc is not initialized, or if the
409  *         initialization function failed, then a copy of default_dir
410  *         will be returned. If default_dir is NULL, then NULL will be
411  *         returned.
412  */
413 gchar *
414 gbr_find_exe_dir (const gchar *default_dir)
415 {
416         if (exe == NULL) {
417                 /* BinReloc not initialized. */
418                 if (default_dir != NULL)
419                         return g_strdup (default_dir);
420                 else
421                         return NULL;
422         }
423
424         return g_path_get_dirname (exe);
425 }
426
427
428 /** Locate the prefix in which the current application is installed.
429  *
430  * The prefix is generated by the following pseudo-code evaluation:
431  * \code
432  * dirname(dirname(exename))
433  * \endcode
434  *
435  * @param default_prefix  A default prefix which will used as fallback.
436  * @return A string containing the prefix, which must be freed when no
437  *         longer necessary. If BinReloc is not initialized, or if the
438  *         initialization function failed, then a copy of default_prefix
439  *         will be returned. If default_prefix is NULL, then NULL will be
440  *         returned.
441  */
442 gchar *
443 gbr_find_prefix (const gchar *default_prefix)
444 {
445         gchar *dir1, *dir2;
446
447         if (exe == NULL) {
448                 /* BinReloc not initialized. */
449                 if (default_prefix != NULL)
450                         return g_strdup (default_prefix);
451                 else
452                         return NULL;
453         }
454
455         dir1 = g_path_get_dirname (exe);
456         dir2 = g_path_get_dirname (dir1);
457         g_free (dir1);
458         return dir2;
459 }
460
461
462 /** Locate the application's binary folder.
463  *
464  * The path is generated by the following pseudo-code evaluation:
465  * \code
466  * prefix + "/bin"
467  * \endcode
468  *
469  * @param default_bin_dir  A default path which will used as fallback.
470  * @return A string containing the bin folder's path, which must be freed when
471  *         no longer necessary. If BinReloc is not initialized, or if the
472  *         initialization function failed, then a copy of default_bin_dir will
473  *         be returned. If default_bin_dir is NULL, then NULL will be returned.
474  */
475 gchar *
476 gbr_find_bin_dir (const gchar *default_bin_dir)
477 {
478         gchar *prefix, *dir;
479
480         prefix = gbr_find_prefix (NULL);
481         if (prefix == NULL) {
482                 /* BinReloc not initialized. */
483                 if (default_bin_dir != NULL)
484                         return g_strdup (default_bin_dir);
485                 else
486                         return NULL;
487         }
488
489         dir = g_build_filename (prefix, "bin", NULL);
490         g_free (prefix);
491         return dir;
492 }
493
494
495 /** Locate the application's superuser binary folder.
496  *
497  * The path is generated by the following pseudo-code evaluation:
498  * \code
499  * prefix + "/sbin"
500  * \endcode
501  *
502  * @param default_sbin_dir  A default path which will used as fallback.
503  * @return A string containing the sbin folder's path, which must be freed when
504  *         no longer necessary. If BinReloc is not initialized, or if the
505  *         initialization function failed, then a copy of default_sbin_dir will
506  *         be returned. If default_bin_dir is NULL, then NULL will be returned.
507  */
508 gchar *
509 gbr_find_sbin_dir (const gchar *default_sbin_dir)
510 {
511         gchar *prefix, *dir;
512
513         prefix = gbr_find_prefix (NULL);
514         if (prefix == NULL) {
515                 /* BinReloc not initialized. */
516                 if (default_sbin_dir != NULL)
517                         return g_strdup (default_sbin_dir);
518                 else
519                         return NULL;
520         }
521
522         dir = g_build_filename (prefix, "sbin", NULL);
523         g_free (prefix);
524         return dir;
525 }
526
527
528 /** Locate the application's data folder.
529  *
530  * The path is generated by the following pseudo-code evaluation:
531  * \code
532  * prefix + "/share"
533  * \endcode
534  *
535  * @param default_data_dir  A default path which will used as fallback.
536  * @return A string containing the data folder's path, which must be freed when
537  *         no longer necessary. If BinReloc is not initialized, or if the
538  *         initialization function failed, then a copy of default_data_dir
539  *         will be returned. If default_data_dir is NULL, then NULL will be
540  *         returned.
541  */
542 gchar *
543 gbr_find_data_dir (const gchar *default_data_dir)
544 {
545         gchar *prefix, *dir;
546
547         prefix = gbr_find_prefix (NULL);
548         if (prefix == NULL) {
549                 /* BinReloc not initialized. */
550                 if (default_data_dir != NULL)
551                         return g_strdup (default_data_dir);
552                 else
553                         return NULL;
554         }
555
556         dir = g_build_filename (prefix, "share", NULL);
557         g_free (prefix);
558         return dir;
559 }
560
561
562 /** Locate the application's localization folder.
563  *
564  * The path is generated by the following pseudo-code evaluation:
565  * \code
566  * prefix + "/share/locale"
567  * \endcode
568  *
569  * @param default_locale_dir  A default path which will used as fallback.
570  * @return A string containing the localization folder's path, which must be freed when
571  *         no longer necessary. If BinReloc is not initialized, or if the
572  *         initialization function failed, then a copy of default_locale_dir will be returned.
573  *         If default_locale_dir is NULL, then NULL will be returned.
574  */
575 gchar *
576 gbr_find_locale_dir (const gchar *default_locale_dir)
577 {
578         gchar *data_dir, *dir;
579
580         data_dir = gbr_find_data_dir (NULL);
581         if (data_dir == NULL) {
582                 /* BinReloc not initialized. */
583                 if (default_locale_dir != NULL)
584                         return g_strdup (default_locale_dir);
585                 else
586                         return NULL;
587         }
588
589         dir = g_build_filename (data_dir, "locale", NULL);
590         g_free (data_dir);
591         return dir;
592 }
593
594
595 /** Locate the application's library folder.
596  *
597  * The path is generated by the following pseudo-code evaluation:
598  * \code
599  * prefix + "/lib"
600  * \endcode
601  *
602  * @param default_lib_dir  A default path which will used as fallback.
603  * @return A string containing the library folder's path, which must be freed when
604  *         no longer necessary. If BinReloc is not initialized, or if the
605  *         initialization function failed, then a copy of default_lib_dir will be returned.
606  *         If default_lib_dir is NULL, then NULL will be returned.
607  */
608 gchar *
609 gbr_find_lib_dir (const gchar *default_lib_dir)
610 {
611         gchar *prefix, *dir;
612
613         prefix = gbr_find_prefix (NULL);
614         if (prefix == NULL) {
615                 /* BinReloc not initialized. */
616                 if (default_lib_dir != NULL)
617                         return g_strdup (default_lib_dir);
618                 else
619                         return NULL;
620         }
621
622         dir = g_build_filename (prefix, "lib", NULL);
623         g_free (prefix);
624         return dir;
625 }
626
627
628 /** Locate the application's libexec folder.
629  *
630  * The path is generated by the following pseudo-code evaluation:
631  * \code
632  * prefix + "/libexec"
633  * \endcode
634  *
635  * @param default_libexec_dir  A default path which will used as fallback.
636  * @return A string containing the libexec folder's path, which must be freed when
637  *         no longer necessary. If BinReloc is not initialized, or if the initialization
638  *         function failed, then a copy of default_libexec_dir will be returned.
639  *         If default_libexec_dir is NULL, then NULL will be returned.
640  */
641 gchar *
642 gbr_find_libexec_dir (const gchar *default_libexec_dir)
643 {
644         gchar *prefix, *dir;
645
646         prefix = gbr_find_prefix (NULL);
647         if (prefix == NULL) {
648                 /* BinReloc not initialized. */
649                 if (default_libexec_dir != NULL)
650                         return g_strdup (default_libexec_dir);
651                 else
652                         return NULL;
653         }
654
655         dir = g_build_filename (prefix, "libexec", NULL);
656         g_free (prefix);
657         return dir;
658 }
659
660
661 /** Locate the application's configuration files folder.
662  *
663  * The path is generated by the following pseudo-code evaluation:
664  * \code
665  * prefix + "/etc"
666  * \endcode
667  *
668  * @param default_etc_dir  A default path which will used as fallback.
669  * @return A string containing the etc folder's path, which must be freed when
670  *         no longer necessary. If BinReloc is not initialized, or if the initialization
671  *         function failed, then a copy of default_etc_dir will be returned.
672  *         If default_etc_dir is NULL, then NULL will be returned.
673  */
674 gchar *
675 gbr_find_etc_dir (const gchar *default_etc_dir)
676 {
677         gchar *prefix, *dir;
678
679         prefix = gbr_find_prefix (NULL);
680         if (prefix == NULL) {
681                 /* BinReloc not initialized. */
682                 if (default_etc_dir != NULL)
683                         return g_strdup (default_etc_dir);
684                 else
685                         return NULL;
686         }
687
688         dir = g_build_filename (prefix, "etc", NULL);
689         g_free (prefix);
690         return dir;
691 }
692
693
694 G_END_DECLS
695
696 #endif /* __BINRELOC_C__ */