qemu/util/module.c
<<
>>
Prefs
   1/*
   2 * QEMU Module Infrastructure
   3 *
   4 * Copyright IBM, Corp. 2009
   5 *
   6 * Authors:
   7 *  Anthony Liguori   <aliguori@us.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2.  See
  10 * the COPYING file in the top-level directory.
  11 *
  12 * Contributions after 2012-01-13 are licensed under the terms of the
  13 * GNU GPL, version 2 or (at your option) any later version.
  14 */
  15
  16#include "qemu/osdep.h"
  17#ifdef CONFIG_MODULES
  18#include <gmodule.h>
  19#endif
  20#include "qemu/queue.h"
  21#include "qemu/module.h"
  22#ifdef CONFIG_MODULE_UPGRADES
  23#include "qemu-version.h"
  24#endif
  25
  26typedef struct ModuleEntry
  27{
  28    void (*init)(void);
  29    QTAILQ_ENTRY(ModuleEntry) node;
  30    module_init_type type;
  31} ModuleEntry;
  32
  33typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList;
  34
  35static ModuleTypeList init_type_list[MODULE_INIT_MAX];
  36static bool modules_init_done[MODULE_INIT_MAX];
  37
  38static ModuleTypeList dso_init_list;
  39
  40static void init_lists(void)
  41{
  42    static int inited;
  43    int i;
  44
  45    if (inited) {
  46        return;
  47    }
  48
  49    for (i = 0; i < MODULE_INIT_MAX; i++) {
  50        QTAILQ_INIT(&init_type_list[i]);
  51    }
  52
  53    QTAILQ_INIT(&dso_init_list);
  54
  55    inited = 1;
  56}
  57
  58
  59static ModuleTypeList *find_type(module_init_type type)
  60{
  61    init_lists();
  62
  63    return &init_type_list[type];
  64}
  65
  66void register_module_init(void (*fn)(void), module_init_type type)
  67{
  68    ModuleEntry *e;
  69    ModuleTypeList *l;
  70
  71    e = g_malloc0(sizeof(*e));
  72    e->init = fn;
  73    e->type = type;
  74
  75    l = find_type(type);
  76
  77    QTAILQ_INSERT_TAIL(l, e, node);
  78}
  79
  80void register_dso_module_init(void (*fn)(void), module_init_type type)
  81{
  82    ModuleEntry *e;
  83
  84    init_lists();
  85
  86    e = g_malloc0(sizeof(*e));
  87    e->init = fn;
  88    e->type = type;
  89
  90    QTAILQ_INSERT_TAIL(&dso_init_list, e, node);
  91}
  92
  93void module_call_init(module_init_type type)
  94{
  95    ModuleTypeList *l;
  96    ModuleEntry *e;
  97
  98    if (modules_init_done[type]) {
  99        return;
 100    }
 101
 102    l = find_type(type);
 103
 104    QTAILQ_FOREACH(e, l, node) {
 105        e->init();
 106    }
 107
 108    modules_init_done[type] = true;
 109}
 110
 111#ifdef CONFIG_MODULES
 112static int module_load_file(const char *fname)
 113{
 114    GModule *g_module;
 115    void (*sym)(void);
 116    const char *dsosuf = HOST_DSOSUF;
 117    int len = strlen(fname);
 118    int suf_len = strlen(dsosuf);
 119    ModuleEntry *e, *next;
 120    int ret;
 121
 122    if (len <= suf_len || strcmp(&fname[len - suf_len], dsosuf)) {
 123        /* wrong suffix */
 124        ret = -EINVAL;
 125        goto out;
 126    }
 127    if (access(fname, F_OK)) {
 128        ret = -ENOENT;
 129        goto out;
 130    }
 131
 132    assert(QTAILQ_EMPTY(&dso_init_list));
 133
 134    g_module = g_module_open(fname, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
 135    if (!g_module) {
 136        fprintf(stderr, "Failed to open module: %s\n",
 137                g_module_error());
 138        ret = -EINVAL;
 139        goto out;
 140    }
 141    if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) {
 142        fprintf(stderr, "Failed to initialize module: %s\n",
 143                fname);
 144        /* Print some info if this is a QEMU module (but from different build),
 145         * this will make debugging user problems easier. */
 146        if (g_module_symbol(g_module, "qemu_module_dummy", (gpointer *)&sym)) {
 147            fprintf(stderr,
 148                    "Note: only modules from the same build can be loaded.\n");
 149        }
 150        g_module_close(g_module);
 151        ret = -EINVAL;
 152    } else {
 153        QTAILQ_FOREACH(e, &dso_init_list, node) {
 154            e->init();
 155            register_module_init(e->init, e->type);
 156        }
 157        ret = 0;
 158    }
 159
 160    QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) {
 161        QTAILQ_REMOVE(&dso_init_list, e, node);
 162        g_free(e);
 163    }
 164out:
 165    return ret;
 166}
 167#endif
 168
 169bool module_load_one(const char *prefix, const char *lib_name)
 170{
 171    bool success = false;
 172
 173#ifdef CONFIG_MODULES
 174    char *fname = NULL;
 175    char *exec_dir;
 176#ifdef CONFIG_MODULE_UPGRADES
 177    char *version_dir;
 178#endif
 179    const char *search_dir;
 180    char *dirs[5];
 181    char *module_name;
 182    int i = 0, n_dirs = 0;
 183    int ret;
 184    static GHashTable *loaded_modules;
 185
 186    if (!g_module_supported()) {
 187        fprintf(stderr, "Module is not supported by system.\n");
 188        return false;
 189    }
 190
 191    if (!loaded_modules) {
 192        loaded_modules = g_hash_table_new(g_str_hash, g_str_equal);
 193    }
 194
 195    module_name = g_strdup_printf("%s%s", prefix, lib_name);
 196
 197    if (!g_hash_table_add(loaded_modules, module_name)) {
 198        g_free(module_name);
 199        return true;
 200    }
 201
 202    exec_dir = qemu_get_exec_dir();
 203    search_dir = getenv("QEMU_MODULE_DIR");
 204    if (search_dir != NULL) {
 205        dirs[n_dirs++] = g_strdup_printf("%s", search_dir);
 206    }
 207    dirs[n_dirs++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR);
 208    dirs[n_dirs++] = g_strdup_printf("%s/..", exec_dir ? : "");
 209    dirs[n_dirs++] = g_strdup_printf("%s", exec_dir ? : "");
 210
 211#ifdef CONFIG_MODULE_UPGRADES
 212    version_dir = g_strcanon(g_strdup(QEMU_PKGVERSION),
 213                             G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "+-.~",
 214                             '_');
 215    dirs[n_dirs++] = g_strdup_printf("/var/run/qemu/%s", version_dir);
 216#endif
 217
 218    assert(n_dirs <= ARRAY_SIZE(dirs));
 219
 220    g_free(exec_dir);
 221    exec_dir = NULL;
 222
 223    for (i = 0; i < n_dirs; i++) {
 224        fname = g_strdup_printf("%s/%s%s",
 225                dirs[i], module_name, HOST_DSOSUF);
 226        ret = module_load_file(fname);
 227        g_free(fname);
 228        fname = NULL;
 229        /* Try loading until loaded a module file */
 230        if (!ret) {
 231            success = true;
 232            break;
 233        }
 234    }
 235
 236    if (!success) {
 237        g_hash_table_remove(loaded_modules, module_name);
 238        g_free(module_name);
 239    }
 240
 241    for (i = 0; i < n_dirs; i++) {
 242        g_free(dirs[i]);
 243    }
 244
 245#endif
 246    return success;
 247}
 248