| /* |
| * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. |
| * Copyright (c) 2006 Cisco Systems, Inc. All rights reserved. |
| * Copyright (c) 2018 Mellanox Technologies, Ltd. All rights reserved. |
| * |
| * This software is available to you under a choice of one of two |
| * licenses. You may choose to be licensed under the terms of the GNU |
| * General Public License (GPL) Version 2, available from the file |
| * COPYING in the main directory of this source tree, or the |
| * OpenIB.org BSD license below: |
| * |
| * Redistribution and use in source and binary forms, with or |
| * without modification, are permitted provided that the following |
| * conditions are met: |
| * |
| * - Redistributions of source code must retain the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials |
| * provided with the distribution. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| #ifndef _STATIC_LIBRARY_BUILD_ |
| #define _GNU_SOURCE |
| |
| #include <dlfcn.h> |
| #include <stdio.h> |
| #include <dirent.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <ccan/list.h> |
| |
| #include "ibverbs.h" |
| |
| struct ibv_driver_name { |
| struct list_node entry; |
| char *name; |
| }; |
| |
| static LIST_HEAD(driver_name_list); |
| |
| static void read_config_file(const char *path) |
| { |
| FILE *conf; |
| char *line = NULL; |
| char *config; |
| char *field; |
| size_t buflen = 0; |
| ssize_t len; |
| |
| conf = fopen(path, "r" STREAM_CLOEXEC); |
| if (!conf) { |
| fprintf(stderr, PFX "Warning: couldn't read config file %s.\n", |
| path); |
| return; |
| } |
| |
| while ((len = getline(&line, &buflen, conf)) != -1) { |
| config = line + strspn(line, "\t "); |
| if (config[0] == '\n' || config[0] == '#') |
| continue; |
| |
| field = strsep(&config, "\n\t "); |
| |
| if (strcmp(field, "driver") == 0 && config != NULL) { |
| struct ibv_driver_name *driver_name; |
| |
| config += strspn(config, "\t "); |
| field = strsep(&config, "\n\t "); |
| |
| driver_name = malloc(sizeof(*driver_name)); |
| if (!driver_name) { |
| fprintf(stderr, |
| PFX |
| "Warning: couldn't allocate driver name '%s'.\n", |
| field); |
| continue; |
| } |
| |
| driver_name->name = strdup(field); |
| if (!driver_name->name) { |
| fprintf(stderr, |
| PFX |
| "Warning: couldn't allocate driver name '%s'.\n", |
| field); |
| free(driver_name); |
| continue; |
| } |
| |
| list_add(&driver_name_list, &driver_name->entry); |
| } else |
| fprintf(stderr, |
| PFX |
| "Warning: ignoring bad config directive '%s' in file '%s'.\n", |
| field, path); |
| } |
| |
| if (line) |
| free(line); |
| fclose(conf); |
| } |
| |
| static void read_config(void) |
| { |
| DIR *conf_dir; |
| struct dirent *dent; |
| char *path; |
| |
| conf_dir = opendir(IBV_CONFIG_DIR); |
| if (!conf_dir) { |
| fprintf(stderr, |
| PFX "Warning: couldn't open config directory '%s'.\n", |
| IBV_CONFIG_DIR); |
| return; |
| } |
| |
| while ((dent = readdir(conf_dir))) { |
| struct stat buf; |
| |
| if (asprintf(&path, "%s/%s", IBV_CONFIG_DIR, dent->d_name) < |
| 0) { |
| fprintf(stderr, |
| PFX |
| "Warning: couldn't read config file %s/%s.\n", |
| IBV_CONFIG_DIR, dent->d_name); |
| goto out; |
| } |
| |
| if (stat(path, &buf)) { |
| fprintf(stderr, |
| PFX |
| "Warning: couldn't stat config file '%s'.\n", |
| path); |
| goto next; |
| } |
| |
| if (!S_ISREG(buf.st_mode)) |
| goto next; |
| |
| read_config_file(path); |
| next: |
| free(path); |
| } |
| |
| out: |
| closedir(conf_dir); |
| } |
| |
| static void load_driver(const char *name) |
| { |
| char *so_name; |
| void *dlhandle; |
| |
| /* If the name is an absolute path then open that path after appending |
| * the trailer suffix |
| */ |
| if (name[0] == '/') { |
| if (asprintf(&so_name, "%s" VERBS_PROVIDER_SUFFIX, name) < 0) |
| goto out_asprintf; |
| dlhandle = dlopen(so_name, RTLD_NOW); |
| if (!dlhandle) |
| goto out_dlopen; |
| free(so_name); |
| return; |
| } |
| |
| /* If configured with a provider plugin path then try that next */ |
| if (sizeof(VERBS_PROVIDER_DIR) > 1) { |
| if (asprintf(&so_name, |
| VERBS_PROVIDER_DIR "/lib%s" VERBS_PROVIDER_SUFFIX, |
| name) < 0) |
| goto out_asprintf; |
| dlhandle = dlopen(so_name, RTLD_NOW); |
| free(so_name); |
| if (dlhandle) |
| return; |
| } |
| |
| /* Otherwise use the system library search path. This is the historical |
| * behavior of libibverbs |
| */ |
| if (asprintf(&so_name, "lib%s" VERBS_PROVIDER_SUFFIX, name) < 0) |
| goto out_asprintf; |
| dlhandle = dlopen(so_name, RTLD_NOW); |
| if (!dlhandle) |
| goto out_dlopen; |
| free(so_name); |
| return; |
| |
| out_asprintf: |
| fprintf(stderr, PFX "Warning: couldn't load driver '%s'.\n", name); |
| return; |
| out_dlopen: |
| fprintf(stderr, PFX "Warning: couldn't load driver '%s': %s\n", so_name, |
| dlerror()); |
| free(so_name); |
| } |
| |
| void load_drivers(void) |
| { |
| struct ibv_driver_name *name, *next_name; |
| const char *env; |
| char *list, *env_name; |
| |
| read_config(); |
| |
| /* Only use drivers passed in through the calling user's environment |
| * if we're not running setuid. |
| */ |
| if (getuid() == geteuid()) { |
| if ((env = getenv("RDMAV_DRIVERS"))) { |
| list = strdupa(env); |
| while ((env_name = strsep(&list, ":;"))) |
| load_driver(env_name); |
| } else if ((env = getenv("IBV_DRIVERS"))) { |
| list = strdupa(env); |
| while ((env_name = strsep(&list, ":;"))) |
| load_driver(env_name); |
| } |
| } |
| |
| list_for_each_safe (&driver_name_list, name, next_name, entry) { |
| load_driver(name->name); |
| free(name->name); |
| free(name); |
| } |
| } |
| #endif |