libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

extractor_plugpath.c (15197B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2002, 2003, 2004, 2005, 2006, 2009, 2012 Vidyut Samanta and Christian Grothoff
      4 
      5      libextractor is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libextractor is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libextractor; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19  */
     20 /**
     21  * @file main/extractor_plugpath.c
     22  * @brief determine path where plugins are installed
     23  * @author Christian Grothoff
     24  */
     25 
     26 #include "platform.h"
     27 #include "extractor.h"
     28 #include <dirent.h>
     29 #include <sys/types.h>
     30 #include <signal.h>
     31 #include <ltdl.h>
     32 #include <stdbool.h>
     33 
     34 #include "extractor_plugpath.h"
     35 #include "extractor_logging.h"
     36 
     37 /**
     38  * Function to call on paths.
     39  *
     40  * @param cls closure
     41  * @param path a directory path
     42  */
     43 typedef void (*EXTRACTOR_PathProcessor) (void *cls,
     44                                          const char *path);
     45 
     46 
     47 /**
     48  * Remove a trailing '/bin/' from 'in' (if present).
     49  *
     50  * @param in input string, modified
     51  * @return NULL if 'in' is NULL, otherwise 'in' with '/bin/' removed
     52  */
     53 static char *
     54 cut_bin (char *in)
     55 {
     56   size_t p;
     57 
     58   if (NULL == in)
     59     return NULL;
     60   p = strlen (in);
     61   if (p < 4)
     62     return in;
     63   if ( ('/' == in[p - 1]) ||
     64        ('\\' == in[p - 1]) )
     65     in[--p] = '\0';
     66   if (0 == strcmp (&in[p - 4],
     67                    "/bin"))
     68   {
     69     in[p - 4] = '\0';
     70     p -= 4;
     71   }
     72   else if (0 == strcmp (&in[p - 4],
     73                         "\\bin"))
     74   {
     75     in[p - 4] = '\0';
     76     p -= 4;
     77   }
     78   return in;
     79 }
     80 
     81 
     82 #if GNU_LINUX
     83 /**
     84  * Try to determine path by reading /proc/PID/exe or
     85  * /proc/PID/maps.
     86  *
     87  * Note that this may fail if LE is installed in one directory
     88  * and the binary linking against it sits elsewhere.
     89  */
     90 static char *
     91 get_path_from_proc_exe ()
     92 {
     93   char fn[64];
     94   char line[1024];
     95   char dir[1024];
     96   char *lnk;
     97   char *ret;
     98   char *lestr;
     99   ssize_t size;
    100   FILE *f;
    101 
    102   snprintf (fn,
    103             sizeof (fn),
    104             "/proc/%u/maps",
    105             getpid ());
    106   if (NULL != (f = fopen (fn, "r")))
    107   {
    108     while (NULL != fgets (line, 1024, f))
    109     {
    110       if ( (1 == sscanf (line,
    111                          "%*x-%*x %*c%*c%*c%*c %*x %*2x:%*2x %*u%*[ ]%1023s",
    112                          dir)) &&
    113            (NULL != (lestr = strstr (dir,
    114                                      "libextractor")) ) )
    115       {
    116         lestr[0] = '\0';
    117         fclose (f);
    118         return strdup (dir);
    119       }
    120     }
    121     fclose (f);
    122   }
    123   snprintf (fn,
    124             sizeof (fn),
    125             "/proc/%u/exe",
    126             getpid ());
    127   if (NULL == (lnk = malloc (1029))) /* 1024 + 6 for "/lib/" catenation */
    128     return NULL;
    129   size = readlink (fn, lnk, 1023);
    130   if ( (size <= 0) || (size >= 1024) )
    131   {
    132     free (lnk);
    133     return NULL;
    134   }
    135   lnk[size] = '\0';
    136   while ( ('/' != lnk[size]) &&
    137           (size > 0) )
    138     size--;
    139   if ( (size < 4) ||
    140        ('/' != lnk[size - 4]) )
    141   {
    142     /* not installed in "/bin/" -- binary path probably useless */
    143     free (lnk);
    144     return NULL;
    145   }
    146   lnk[size] = '\0';
    147   lnk = cut_bin (lnk);
    148   if (NULL == (ret = realloc (lnk, strlen (lnk) + 6)))
    149   {
    150     LOG_STRERROR ("realloc");
    151     free (lnk);
    152     return NULL;
    153   }
    154   strcat (ret, "/lib/"); /* guess "lib/" as the library dir */
    155   return ret;
    156 }
    157 
    158 
    159 #endif
    160 
    161 
    162 #if WINDOWS
    163 static HMODULE le_dll = NULL;
    164 
    165 BOOL WINAPI
    166 DllMain (HINSTANCE hinstDLL,
    167          DWORD fdwReason,
    168          LPVOID lpvReserved)
    169 {
    170   switch (fdwReason)
    171   {
    172   case DLL_PROCESS_ATTACH:
    173     le_dll = (HMODULE) hinstDLL;
    174     break;
    175   }
    176 
    177   return TRUE;
    178 }
    179 
    180 
    181 /**
    182  * Try to determine path with win32-specific function
    183  */
    184 static char *
    185 get_path_from_module_filename ()
    186 {
    187   char *path;
    188   char *ret;
    189   char *idx;
    190 
    191   if (NULL == (path = malloc (4103))) /* 4096+nil+6 for "/lib/" catenation */
    192     return NULL;
    193   GetModuleFileName (le_dll, path, 4096);
    194   idx = path + strlen (path);
    195   while ( (idx > path) &&
    196           ('\\' != *idx) &&
    197           ('/' != *idx) )
    198     idx--;
    199   *idx = '\0';
    200   path = cut_bin (path);
    201   if (NULL == (ret = realloc (path, strlen (path) + 6)))
    202   {
    203     LOG_STRERROR ("realloc");
    204     free (path);
    205     return NULL;
    206   }
    207   strcat (ret, "/lib/"); /* guess "lib/" as the library dir */
    208   return ret;
    209 }
    210 
    211 
    212 #endif
    213 
    214 
    215 #if DARWIN
    216 #include <dlfcn.h>
    217 #include <mach-o/dyld.h>
    218 
    219 /**
    220  * Signature of the '_NSGetExecutablePath" function.
    221  *
    222  * @param buf where to write the path
    223  * @param number of bytes available in 'buf'
    224  * @return 0 on success, otherwise desired number of bytes is stored in 'bufsize'
    225  */
    226 typedef int
    227 (*MyNSGetExecutablePathProto) (char *buf,
    228                                size_t *bufsize);
    229 
    230 
    231 /**
    232  * Try to obtain the path of our executable using '_NSGetExecutablePath'.
    233  *
    234  * @return NULL on error
    235  */
    236 static char *
    237 get_path_from_NSGetExecutablePath ()
    238 {
    239   static char zero;
    240   char *path;
    241   char *ret;
    242   size_t len;
    243   MyNSGetExecutablePathProto func;
    244 
    245   path = NULL;
    246   if (NULL == (func =
    247                  (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT,
    248                                                      "_NSGetExecutablePath")))
    249     return NULL;
    250   path = &zero;
    251   len = 0;
    252   /* get the path len, including the trailing \0 */
    253   (void) func (path, &len);
    254   if (0 == len)
    255     return NULL;
    256   if (NULL == (path = malloc (len)))
    257   {
    258     LOG_STRERROR ("malloc");
    259     return NULL;
    260   }
    261   if (0 != func (path, &len))
    262   {
    263     free (path);
    264     return NULL;
    265   }
    266   len = strlen (path);
    267   while ((path[len] != '/') && (len > 0))
    268     len--;
    269   path[len] = '\0';
    270   if (NULL != strstr (path, "/lib"))
    271     return path;
    272   path = cut_bin (path);
    273   if (NULL == (ret = realloc (path, strlen (path) + 5)))
    274   {
    275     LOG_STRERROR ("realloc");
    276     free (path);
    277     return NULL;
    278   }
    279   strcat (ret, "/lib/");
    280   return ret;
    281 }
    282 
    283 
    284 /**
    285  * Try to obtain the path of our executable using '_dyld_image' API.
    286  *
    287  * @return NULL on error
    288  */
    289 static char *
    290 get_path_from_dyld_image ()
    291 {
    292   const char *path;
    293   char *s;
    294   char *p;
    295   unsigned int i;
    296   int c;
    297 
    298   c = _dyld_image_count ();
    299   for (i = 0; i < c; i++)
    300   {
    301     if (((void *) _dyld_get_image_header (i)) != (void *) &_mh_dylib_header)
    302       continue;
    303     path = _dyld_get_image_name (i);
    304     if ( (NULL == path) || (0 == strlen (path)) )
    305       continue;
    306     if (NULL == (p = strdup (path)))
    307     {
    308       LOG_STRERROR ("strdup");
    309       return NULL;
    310     }
    311     s = p + strlen (p);
    312     while ( (s > p) && ('/' != *s) )
    313       s--;
    314     s++;
    315     *s = '\0';
    316     return p;
    317   }
    318   return NULL;
    319 }
    320 
    321 
    322 #endif
    323 
    324 
    325 /**
    326  * Return the actual path to a file found in the current
    327  * PATH environment variable.
    328  *
    329  * @return path to binary, NULL if not found
    330  */
    331 static char *
    332 get_path_from_PATH ()
    333 {
    334   struct stat sbuf;
    335   char *path;
    336   char *pos;
    337   char *end;
    338   char *buf;
    339   char *ret;
    340   const char *p;
    341 
    342   if (NULL == (p = getenv ("PATH")))
    343     return NULL;
    344   if (NULL == (path = strdup (p))) /* because we write on it */
    345   {
    346     LOG_STRERROR ("strdup");
    347     return NULL;
    348   }
    349   if (NULL == (buf = malloc (strlen (path) + 20)))
    350   {
    351     LOG_STRERROR ("malloc");
    352     free (path);
    353     return NULL;
    354   }
    355   pos = path;
    356   while (NULL != (end = strchr (pos, ':')))
    357   {
    358     *end = '\0';
    359     sprintf (buf, "%s/%s", pos, "extract");
    360     if (0 == stat (buf, &sbuf))
    361     {
    362       free (buf);
    363       if (NULL == (pos = strdup (pos)))
    364       {
    365         LOG_STRERROR ("strdup");
    366         free (path);
    367         return NULL;
    368       }
    369       free (path);
    370       pos = cut_bin (pos);
    371       if (NULL == (ret = realloc (pos, strlen (pos) + 6)))
    372       {
    373         LOG_STRERROR ("realloc");
    374         free (pos);
    375         return NULL;
    376       }
    377       strcat (ret, "/lib/");
    378       return ret;
    379     }
    380     pos = end + 1;
    381   }
    382   sprintf (buf, "%s/%s", pos, "extract");
    383   if (0 == stat (buf, &sbuf))
    384   {
    385     pos = strdup (pos);
    386     free (buf);
    387     free (path);
    388     if (NULL == pos)
    389       return NULL;
    390     pos = cut_bin (pos);
    391     if (NULL == (ret = realloc (pos, strlen (pos) + 6)))
    392     {
    393       LOG_STRERROR ("realloc");
    394       free (pos);
    395       return NULL;
    396     }
    397     strcat (ret, "/lib/");
    398     return ret;
    399   }
    400   free (buf);
    401   free (path);
    402   return NULL;
    403 }
    404 
    405 
    406 /**
    407  * Create a filename by appending 'fname' to 'path'.
    408  *
    409  * @param path the base path
    410  * @param fname the filename to append
    411  * @return '$path/$fname', NULL on error
    412  */
    413 static char *
    414 append_to_dir (const char *path,
    415                const char *fname)
    416 {
    417   char *ret;
    418   size_t slen;
    419 
    420   if (0 == (slen = strlen (path)))
    421     return NULL;
    422   if ('/' == fname[0])
    423     fname++;
    424   ret = malloc (slen + strlen (fname) + 2);
    425   if (NULL == ret)
    426     return NULL;
    427 #ifdef MINGW
    428   if ('\\' == path[slen - 1])
    429     sprintf (ret,
    430              "%s%s",
    431              path,
    432              fname);
    433   else
    434     sprintf (ret,
    435              "%s\\%s",
    436              path,
    437              fname);
    438 #else
    439   if ('/' == path[slen - 1])
    440     sprintf (ret,
    441              "%s%s",
    442              path,
    443              fname);
    444   else
    445     sprintf (ret,
    446              "%s/%s",
    447              path,
    448              fname);
    449 #endif
    450   return ret;
    451 }
    452 
    453 
    454 /**
    455  * Iterate over all paths where we expect to find GNU libextractor
    456  * plugins.
    457  *
    458  * @param pp function to call for each path
    459  * @param pp_cls cls argument for pp.
    460  */
    461 static void
    462 get_installation_paths (EXTRACTOR_PathProcessor pp,
    463                         void *pp_cls)
    464 {
    465   const char *p;
    466   char *path;
    467   char *prefix;
    468   char *d;
    469   char *saveptr;
    470   bool skip; /* true if we should skip GETENV */
    471 
    472   prefix = NULL;
    473 #if _GNU_SOURCE
    474   #define GETENV secure_getenv
    475     skip = false;
    476 #else
    477   #define GETENV getenv
    478     skip = (0 == geteuid ());
    479 #endif
    480 
    481   if ( (! skip) &&
    482        (NULL != (p = GETENV ("LIBEXTRACTOR_PREFIX"))) )
    483   {
    484     if (NULL == (d = strdup (p)))
    485     {
    486       LOG_STRERROR ("strdup");
    487       return;
    488     }
    489     for (prefix = strtok_r (d, ":", &saveptr);
    490          NULL != prefix;
    491          prefix = strtok_r (NULL, ":", &saveptr))
    492       pp (pp_cls, prefix);
    493     free (d);
    494     return;
    495   }
    496 #if GNU_LINUX
    497   if (NULL == prefix)
    498     prefix = get_path_from_proc_exe ();
    499 #endif
    500 #if WINDOWS
    501   if (NULL == prefix)
    502     prefix = get_path_from_module_filename ();
    503 #endif
    504 #if DARWIN
    505   if (NULL == prefix)
    506     prefix = get_path_from_NSGetExecutablePath ();
    507   if (NULL == prefix)
    508     prefix = get_path_from_dyld_image ();
    509 #endif
    510   if (NULL == prefix)
    511     prefix = get_path_from_PATH ();
    512   pp (pp_cls, PLUGININSTDIR);
    513   if (NULL == prefix)
    514     return;
    515   path = append_to_dir (prefix, PLUGINDIR);
    516   if (NULL != path)
    517   {
    518     if (0 != strcmp (path,
    519                      PLUGININSTDIR))
    520       pp (pp_cls, path);
    521     free (path);
    522   }
    523   free (prefix);
    524 }
    525 
    526 
    527 /**
    528  * Closure for #find_plugin_in_path().
    529  */
    530 struct SearchContext
    531 {
    532   /**
    533    * Name of the plugin we are looking for.
    534    */
    535   const char *short_name;
    536 
    537   /**
    538    * Location for storing the path to the plugin upon success.
    539    */
    540   char *path;
    541 };
    542 
    543 
    544 /**
    545  * Load all plugins from the given directory.
    546  *
    547  * @param cls pointer to the "struct EXTRACTOR_PluginList*" to extend
    548  * @param path path to a directory with plugins
    549  */
    550 static void
    551 find_plugin_in_path (void *cls,
    552                      const char *path)
    553 {
    554   struct SearchContext *sc = cls;
    555   DIR *dir;
    556   struct dirent *ent;
    557   const char *sym_name;
    558   char *sym;
    559   char *dot;
    560   size_t dlen;
    561 
    562   if (NULL != sc->path)
    563     return;
    564   if (NULL == (dir = opendir (path)))
    565     return;
    566   while (NULL != (ent = readdir (dir)))
    567   {
    568     if ('.' == ent->d_name[0])
    569       continue;
    570     dlen = strlen (ent->d_name);
    571     if ( (dlen < 4) ||
    572          ( (0 != strcmp (&ent->d_name[dlen - 3], ".so")) &&
    573            (0 != strcasecmp (&ent->d_name[dlen - 4], ".dll")) ) )
    574       continue; /* only load '.so' and '.dll' */
    575     if (NULL == (sym_name = strrchr (ent->d_name, '_')))
    576       continue;
    577     sym_name++;
    578     if (NULL == (sym = strdup (sym_name)))
    579     {
    580       LOG_STRERROR ("strdup");
    581       closedir (dir);
    582       return;
    583     }
    584     dot = strchr (sym, '.');
    585     if (NULL != dot)
    586       *dot = '\0';
    587     if (0 == strcmp (sym, sc->short_name))
    588     {
    589       sc->path = append_to_dir (path, ent->d_name);
    590       free (sym);
    591       break;
    592     }
    593     free (sym);
    594   }
    595   closedir (dir);
    596 }
    597 
    598 
    599 /**
    600  * Given a short name of a library (i.e. "mime"), find
    601  * the full path of the respective plugin.
    602  */
    603 char *
    604 EXTRACTOR_find_plugin_ (const char *short_name)
    605 {
    606   struct SearchContext sc;
    607 
    608   sc.path = NULL;
    609   sc.short_name = short_name;
    610   get_installation_paths (&find_plugin_in_path,
    611                           &sc);
    612   return sc.path;
    613 }
    614 
    615 
    616 /**
    617  * Closure for #load_plugins_from_dir().
    618  */
    619 struct DefaultLoaderContext
    620 {
    621   /**
    622    * Accumulated result list.
    623    */
    624   struct EXTRACTOR_PluginList *res;
    625 
    626   /**
    627    * Flags to use for all plugins.
    628    */
    629   enum EXTRACTOR_Options flags;
    630 };
    631 
    632 
    633 /**
    634  * Load all plugins from the given directory.
    635  *
    636  * @param cls pointer to the "struct EXTRACTOR_PluginList*" to extend
    637  * @param path path to a directory with plugins
    638  */
    639 static void
    640 load_plugins_from_dir (void *cls,
    641                        const char *path)
    642 {
    643   struct DefaultLoaderContext *dlc = cls;
    644   DIR *dir;
    645   struct dirent *ent;
    646   const char *sym_name;
    647   char *sym;
    648   char *dot;
    649   size_t dlen;
    650 
    651   if (NULL == (dir = opendir (path)))
    652     return;
    653   while (NULL != (ent = readdir (dir)))
    654   {
    655     if (ent->d_name[0] == '.')
    656       continue;
    657     dlen = strlen (ent->d_name);
    658     if ( (dlen < 4) ||
    659          ( (0 != strcmp (&ent->d_name[dlen - 3], ".so")) &&
    660            (0 != strcasecmp (&ent->d_name[dlen - 4], ".dll")) ) )
    661       continue; /* only load '.so' and '.dll' */
    662     if (NULL == (sym_name = strrchr (ent->d_name, '_')))
    663       continue;
    664     sym_name++;
    665     if (NULL == (sym = strdup (sym_name)))
    666     {
    667       LOG_STRERROR ("strdup");
    668       closedir (dir);
    669       return;
    670     }
    671     if (NULL != (dot = strchr (sym, '.')))
    672       *dot = '\0';
    673     dlc->res = EXTRACTOR_plugin_add (dlc->res,
    674                                      sym,
    675                                      NULL,
    676                                      dlc->flags);
    677     free (sym);
    678   }
    679   closedir (dir);
    680 }
    681 
    682 
    683 /**
    684  * Load the default set of plugins. The default can be changed
    685  * by setting the LIBEXTRACTOR_LIBRARIES environment variable.
    686  * If it is set to "env", then this function will return
    687  * #EXTRACTOR_plugin_add_config(NULL, env, flags).  Otherwise,
    688  * it will load all of the installed plugins and return them.
    689  *
    690  * @param flags options for all of the plugins loaded
    691  * @return the default set of plugins, NULL if no plugins were found
    692  */
    693 struct EXTRACTOR_PluginList *
    694 EXTRACTOR_plugin_add_defaults (enum EXTRACTOR_Options flags)
    695 {
    696   struct DefaultLoaderContext dlc;
    697   char *env;
    698 
    699   env = getenv ("LIBEXTRACTOR_LIBRARIES");
    700   if (NULL != env)
    701     return EXTRACTOR_plugin_add_config (NULL, env, flags);
    702   dlc.res = NULL;
    703   dlc.flags = flags;
    704   get_installation_paths (&load_plugins_from_dir,
    705                           &dlc);
    706   return dlc.res;
    707 }
    708 
    709 
    710 /* end of extractor_plugpath.c */