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 */