root/opal/mca/base/mca_base_component_repository.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. clf_constructor
  2. clf_destructor
  3. process_repository_item
  4. file_exists
  5. mca_base_component_repository_add
  6. mca_base_component_repository_init
  7. mca_base_component_repository_get_components
  8. mca_base_component_repository_release_internal
  9. find_component
  10. mca_base_component_repository_release
  11. mca_base_component_repository_retain_component
  12. mca_base_component_repository_open
  13. mca_base_component_repository_finalize
  14. ri_constructor
  15. ri_destructor

   1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
   2 /*
   3  * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
   4  *                         University Research and Technology
   5  *                         Corporation.  All rights reserved.
   6  * Copyright (c) 2004-2005 The University of Tennessee and The University
   7  *                         of Tennessee Research Foundation.  All rights
   8  *                         reserved.
   9  * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
  10  *                         University of Stuttgart.  All rights reserved.
  11  * Copyright (c) 2004-2005 The Regents of the University of California.
  12  *                         All rights reserved.
  13  * Copyright (c) 2008-2015 Cisco Systems, Inc.  All rights reserved.
  14  * Copyright (c) 2015      Los Alamos National Security, LLC. All rights
  15  *                         reserved.
  16  * Copyright (c) 2015      Research Organization for Information Science
  17  *                         and Technology (RIST). All rights reserved.
  18  * Copyright (c) 2017 IBM Corporation.  All rights reserved.
  19  * Copyright (c) 2018      Amazon.com, Inc. or its affiliates.  All Rights reserved.
  20  * $COPYRIGHT$
  21  *
  22  * Additional copyrights may follow
  23  *
  24  * $HEADER$
  25  */
  26 
  27 
  28 #include "opal_config.h"
  29 #ifdef HAVE_SYS_TYPES_H
  30 #include <sys/types.h>
  31 #endif
  32 #include <string.h>
  33 #include <stdlib.h>
  34 #include <stdio.h>
  35 #ifdef HAVE_UNISTD_H
  36 #include <unistd.h>
  37 #endif
  38 
  39 #include "opal/class/opal_list.h"
  40 #include "opal/mca/mca.h"
  41 #include "opal/mca/base/base.h"
  42 #include "opal/mca/base/mca_base_component_repository.h"
  43 #include "opal/mca/dl/base/base.h"
  44 #include "opal/constants.h"
  45 #include "opal/class/opal_hash_table.h"
  46 #include "opal/util/basename.h"
  47 #include "opal/util/string_copy.h"
  48 #include "opal/util/printf.h"
  49 
  50 #if OPAL_HAVE_DL_SUPPORT
  51 
  52 /*
  53  * Private types
  54  */
  55 static void ri_constructor(mca_base_component_repository_item_t *ri);
  56 static void ri_destructor(mca_base_component_repository_item_t *ri);
  57 OBJ_CLASS_INSTANCE(mca_base_component_repository_item_t, opal_list_item_t,
  58                    ri_constructor, ri_destructor);
  59 
  60 #endif /* OPAL_HAVE_DL_SUPPORT */
  61 
  62 static void clf_constructor(opal_object_t *obj);
  63 static void clf_destructor(opal_object_t *obj);
  64 
  65 OBJ_CLASS_INSTANCE(mca_base_failed_component_t, opal_list_item_t,
  66                    clf_constructor, clf_destructor);
  67 
  68 
  69 static void clf_constructor(opal_object_t *obj)
  70 {
  71     mca_base_failed_component_t *cli = (mca_base_failed_component_t *) obj;
  72     cli->comp = NULL;
  73     cli->error_msg = NULL;
  74 }
  75 
  76 static void clf_destructor(opal_object_t *obj)
  77 {
  78     mca_base_failed_component_t *cli = (mca_base_failed_component_t *) obj;
  79     cli->comp = NULL;
  80     if( NULL != cli->error_msg ) {
  81         free(cli->error_msg);
  82         cli->error_msg = NULL;
  83     }
  84 }
  85 
  86 /*
  87  * Private variables
  88  */
  89 static bool initialized = false;
  90 
  91 
  92 #if OPAL_HAVE_DL_SUPPORT
  93 
  94 static opal_hash_table_t mca_base_component_repository;
  95 
  96 /* two-level macro for stringifying a number */
  97 #define STRINGIFYX(x) #x
  98 #define STRINGIFY(x) STRINGIFYX(x)
  99 
 100 static int process_repository_item (const char *filename, void *data)
 101 {
 102     char name[MCA_BASE_MAX_COMPONENT_NAME_LEN + 1];
 103     char type[MCA_BASE_MAX_TYPE_NAME_LEN + 1];
 104     mca_base_component_repository_item_t *ri;
 105     opal_list_t *component_list;
 106     char *base;
 107     int ret;
 108 
 109     base = opal_basename (filename);
 110     if (NULL == base) {
 111         return OPAL_ERROR;
 112     }
 113 
 114     /* check if the plugin has the appropriate prefix */
 115     if (0 != strncmp (base, "mca_", 4)) {
 116         free (base);
 117         return OPAL_SUCCESS;
 118     }
 119 
 120     /* read framework and component names. framework names may not include an _
 121      * but component names may */
 122     ret = sscanf (base, "mca_%" STRINGIFY(MCA_BASE_MAX_TYPE_NAME_LEN) "[^_]_%"
 123                   STRINGIFY(MCA_BASE_MAX_COMPONENT_NAME_LEN) "s", type, name);
 124     if (0 > ret) {
 125         /* does not patch the expected template. skip */
 126         free(base);
 127         return OPAL_SUCCESS;
 128     }
 129 
 130     /* lookup the associated framework list and create if it doesn't already exist */
 131     ret = opal_hash_table_get_value_ptr (&mca_base_component_repository, type,
 132                                          strlen (type), (void **) &component_list);
 133     if (OPAL_SUCCESS != ret) {
 134         component_list = OBJ_NEW(opal_list_t);
 135         if (NULL == component_list) {
 136             free (base);
 137             /* OOM. nothing to do but fail */
 138             return OPAL_ERR_OUT_OF_RESOURCE;
 139         }
 140 
 141         ret = opal_hash_table_set_value_ptr (&mca_base_component_repository, type,
 142                                              strlen (type), (void *) component_list);
 143         if (OPAL_SUCCESS != ret) {
 144             free (base);
 145             OBJ_RELEASE(component_list);
 146             return ret;
 147         }
 148     }
 149 
 150     /* check for duplicate components */
 151     OPAL_LIST_FOREACH(ri, component_list, mca_base_component_repository_item_t) {
 152         if (0 == strcmp (ri->ri_name, name)) {
 153             /* already scanned this component */
 154             free (base);
 155             return OPAL_SUCCESS;
 156         }
 157     }
 158 
 159     ri = OBJ_NEW(mca_base_component_repository_item_t);
 160     if (NULL == ri) {
 161         free (base);
 162         return OPAL_ERR_OUT_OF_RESOURCE;
 163     }
 164 
 165     ri->ri_base = base;
 166 
 167     ri->ri_path = strdup (filename);
 168     if (NULL == ri->ri_path) {
 169         OBJ_RELEASE(ri);
 170         return OPAL_ERR_OUT_OF_RESOURCE;
 171     }
 172 
 173     opal_string_copy (ri->ri_type, type, MCA_BASE_MAX_TYPE_NAME_LEN);
 174     opal_string_copy (ri->ri_name, name, MCA_BASE_MAX_COMPONENT_NAME_LEN);
 175 
 176     opal_list_append (component_list, &ri->super);
 177 
 178     return OPAL_SUCCESS;
 179 }
 180 
 181 static int file_exists(const char *filename, const char *ext)
 182 {
 183     char *final;
 184     int ret;
 185 
 186     if (NULL == ext) {
 187         return access (filename, F_OK) == 0;
 188     }
 189 
 190     ret = opal_asprintf(&final, "%s.%s", filename, ext);
 191     if (0 > ret || NULL == final) {
 192         return 0;
 193     }
 194 
 195     ret = access (final, F_OK);
 196     free(final);
 197     return (0 == ret);
 198 }
 199 
 200 #endif /* OPAL_HAVE_DL_SUPPORT */
 201 
 202 int mca_base_component_repository_add (const char *path)
 203 {
 204 #if OPAL_HAVE_DL_SUPPORT
 205     char *path_to_use = NULL, *dir, *ctx;
 206     const char sep[] = {OPAL_ENV_SEP, '\0'};
 207 
 208     if (NULL == path) {
 209         /* nothing to do */
 210         return OPAL_SUCCESS;
 211     }
 212 
 213     path_to_use = strdup (path);
 214 
 215     dir = strtok_r (path_to_use, sep, &ctx);
 216     do {
 217         if ((0 == strcmp(dir, "USER_DEFAULT") || 0 == strcmp(dir, "USR_DEFAULT"))
 218             && NULL != mca_base_user_default_path) {
 219             dir = mca_base_user_default_path;
 220         } else if (0 == strcmp(dir, "SYS_DEFAULT") ||
 221                    0 == strcmp(dir, "SYSTEM_DEFAULT")) {
 222             dir = mca_base_system_default_path;
 223         }
 224 
 225         if (0 != opal_dl_foreachfile(dir, process_repository_item, NULL)) {
 226             break;
 227         }
 228     } while (NULL != (dir = strtok_r (NULL, sep, &ctx)));
 229 
 230     free (path_to_use);
 231 
 232 #endif /* OPAL_HAVE_DL_SUPPORT */
 233 
 234     return OPAL_SUCCESS;
 235 }
 236 
 237 
 238 /*
 239  * Initialize the repository
 240  */
 241 int mca_base_component_repository_init(void)
 242 {
 243   /* Setup internal structures */
 244 
 245   if (!initialized) {
 246 #if OPAL_HAVE_DL_SUPPORT
 247 
 248     /* Initialize the dl framework */
 249     int ret = mca_base_framework_open(&opal_dl_base_framework, 0);
 250     if (OPAL_SUCCESS != ret) {
 251         opal_output(0, "%s %d:%s failed -- process will likely abort (open the dl framework returned %d instead of OPAL_SUCCESS)\n",
 252                     __FILE__, __LINE__, __func__, ret);
 253         return ret;
 254     }
 255     opal_dl_base_select();
 256 
 257     OBJ_CONSTRUCT(&mca_base_component_repository, opal_hash_table_t);
 258     ret = opal_hash_table_init (&mca_base_component_repository, 128);
 259     if (OPAL_SUCCESS != ret) {
 260         (void) mca_base_framework_close (&opal_dl_base_framework);
 261         return ret;
 262     }
 263 
 264     ret = mca_base_component_repository_add (mca_base_component_path);
 265     if (OPAL_SUCCESS != ret) {
 266         OBJ_DESTRUCT(&mca_base_component_repository);
 267         (void) mca_base_framework_close (&opal_dl_base_framework);
 268         return ret;
 269     }
 270 #endif
 271 
 272     initialized = true;
 273   }
 274 
 275   /* All done */
 276 
 277   return OPAL_SUCCESS;
 278 }
 279 
 280 int mca_base_component_repository_get_components (mca_base_framework_t *framework,
 281                                                   opal_list_t **framework_components)
 282 {
 283     *framework_components = NULL;
 284 #if OPAL_HAVE_DL_SUPPORT
 285     return opal_hash_table_get_value_ptr (&mca_base_component_repository, framework->framework_name,
 286                                           strlen (framework->framework_name), (void **) framework_components);
 287 #endif
 288     return OPAL_ERR_NOT_FOUND;
 289 }
 290 
 291 #if OPAL_HAVE_DL_SUPPORT
 292 static void mca_base_component_repository_release_internal (mca_base_component_repository_item_t *ri) {
 293     int group_id;
 294 
 295     group_id = mca_base_var_group_find (NULL, ri->ri_type, ri->ri_name);
 296     if (0 <= group_id) {
 297         /* ensure all variables are deregistered before we dlclose the component */
 298         mca_base_var_group_deregister (group_id);
 299     }
 300 
 301     /* Close the component (and potentially unload it from memory */
 302     if (ri->ri_dlhandle) {
 303         opal_dl_close(ri->ri_dlhandle);
 304         ri->ri_dlhandle = NULL;
 305     }
 306 }
 307 #endif
 308 
 309 #if OPAL_HAVE_DL_SUPPORT
 310 static mca_base_component_repository_item_t *find_component (const char *type, const char *name)
 311 {
 312     mca_base_component_repository_item_t *ri;
 313     opal_list_t *component_list;
 314     int ret;
 315 
 316     ret = opal_hash_table_get_value_ptr (&mca_base_component_repository, type,
 317                                          strlen (type), (void **) &component_list);
 318     if (OPAL_SUCCESS != ret) {
 319         /* component does not exist in the repository */
 320         return NULL;
 321     }
 322 
 323     OPAL_LIST_FOREACH(ri, component_list, mca_base_component_repository_item_t) {
 324         if (0 == strcmp (ri->ri_name, name)) {
 325             return ri;
 326         }
 327     }
 328 
 329     return NULL;
 330 }
 331 #endif
 332 
 333 void mca_base_component_repository_release(const mca_base_component_t *component)
 334 {
 335 #if OPAL_HAVE_DL_SUPPORT
 336     mca_base_component_repository_item_t *ri;
 337 
 338     ri = find_component (component->mca_type_name, component->mca_component_name);
 339     if (NULL != ri && !(--ri->ri_refcnt)) {
 340         mca_base_component_repository_release_internal (ri);
 341     }
 342 #endif
 343 }
 344 
 345 int mca_base_component_repository_retain_component (const char *type, const char *name)
 346 {
 347 #if OPAL_HAVE_DL_SUPPORT
 348     mca_base_component_repository_item_t *ri = find_component(type, name);
 349 
 350     if (NULL != ri) {
 351         ++ri->ri_refcnt;
 352         return OPAL_SUCCESS;
 353     }
 354 
 355     return OPAL_ERR_NOT_FOUND;
 356 #else
 357     return OPAL_ERR_NOT_SUPPORTED;
 358 #endif
 359 }
 360 
 361 int mca_base_component_repository_open (mca_base_framework_t *framework,
 362                                         mca_base_component_repository_item_t *ri)
 363 {
 364 #if OPAL_HAVE_DL_SUPPORT
 365     mca_base_component_t *component_struct;
 366     mca_base_component_list_item_t *mitem = NULL;
 367     char *struct_name = NULL;
 368     int vl, ret;
 369 
 370     opal_output_verbose(MCA_BASE_VERBOSE_INFO, 0, "mca_base_component_repository_open: examining dynamic "
 371                         "%s MCA component \"%s\" at path %s", ri->ri_type, ri->ri_name, ri->ri_path);
 372 
 373     vl = mca_base_component_show_load_errors ? MCA_BASE_VERBOSE_ERROR : MCA_BASE_VERBOSE_INFO;
 374 
 375     /* Ensure that this component is not already loaded (should only happen
 376        if it was statically loaded).  It's an error if it's already
 377        loaded because we're evaluating this file -- not this component.
 378        Hence, returning OPAL_ERR_PARAM indicates that the *file* failed
 379        to load, not the component. */
 380 
 381     OPAL_LIST_FOREACH(mitem, &framework->framework_components, mca_base_component_list_item_t) {
 382         if (0 == strcmp(mitem->cli_component->mca_component_name, ri->ri_name)) {
 383             opal_output_verbose (MCA_BASE_VERBOSE_INFO, 0, "mca_base_component_repository_open: already loaded (ignored)");
 384             return OPAL_ERR_BAD_PARAM;
 385         }
 386     }
 387 
 388     /* silence coverity issue (invalid free) */
 389     mitem = NULL;
 390 
 391     if (NULL != ri->ri_dlhandle) {
 392         opal_output_verbose (MCA_BASE_VERBOSE_INFO, 0, "mca_base_component_repository_open: already loaded. returning cached component");
 393         mitem = OBJ_NEW(mca_base_component_list_item_t);
 394         if (NULL == mitem) {
 395             return OPAL_ERR_OUT_OF_RESOURCE;
 396         }
 397 
 398         mitem->cli_component = ri->ri_component_struct;
 399         opal_list_append (&framework->framework_components, &mitem->super);
 400 
 401         return OPAL_SUCCESS;
 402     }
 403 
 404     if (0 != strcmp (ri->ri_type, framework->framework_name)) {
 405         /* shouldn't happen. attempting to open a component belonging to
 406          * another framework. if this happens it is likely a MCA base
 407          * bug so assert */
 408         assert (0);
 409         return OPAL_ERR_NOT_SUPPORTED;
 410     }
 411 
 412     /* Now try to load the component */
 413 
 414     char *err_msg = NULL;
 415     if (OPAL_SUCCESS != opal_dl_open(ri->ri_path, true, false, &ri->ri_dlhandle, &err_msg)) {
 416         if (NULL == err_msg) {
 417             err_msg = "opal_dl_open() error message was NULL!";
 418         }
 419         /* Because libltdl erroneously says "file not found" for any
 420            type of error -- which is especially misleading when the file
 421            is actually there but cannot be opened for some other reason
 422            (e.g., missing symbol) -- do some simple huersitics and if
 423            the file [probably] does exist, print a slightly better error
 424            message. */
 425         if (0 == strcasecmp("file not found", err_msg) &&
 426             (file_exists(ri->ri_path, "lo") ||
 427              file_exists(ri->ri_path, "so") ||
 428              file_exists(ri->ri_path, "dylib") ||
 429              file_exists(ri->ri_path, "dll"))) {
 430             err_msg = "perhaps a missing symbol, or compiled for a different version of Open MPI?";
 431         }
 432         opal_output_verbose(vl, 0, "mca_base_component_repository_open: unable to open %s: %s (ignored)",
 433                             ri->ri_base, err_msg);
 434 
 435         if( mca_base_component_track_load_errors ) {
 436             mca_base_failed_component_t *f_comp = OBJ_NEW(mca_base_failed_component_t);
 437             f_comp->comp = ri;
 438             opal_asprintf(&(f_comp->error_msg), "%s", err_msg);
 439             opal_list_append(&framework->framework_failed_components, &f_comp->super);
 440         }
 441 
 442         return OPAL_ERR_BAD_PARAM;
 443     }
 444 
 445     /* Successfully opened the component; now find the public struct.
 446        Malloc out enough space for it. */
 447 
 448     do {
 449         ret = opal_asprintf (&struct_name, "mca_%s_%s_component", ri->ri_type, ri->ri_name);
 450         if (0 > ret) {
 451             ret = OPAL_ERR_OUT_OF_RESOURCE;
 452             break;
 453         }
 454 
 455         mitem = OBJ_NEW(mca_base_component_list_item_t);
 456         if (NULL == mitem) {
 457             ret = OPAL_ERR_OUT_OF_RESOURCE;
 458             break;
 459         }
 460 
 461         err_msg = NULL;
 462         ret = opal_dl_lookup(ri->ri_dlhandle, struct_name, (void**) &component_struct, &err_msg);
 463         if (OPAL_SUCCESS != ret || NULL == component_struct) {
 464             if (NULL == err_msg) {
 465                 err_msg = "opal_dl_loookup() error message was NULL!";
 466             }
 467             opal_output_verbose(vl, 0, "mca_base_component_repository_open: \"%s\" does not appear to be a valid "
 468                                 "%s MCA dynamic component (ignored): %s. ret %d", ri->ri_base, ri->ri_type, err_msg, ret);
 469 
 470             ret = OPAL_ERR_BAD_PARAM;
 471             break;
 472         }
 473 
 474         /* done with the structure name */
 475         free (struct_name);
 476         struct_name = NULL;
 477 
 478         /* We found the public struct.  Make sure its MCA major.minor
 479            version is the same as ours. TODO -- add checks for project version (from framework) */
 480         if (!(MCA_BASE_VERSION_MAJOR == component_struct->mca_major_version &&
 481               MCA_BASE_VERSION_MINOR == component_struct->mca_minor_version)) {
 482             opal_output_verbose(vl, 0, "mca_base_component_repository_open: %s \"%s\" uses an MCA interface that is "
 483                                 "not recognized (component MCA v%d.%d.%d != supported MCA v%d.%d.%d) -- ignored",
 484                                 ri->ri_type, ri->ri_path, component_struct->mca_major_version,
 485                                 component_struct->mca_minor_version, component_struct->mca_release_version,
 486                                 MCA_BASE_VERSION_MAJOR, MCA_BASE_VERSION_MINOR, MCA_BASE_VERSION_RELEASE);
 487             ret = OPAL_ERR_BAD_PARAM;
 488             break;
 489         }
 490 
 491         /* Also check that the component struct framework and component
 492            names match the expected names from the filename */
 493         if (0 != strcmp(component_struct->mca_type_name, ri->ri_type) ||
 494             0 != strcmp(component_struct->mca_component_name, ri->ri_name)) {
 495             opal_output_verbose(vl, 0, "Component file data does not match filename: %s (%s / %s) != %s %s -- ignored",
 496                                 ri->ri_path, ri->ri_type, ri->ri_name,
 497                                 component_struct->mca_type_name,
 498                                 component_struct->mca_component_name);
 499             ret = OPAL_ERR_BAD_PARAM;
 500             break;
 501         }
 502 
 503         /* Alles gut.  Save the component struct, and register this
 504            component to be closed later. */
 505 
 506         ri->ri_component_struct = mitem->cli_component = component_struct;
 507         ri->ri_refcnt = 1;
 508         opal_list_append(&framework->framework_components, &mitem->super);
 509 
 510         opal_output_verbose (MCA_BASE_VERBOSE_INFO, 0, "mca_base_component_repository_open: opened dynamic %s MCA "
 511                              "component \"%s\"", ri->ri_type, ri->ri_name);
 512 
 513         return OPAL_SUCCESS;
 514     } while (0);
 515 
 516     if (mitem) {
 517         OBJ_RELEASE(mitem);
 518     }
 519 
 520     if (struct_name) {
 521         free (struct_name);
 522     }
 523 
 524     opal_dl_close (ri->ri_dlhandle);
 525     ri->ri_dlhandle = NULL;
 526 
 527     return ret;
 528 #else
 529 
 530     /* no dlopen support */
 531     return OPAL_ERR_NOT_SUPPORTED;
 532 #endif
 533 }
 534 
 535 /*
 536  * Finalize the repository -- close everything that's still open.
 537  */
 538 void mca_base_component_repository_finalize(void)
 539 {
 540     if (!initialized) {
 541         return;
 542     }
 543 
 544     initialized = false;
 545 
 546 #if OPAL_HAVE_DL_SUPPORT
 547     opal_list_t *component_list;
 548     void *node, *key;
 549     size_t key_size;
 550     int ret;
 551 
 552     ret = opal_hash_table_get_first_key_ptr (&mca_base_component_repository, &key, &key_size,
 553                                              (void **) &component_list, &node);
 554     while (OPAL_SUCCESS == ret) {
 555         OPAL_LIST_RELEASE(component_list);
 556         ret = opal_hash_table_get_next_key_ptr (&mca_base_component_repository, &key,
 557                                                 &key_size, (void **) &component_list,
 558                                                 node, &node);
 559     }
 560 
 561     (void) mca_base_framework_close(&opal_dl_base_framework);
 562     OBJ_DESTRUCT(&mca_base_component_repository);
 563 #endif
 564 }
 565 
 566 #if OPAL_HAVE_DL_SUPPORT
 567 
 568 /*
 569  * Basic sentinel values, and construct the inner list
 570  */
 571 static void ri_constructor (mca_base_component_repository_item_t *ri)
 572 {
 573     memset(ri->ri_type, 0, sizeof(ri->ri_type));
 574     ri->ri_dlhandle = NULL;
 575     ri->ri_component_struct = NULL;
 576     ri->ri_path = NULL;
 577 }
 578 
 579 
 580 /*
 581  * Close a component
 582  */
 583 static void ri_destructor (mca_base_component_repository_item_t *ri)
 584 {
 585     /* dlclose the component if it is still open */
 586     mca_base_component_repository_release_internal (ri);
 587 
 588     /* It should be obvious, but I'll state it anyway because it bit me
 589        during debugging: after the dlclose(), the mca_base_component_t
 590        pointer is no longer valid because it has [potentially] been
 591        unloaded from memory.  So don't try to use it.  :-) */
 592 
 593     if (ri->ri_path) {
 594         free (ri->ri_path);
 595     }
 596 
 597     if (ri->ri_base) {
 598         free (ri->ri_base);
 599     }
 600 }
 601 
 602 #endif /* OPAL_HAVE_DL_SUPPORT */

/* [<][>][^][v][top][bottom][index][help] */