Atrinik Client 2.5
toolkit/binreloc.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00039 #include <global.h>
00040 
00046 static char *
00047 _br_find_exe (BrInitError *error)
00048 {
00049 #ifndef ENABLE_BINRELOC
00050     if (error)
00051         *error = BR_INIT_ERROR_DISABLED;
00052     return NULL;
00053 #else
00054     char *path, *path2, *line, *result;
00055     size_t buf_size;
00056     ssize_t size;
00057     struct stat stat_buf;
00058     FILE *f;
00059 
00060     /* Read from /proc/self/exe (symlink) */
00061     if (sizeof (path) > SSIZE_MAX)
00062         buf_size = SSIZE_MAX - 1;
00063     else
00064         buf_size = PATH_MAX - 1;
00065     path = (char *) malloc (buf_size);
00066     if (path == NULL) {
00067         /* Cannot allocate memory. */
00068         if (error)
00069             *error = BR_INIT_ERROR_NOMEM;
00070         return NULL;
00071     }
00072     path2 = (char *) malloc (buf_size);
00073     if (path2 == NULL) {
00074         /* Cannot allocate memory. */
00075         if (error)
00076             *error = BR_INIT_ERROR_NOMEM;
00077         free (path);
00078         return NULL;
00079     }
00080 
00081     strncpy (path2, "/proc/self/exe", buf_size - 1);
00082 
00083     while (1) {
00084         int i;
00085 
00086         size = readlink (path2, path, buf_size - 1);
00087         if (size == -1) {
00088             /* Error. */
00089             free (path2);
00090             break;
00091         }
00092 
00093         /* readlink() success. */
00094         path[size] = '\0';
00095 
00096         /* Check whether the symlink's target is also a symlink.
00097          * We want to get the final target. */
00098         i = stat (path, &stat_buf);
00099         if (i == -1) {
00100             /* Error. */
00101             free (path2);
00102             break;
00103         }
00104 
00105         /* stat() success. */
00106         if (!S_ISLNK (stat_buf.st_mode)) {
00107             /* path is not a symlink. Done. */
00108             free (path2);
00109             return path;
00110         }
00111 
00112         /* path is a symlink. Continue loop and resolve this. */
00113         strncpy (path, path2, buf_size - 1);
00114     }
00115 
00116 
00117     /* readlink() or stat() failed; this can happen when the program is
00118      * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
00119 
00120     buf_size = PATH_MAX + 128;
00121     line = (char *) realloc (path, buf_size);
00122     if (line == NULL) {
00123         /* Cannot allocate memory. */
00124         free (path);
00125         if (error)
00126             *error = BR_INIT_ERROR_NOMEM;
00127         return NULL;
00128     }
00129 
00130     f = fopen ("/proc/self/maps", "r");
00131     if (f == NULL) {
00132         free (line);
00133         if (error)
00134             *error = BR_INIT_ERROR_OPEN_MAPS;
00135         return NULL;
00136     }
00137 
00138     /* The first entry should be the executable name. */
00139     result = fgets (line, (int) buf_size, f);
00140     if (result == NULL) {
00141         fclose (f);
00142         free (line);
00143         if (error)
00144             *error = BR_INIT_ERROR_READ_MAPS;
00145         return NULL;
00146     }
00147 
00148     /* Get rid of newline character. */
00149     buf_size = strlen (line);
00150     if (buf_size <= 0) {
00151         /* Huh? An empty string? */
00152         fclose (f);
00153         free (line);
00154         if (error)
00155             *error = BR_INIT_ERROR_INVALID_MAPS;
00156         return NULL;
00157     }
00158     if (line[buf_size - 1] == 10)
00159         line[buf_size - 1] = 0;
00160 
00161     /* Extract the filename; it is always an absolute path. */
00162     path = strchr (line, '/');
00163 
00164     /* Sanity check. */
00165     if (strstr (line, " r-xp ") == NULL || path == NULL) {
00166         fclose (f);
00167         free (line);
00168         if (error)
00169             *error = BR_INIT_ERROR_INVALID_MAPS;
00170         return NULL;
00171     }
00172 
00173     path = strdup (path);
00174     free (line);
00175     fclose (f);
00176     return path;
00177 #endif /* ENABLE_BINRELOC */
00178 }
00179 
00180 
00185 static char *
00186 _br_find_exe_for_symbol (const void *symbol, BrInitError *error)
00187 {
00188 #ifndef ENABLE_BINRELOC
00189     (void) symbol;
00190     if (error)
00191         *error = BR_INIT_ERROR_DISABLED;
00192     return (char *) NULL;
00193 #else
00194 #   define SIZE PATH_MAX + 100
00195     FILE *f;
00196     size_t address_string_len;
00197     char *address_string, line[SIZE], *found;
00198 
00199     (void) error;
00200 
00201     if (symbol == NULL)
00202         return (char *) NULL;
00203 
00204     f = fopen ("/proc/self/maps", "r");
00205     if (f == NULL)
00206         return (char *) NULL;
00207 
00208     address_string_len = 4;
00209     address_string = (char *) malloc (address_string_len);
00210     found = (char *) NULL;
00211 
00212     while (!feof (f)) {
00213         char *start_addr, *end_addr, *end_addr_end, *file;
00214         void *start_addr_p, *end_addr_p;
00215         size_t len;
00216 
00217         if (fgets (line, SIZE, f) == NULL)
00218             break;
00219 
00220         /* Sanity check. */
00221         if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
00222             continue;
00223 
00224         /* Parse line. */
00225         start_addr = line;
00226         end_addr = strchr (line, '-');
00227         file = strchr (line, '/');
00228 
00229         /* More sanity check. */
00230         if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
00231             continue;
00232 
00233         end_addr[0] = '\0';
00234         end_addr++;
00235         end_addr_end = strchr (end_addr, ' ');
00236         if (end_addr_end == NULL)
00237             continue;
00238 
00239         end_addr_end[0] = '\0';
00240         len = strlen (file);
00241         if (len == 0)
00242             continue;
00243         if (file[len - 1] == '\n')
00244             file[len - 1] = '\0';
00245 
00246         /* Get rid of "(deleted)" from the filename. */
00247         len = strlen (file);
00248         if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
00249             file[len - 10] = '\0';
00250 
00251         /* I don't know whether this can happen but better safe than sorry. */
00252         len = strlen (start_addr);
00253         if (len != strlen (end_addr))
00254             continue;
00255 
00256 
00257         /* Transform the addresses into a string in the form of 0xdeadbeef,
00258          * then transform that into a pointer. */
00259         if (address_string_len < len + 3) {
00260             address_string_len = len + 3;
00261             address_string = (char *) realloc (address_string, address_string_len);
00262         }
00263 
00264         memcpy (address_string, "0x", 2);
00265         memcpy (address_string + 2, start_addr, len);
00266         address_string[2 + len] = '\0';
00267         sscanf (address_string, "%p", &start_addr_p);
00268 
00269         memcpy (address_string, "0x", 2);
00270         memcpy (address_string + 2, end_addr, len);
00271         address_string[2 + len] = '\0';
00272         sscanf (address_string, "%p", &end_addr_p);
00273 
00274 
00275         if (symbol >= start_addr_p && symbol < end_addr_p) {
00276             found = file;
00277             break;
00278         }
00279     }
00280 
00281     free (address_string);
00282     fclose (f);
00283 
00284     if (found == NULL)
00285         return (char *) NULL;
00286     else
00287         return strdup (found);
00288 #endif /* ENABLE_BINRELOC */
00289 }
00290 
00291 static char *exe = (char *) NULL;
00292 
00293 
00308 int
00309 br_init (BrInitError *error)
00310 {
00311     exe = _br_find_exe (error);
00312     return exe != NULL;
00313 }
00314 
00315 
00330 int
00331 br_init_lib (BrInitError *error)
00332 {
00333     exe = _br_find_exe_for_symbol ((const void *) "", error);
00334     return exe != NULL;
00335 }
00336 
00337 
00347 char *
00348 br_find_exe (const char *default_exe)
00349 {
00350     if (exe == (char *) NULL) {
00351         /* BinReloc is not initialized. */
00352         if (default_exe != (const char *) NULL)
00353             return strdup (default_exe);
00354         else
00355             return (char *) NULL;
00356     }
00357     return strdup (exe);
00358 }
00359 
00360 
00375 char *
00376 br_find_exe_dir (const char *default_dir)
00377 {
00378     if (exe == NULL) {
00379         /* BinReloc not initialized. */
00380         if (default_dir != NULL)
00381             return strdup (default_dir);
00382         else
00383             return NULL;
00384     }
00385 
00386     return br_dirname (exe);
00387 }
00388 
00389 
00403 char *
00404 br_find_prefix (const char *default_prefix)
00405 {
00406     char *dir1, *dir2;
00407 
00408     if (exe == (char *) NULL) {
00409         /* BinReloc not initialized. */
00410         if (default_prefix != (const char *) NULL)
00411             return strdup (default_prefix);
00412         else
00413             return (char *) NULL;
00414     }
00415 
00416     dir1 = br_dirname (exe);
00417     dir2 = br_dirname (dir1);
00418     free (dir1);
00419     return dir2;
00420 }
00421 
00422 
00436 char *
00437 br_find_bin_dir (const char *default_bin_dir)
00438 {
00439     char *prefix, *dir;
00440 
00441     prefix = br_find_prefix ((const char *) NULL);
00442     if (prefix == (char *) NULL) {
00443         /* BinReloc not initialized. */
00444         if (default_bin_dir != (const char *) NULL)
00445             return strdup (default_bin_dir);
00446         else
00447             return (char *) NULL;
00448     }
00449 
00450     dir = br_build_path (prefix, "bin");
00451     free (prefix);
00452     return dir;
00453 }
00454 
00455 
00469 char *
00470 br_find_sbin_dir (const char *default_sbin_dir)
00471 {
00472     char *prefix, *dir;
00473 
00474     prefix = br_find_prefix ((const char *) NULL);
00475     if (prefix == (char *) NULL) {
00476         /* BinReloc not initialized. */
00477         if (default_sbin_dir != (const char *) NULL)
00478             return strdup (default_sbin_dir);
00479         else
00480             return (char *) NULL;
00481     }
00482 
00483     dir = br_build_path (prefix, "sbin");
00484     free (prefix);
00485     return dir;
00486 }
00487 
00488 
00503 char *
00504 br_find_data_dir (const char *default_data_dir)
00505 {
00506     char *prefix, *dir;
00507 
00508     prefix = br_find_prefix ((const char *) NULL);
00509     if (prefix == (char *) NULL) {
00510         /* BinReloc not initialized. */
00511         if (default_data_dir != (const char *) NULL)
00512             return strdup (default_data_dir);
00513         else
00514             return (char *) NULL;
00515     }
00516 
00517     dir = br_build_path (prefix, "share");
00518     free (prefix);
00519     return dir;
00520 }
00521 
00522 
00536 char *
00537 br_find_locale_dir (const char *default_locale_dir)
00538 {
00539     char *data_dir, *dir;
00540 
00541     data_dir = br_find_data_dir ((const char *) NULL);
00542     if (data_dir == (char *) NULL) {
00543         /* BinReloc not initialized. */
00544         if (default_locale_dir != (const char *) NULL)
00545             return strdup (default_locale_dir);
00546         else
00547             return (char *) NULL;
00548     }
00549 
00550     dir = br_build_path (data_dir, "locale");
00551     free (data_dir);
00552     return dir;
00553 }
00554 
00555 
00569 char *
00570 br_find_lib_dir (const char *default_lib_dir)
00571 {
00572     char *prefix, *dir;
00573 
00574     prefix = br_find_prefix ((const char *) NULL);
00575     if (prefix == (char *) NULL) {
00576         /* BinReloc not initialized. */
00577         if (default_lib_dir != (const char *) NULL)
00578             return strdup (default_lib_dir);
00579         else
00580             return (char *) NULL;
00581     }
00582 
00583     dir = br_build_path (prefix, "lib");
00584     free (prefix);
00585     return dir;
00586 }
00587 
00588 
00602 char *
00603 br_find_libexec_dir (const char *default_libexec_dir)
00604 {
00605     char *prefix, *dir;
00606 
00607     prefix = br_find_prefix ((const char *) NULL);
00608     if (prefix == (char *) NULL) {
00609         /* BinReloc not initialized. */
00610         if (default_libexec_dir != (const char *) NULL)
00611             return strdup (default_libexec_dir);
00612         else
00613             return (char *) NULL;
00614     }
00615 
00616     dir = br_build_path (prefix, "libexec");
00617     free (prefix);
00618     return dir;
00619 }
00620 
00621 
00635 char *
00636 br_find_etc_dir (const char *default_etc_dir)
00637 {
00638     char *prefix, *dir;
00639 
00640     prefix = br_find_prefix ((const char *) NULL);
00641     if (prefix == (char *) NULL) {
00642         /* BinReloc not initialized. */
00643         if (default_etc_dir != (const char *) NULL)
00644             return strdup (default_etc_dir);
00645         else
00646             return (char *) NULL;
00647     }
00648 
00649     dir = br_build_path (prefix, "etc");
00650     free (prefix);
00651     return dir;
00652 }
00653 
00654 
00655 /***********************
00656  * Utility functions
00657  ***********************/
00658 
00665 char *
00666 br_strcat (const char *str1, const char *str2)
00667 {
00668     char *result;
00669     size_t len1, len2;
00670 
00671     if (str1 == NULL)
00672         str1 = "";
00673     if (str2 == NULL)
00674         str2 = "";
00675 
00676     len1 = strlen (str1);
00677     len2 = strlen (str2);
00678 
00679     result = (char *) malloc (len1 + len2 + 1);
00680     memcpy (result, str1, len1);
00681     memcpy (result + len1, str2, len2);
00682     result[len1 + len2] = '\0';
00683 
00684     return result;
00685 }
00686 
00687 
00688 char *
00689 br_build_path (const char *dir, const char *file)
00690 {
00691     char *dir2, *result;
00692     size_t len;
00693     int must_free = 0;
00694 
00695     len = strlen (dir);
00696     if (len > 0 && dir[len - 1] != '/') {
00697         dir2 = br_strcat (dir, "/");
00698         must_free = 1;
00699     } else
00700         dir2 = (char *) dir;
00701 
00702     result = br_strcat (dir2, file);
00703     if (must_free)
00704         free (dir2);
00705     return result;
00706 }
00707 
00708 
00709 /* Emulates glibc's strndup() */
00710 static char *
00711 br_strndup (const char *str, size_t size)
00712 {
00713     char *result = (char *) NULL;
00714     size_t len;
00715 
00716     if (str == (const char *) NULL)
00717         return (char *) NULL;
00718 
00719     len = strlen (str);
00720     if (len == 0)
00721         return strdup ("");
00722     if (size > len)
00723         size = len;
00724 
00725     result = (char *) malloc (len + 1);
00726     memcpy (result, str, size);
00727     result[size] = '\0';
00728     return result;
00729 }
00730 
00731 
00744 char *
00745 br_dirname (const char *path)
00746 {
00747     char *end, *result;
00748 
00749     if (path == (const char *) NULL)
00750         return (char *) NULL;
00751 
00752     end = strrchr (path, '/');
00753     if (end == (const char *) NULL)
00754         return strdup (".");
00755 
00756     while (end > path && *end == '/')
00757         end--;
00758     result = br_strndup (path, end - path + 1);
00759     if (result[0] == 0) {
00760         free (result);
00761         return strdup ("/");
00762     } else
00763         return result;
00764 }