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