libextractor

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

extractor_plugpath.c (14967B)


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