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