libextractor

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

extractor_plugpath.c (15707B)


      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 #ifdef HAVE_SECURE_GETENV
    474   /* secure_getenv() already ignores the environment in secure execution
    475      mode, so we never have to suppress the lookup ourselves */
    476   #define GETENV secure_getenv
    477     skip = false;
    478 #elif WINDOWS
    479   /* no set-user-ID/set-group-ID notion to worry about */
    480   #define GETENV getenv
    481     skip = false;
    482 #elif defined(HAVE_ISSETUGID)
    483   #define GETENV getenv
    484     skip = (0 != issetugid ());
    485 #else
    486   /* poor man's approximation of the secure execution mode test
    487      that secure_getenv() would do for us */
    488   #define GETENV getenv
    489     skip = ( (getuid () != geteuid ()) ||
    490              (getgid () != getegid ()) );
    491 #endif
    492 
    493   if ( (! skip) &&
    494        (NULL != (p = GETENV ("LIBEXTRACTOR_PREFIX"))) )
    495   {
    496     if (NULL == (d = strdup (p)))
    497     {
    498       LOG_STRERROR ("strdup");
    499       return;
    500     }
    501     for (prefix = strtok_r (d, ":", &saveptr);
    502          NULL != prefix;
    503          prefix = strtok_r (NULL, ":", &saveptr))
    504       pp (pp_cls, prefix);
    505     free (d);
    506     return;
    507   }
    508 #if GNU_LINUX
    509   if (NULL == prefix)
    510     prefix = get_path_from_proc_exe ();
    511 #endif
    512 #if WINDOWS
    513   if (NULL == prefix)
    514     prefix = get_path_from_module_filename ();
    515 #endif
    516 #if DARWIN
    517   if (NULL == prefix)
    518     prefix = get_path_from_NSGetExecutablePath ();
    519   if (NULL == prefix)
    520     prefix = get_path_from_dyld_image ();
    521 #endif
    522   if (NULL == prefix)
    523     prefix = get_path_from_PATH ();
    524   pp (pp_cls, PLUGININSTDIR);
    525   if (NULL == prefix)
    526     return;
    527   path = append_to_dir (prefix, PLUGINDIR);
    528   if (NULL != path)
    529   {
    530     if (0 != strcmp (path,
    531                      PLUGININSTDIR))
    532       pp (pp_cls, path);
    533     free (path);
    534   }
    535   free (prefix);
    536 }
    537 
    538 
    539 /**
    540  * Closure for #find_plugin_in_path().
    541  */
    542 struct SearchContext
    543 {
    544   /**
    545    * Name of the plugin we are looking for.
    546    */
    547   const char *short_name;
    548 
    549   /**
    550    * Location for storing the path to the plugin upon success.
    551    */
    552   char *path;
    553 };
    554 
    555 
    556 /**
    557  * Load all plugins from the given directory.
    558  *
    559  * @param cls pointer to the "struct EXTRACTOR_PluginList*" to extend
    560  * @param path path to a directory with plugins
    561  */
    562 static void
    563 find_plugin_in_path (void *cls,
    564                      const char *path)
    565 {
    566   struct SearchContext *sc = cls;
    567   DIR *dir;
    568   struct dirent *ent;
    569   const char *sym_name;
    570   char *sym;
    571   char *dot;
    572   size_t dlen;
    573 
    574   if (NULL != sc->path)
    575     return;
    576   if (NULL == (dir = opendir (path)))
    577     return;
    578   while (NULL != (ent = readdir (dir)))
    579   {
    580     if ('.' == ent->d_name[0])
    581       continue;
    582     dlen = strlen (ent->d_name);
    583     if ( (dlen < 4) ||
    584          ( (0 != strcmp (&ent->d_name[dlen - 3], ".so")) &&
    585            (0 != strcasecmp (&ent->d_name[dlen - 4], ".dll")) ) )
    586       continue; /* only load '.so' and '.dll' */
    587     if (NULL == (sym_name = strrchr (ent->d_name, '_')))
    588       continue;
    589     sym_name++;
    590     if (NULL == (sym = strdup (sym_name)))
    591     {
    592       LOG_STRERROR ("strdup");
    593       closedir (dir);
    594       return;
    595     }
    596     dot = strchr (sym, '.');
    597     if (NULL != dot)
    598       *dot = '\0';
    599     if (0 == strcmp (sym, sc->short_name))
    600     {
    601       sc->path = append_to_dir (path, ent->d_name);
    602       free (sym);
    603       break;
    604     }
    605     free (sym);
    606   }
    607   closedir (dir);
    608 }
    609 
    610 
    611 /**
    612  * Given a short name of a library (i.e. "mime"), find
    613  * the full path of the respective plugin.
    614  */
    615 char *
    616 EXTRACTOR_find_plugin_ (const char *short_name)
    617 {
    618   struct SearchContext sc;
    619 
    620   sc.path = NULL;
    621   sc.short_name = short_name;
    622   get_installation_paths (&find_plugin_in_path,
    623                           &sc);
    624   return sc.path;
    625 }
    626 
    627 
    628 /**
    629  * Closure for #load_plugins_from_dir().
    630  */
    631 struct DefaultLoaderContext
    632 {
    633   /**
    634    * Accumulated result list.
    635    */
    636   struct EXTRACTOR_PluginList *res;
    637 
    638   /**
    639    * Flags to use for all plugins.
    640    */
    641   enum EXTRACTOR_Options flags;
    642 };
    643 
    644 
    645 /**
    646  * Load all plugins from the given directory.
    647  *
    648  * @param cls pointer to the "struct EXTRACTOR_PluginList*" to extend
    649  * @param path path to a directory with plugins
    650  */
    651 static void
    652 load_plugins_from_dir (void *cls,
    653                        const char *path)
    654 {
    655   struct DefaultLoaderContext *dlc = cls;
    656   DIR *dir;
    657   struct dirent *ent;
    658   const char *sym_name;
    659   char *sym;
    660   char *dot;
    661   size_t dlen;
    662 
    663   if (NULL == (dir = opendir (path)))
    664     return;
    665   while (NULL != (ent = readdir (dir)))
    666   {
    667     if (ent->d_name[0] == '.')
    668       continue;
    669     dlen = strlen (ent->d_name);
    670     if ( (dlen < 4) ||
    671          ( (0 != strcmp (&ent->d_name[dlen - 3], ".so")) &&
    672            (0 != strcasecmp (&ent->d_name[dlen - 4], ".dll")) ) )
    673       continue; /* only load '.so' and '.dll' */
    674     if (NULL == (sym_name = strrchr (ent->d_name, '_')))
    675       continue;
    676     sym_name++;
    677     if (NULL == (sym = strdup (sym_name)))
    678     {
    679       LOG_STRERROR ("strdup");
    680       closedir (dir);
    681       return;
    682     }
    683     if (NULL != (dot = strchr (sym, '.')))
    684       *dot = '\0';
    685     dlc->res = EXTRACTOR_plugin_add (dlc->res,
    686                                      sym,
    687                                      NULL,
    688                                      dlc->flags);
    689     free (sym);
    690   }
    691   closedir (dir);
    692 }
    693 
    694 
    695 /**
    696  * Load the default set of plugins. The default can be changed
    697  * by setting the LIBEXTRACTOR_LIBRARIES environment variable.
    698  * If it is set to "env", then this function will return
    699  * #EXTRACTOR_plugin_add_config(NULL, env, flags).  Otherwise,
    700  * it will load all of the installed plugins and return them.
    701  *
    702  * @param flags options for all of the plugins loaded
    703  * @return the default set of plugins, NULL if no plugins were found
    704  */
    705 struct EXTRACTOR_PluginList *
    706 EXTRACTOR_plugin_add_defaults (enum EXTRACTOR_Options flags)
    707 {
    708   struct DefaultLoaderContext dlc;
    709   char *env;
    710 
    711   env = getenv ("LIBEXTRACTOR_LIBRARIES");
    712   if (NULL != env)
    713     return EXTRACTOR_plugin_add_config (NULL, env, flags);
    714   dlc.res = NULL;
    715   dlc.flags = flags;
    716   get_installation_paths (&load_plugins_from_dir,
    717                           &dlc);
    718   return dlc.res;
    719 }
    720 
    721 
    722 /* end of extractor_plugpath.c */