| 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 | #include "qemu/cutils.h" |
| 23 | #include "qemu/config-file.h" |
| 24 | #include "qapi/error.h" |
| 25 | #ifdef CONFIG_MODULE_UPGRADES |
| 26 | #include "qemu-version.h" |
| 27 | #endif |
| 28 | #include "trace.h" |
| 29 | |
| 30 | typedef struct ModuleEntry |
| 31 | { |
| 32 | void (*init)(void); |
| 33 | QTAILQ_ENTRY(ModuleEntry) node; |
| 34 | module_init_type type; |
| 35 | } ModuleEntry; |
| 36 | |
| 37 | typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList; |
| 38 | |
| 39 | static ModuleTypeList init_type_list[MODULE_INIT_MAX]; |
| 40 | static bool modules_init_done[MODULE_INIT_MAX]; |
| 41 | |
| 42 | static ModuleTypeList dso_init_list; |
| 43 | |
| 44 | static void init_lists(void) |
| 45 | { |
| 46 | static int inited; |
| 47 | int i; |
| 48 | |
| 49 | if (inited) { |
| 50 | return; |
| 51 | } |
| 52 | |
| 53 | for (i = 0; i < MODULE_INIT_MAX; i++) { |
| 54 | QTAILQ_INIT(&init_type_list[i]); |
| 55 | } |
| 56 | |
| 57 | QTAILQ_INIT(&dso_init_list); |
| 58 | |
| 59 | inited = 1; |
| 60 | } |
| 61 | |
| 62 | |
| 63 | static ModuleTypeList *find_type(module_init_type type) |
| 64 | { |
| 65 | init_lists(); |
| 66 | |
| 67 | return &init_type_list[type]; |
| 68 | } |
| 69 | |
| 70 | void register_module_init(void (*fn)(void), module_init_type type) |
| 71 | { |
| 72 | ModuleEntry *e; |
| 73 | ModuleTypeList *l; |
| 74 | |
| 75 | e = g_malloc0(sizeof(*e)); |
| 76 | e->init = fn; |
| 77 | e->type = type; |
| 78 | |
| 79 | l = find_type(type); |
| 80 | |
| 81 | QTAILQ_INSERT_TAIL(l, e, node); |
| 82 | } |
| 83 | |
| 84 | void register_dso_module_init(void (*fn)(void), module_init_type type) |
| 85 | { |
| 86 | ModuleEntry *e; |
| 87 | |
| 88 | init_lists(); |
| 89 | |
| 90 | e = g_malloc0(sizeof(*e)); |
| 91 | e->init = fn; |
| 92 | e->type = type; |
| 93 | |
| 94 | QTAILQ_INSERT_TAIL(&dso_init_list, e, node); |
| 95 | } |
| 96 | |
| 97 | void module_call_init(module_init_type type) |
| 98 | { |
| 99 | ModuleTypeList *l; |
| 100 | ModuleEntry *e; |
| 101 | |
| 102 | if (modules_init_done[type]) { |
| 103 | return; |
| 104 | } |
| 105 | |
| 106 | l = find_type(type); |
| 107 | |
| 108 | QTAILQ_FOREACH(e, l, node) { |
| 109 | e->init(); |
| 110 | } |
| 111 | |
| 112 | modules_init_done[type] = true; |
| 113 | } |
| 114 | |
| 115 | #ifdef CONFIG_MODULES |
| 116 | |
| 117 | static const QemuModinfo module_info_stub[] = { { |
| 118 | /* end of list */ |
| 119 | } }; |
| 120 | static const QemuModinfo *module_info = module_info_stub; |
| 121 | static const char *module_arch; |
| 122 | |
| 123 | void module_init_info(const QemuModinfo *info) |
| 124 | { |
| 125 | module_info = info; |
| 126 | } |
| 127 | |
| 128 | void module_allow_arch(const char *arch) |
| 129 | { |
| 130 | module_arch = arch; |
| 131 | } |
| 132 | |
| 133 | static bool module_check_arch(const QemuModinfo *modinfo) |
| 134 | { |
| 135 | if (modinfo->arch) { |
| 136 | if (!module_arch) { |
| 137 | /* no arch set -> ignore all */ |
| 138 | return false; |
| 139 | } |
| 140 | if (strcmp(module_arch, modinfo->arch) != 0) { |
| 141 | /* mismatch */ |
| 142 | return false; |
| 143 | } |
| 144 | } |
| 145 | return true; |
| 146 | } |
| 147 | |
| 148 | /* |
| 149 | * module_load_dso: attempt to load an existing dso file |
| 150 | * |
| 151 | * fname: full pathname of the file to load |
| 152 | * export_symbols: if true, add the symbols to the global name space |
| 153 | * errp: error to set. |
| 154 | * |
| 155 | * Return value: true on success, false on error, and errp will be set. |
| 156 | */ |
| 157 | static bool module_load_dso(const char *fname, bool export_symbols, |
| 158 | Error **errp) |
| 159 | { |
| 160 | GModule *g_module; |
| 161 | void (*sym)(void); |
| 162 | ModuleEntry *e, *next; |
| 163 | int flags; |
| 164 | |
| 165 | assert(QTAILQ_EMPTY(&dso_init_list)); |
| 166 | |
| 167 | flags = 0; |
| 168 | if (!export_symbols) { |
| 169 | flags |= G_MODULE_BIND_LOCAL; |
| 170 | } |
| 171 | g_module = g_module_open(fname, flags); |
| 172 | if (!g_module) { |
| 173 | error_setg(errp, "failed to open module: %s", g_module_error()); |
| 174 | return false; |
| 175 | } |
| 176 | if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) { |
| 177 | error_setg(errp, "failed to initialize module: %s", fname); |
| 178 | /* |
| 179 | * Print some info if this is a QEMU module (but from different build), |
| 180 | * this will make debugging user problems easier. |
| 181 | */ |
| 182 | if (g_module_symbol(g_module, "qemu_module_dummy", (gpointer *)&sym)) { |
| 183 | error_append_hint(errp, |
| 184 | "Only modules from the same build can be loaded.\n"); |
| 185 | } |
| 186 | g_module_close(g_module); |
| 187 | return false; |
| 188 | } |
| 189 | |
| 190 | QTAILQ_FOREACH(e, &dso_init_list, node) { |
| 191 | e->init(); |
| 192 | register_module_init(e->init, e->type); |
| 193 | } |
| 194 | trace_module_load_module(fname); |
| 195 | QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) { |
| 196 | QTAILQ_REMOVE(&dso_init_list, e, node); |
| 197 | g_free(e); |
| 198 | } |
| 199 | return true; |
| 200 | } |
| 201 | |
| 202 | int module_load(const char *prefix, const char *name, Error **errp) |
| 203 | { |
| 204 | int rv = -1; |
| 205 | #ifdef CONFIG_MODULE_UPGRADES |
| 206 | g_autofree char *version_dir = NULL; |
| 207 | #endif |
| 208 | const char *search_dir; |
| 209 | char *dirs[5]; |
| 210 | char *module_name; |
| 211 | int i = 0, n_dirs = 0; |
| 212 | bool export_symbols = false; |
| 213 | static GHashTable *loaded_modules; |
| 214 | const QemuModinfo *modinfo; |
| 215 | const char **sl; |
| 216 | |
| 217 | if (!g_module_supported()) { |
| 218 | error_setg(errp, "%s", "this platform does not support GLib modules"); |
| 219 | return -1; |
| 220 | } |
| 221 | |
| 222 | if (!loaded_modules) { |
| 223 | loaded_modules = g_hash_table_new(g_str_hash, g_str_equal); |
| 224 | } |
| 225 | |
| 226 | /* allocate all resources managed by the out: label here */ |
| 227 | module_name = g_strdup_printf("%s%s", prefix, name); |
| 228 | |
| 229 | if (g_hash_table_contains(loaded_modules, module_name)) { |
| 230 | g_free(module_name); |
| 231 | return 2; /* module already loaded */ |
| 232 | } |
| 233 | g_hash_table_add(loaded_modules, module_name); |
| 234 | |
| 235 | search_dir = getenv("QEMU_MODULE_DIR"); |
| 236 | if (search_dir != NULL) { |
| 237 | dirs[n_dirs++] = g_strdup(search_dir); |
| 238 | } |
| 239 | dirs[n_dirs++] = get_relocated_path(CONFIG_QEMU_MODDIR); |
| 240 | |
| 241 | #ifdef CONFIG_MODULE_UPGRADES |
| 242 | version_dir = g_strcanon(g_strdup(QEMU_PKGVERSION), |
| 243 | G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "+-.~", |
| 244 | '_'); |
| 245 | dirs[n_dirs++] = g_strdup_printf("/var/run/qemu/%s", version_dir); |
| 246 | #endif |
| 247 | assert(n_dirs <= ARRAY_SIZE(dirs)); |
| 248 | |
| 249 | /* end of resources managed by the out: label */ |
| 250 | |
| 251 | for (modinfo = module_info; modinfo->name != NULL; modinfo++) { |
| 252 | if (modinfo->arch) { |
| 253 | if (strcmp(modinfo->name, module_name) == 0) { |
| 254 | if (!module_check_arch(modinfo)) { |
| 255 | error_setg(errp, "module arch does not match: " |
| 256 | "expected '%s', got '%s'", module_arch, modinfo->arch); |
| 257 | goto out; |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | if (modinfo->deps) { |
| 262 | if (strcmp(modinfo->name, module_name) == 0) { |
| 263 | /* we depend on other module(s) */ |
| 264 | for (sl = modinfo->deps; *sl != NULL; sl++) { |
| 265 | int subrv = module_load("", *sl, errp); |
| 266 | if (subrv <= 0) { |
| 267 | rv = subrv; |
| 268 | goto out; |
| 269 | } |
| 270 | } |
| 271 | } else { |
| 272 | for (sl = modinfo->deps; *sl != NULL; sl++) { |
| 273 | if (strcmp(module_name, *sl) == 0) { |
| 274 | /* another module depends on us */ |
| 275 | export_symbols = true; |
| 276 | } |
| 277 | } |
| 278 | } |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | for (i = 0; i < n_dirs; i++) { |
| 283 | char *fname = g_strdup_printf("%s/%s%s", |
| 284 | dirs[i], module_name, CONFIG_HOST_DSOSUF); |
| 285 | int ret = access(fname, F_OK); |
| 286 | if (ret != 0 && (errno == ENOENT || errno == ENOTDIR)) { |
| 287 | /* |
| 288 | * if we don't find the module in this dir, try the next one. |
| 289 | * If we don't find it in any dir, that can be fine too: user |
| 290 | * did not install the module. We will return 0 in this case |
| 291 | * with no error set. |
| 292 | */ |
| 293 | g_free(fname); |
| 294 | continue; |
| 295 | } else if (ret != 0) { |
| 296 | /* most common is EACCES here */ |
| 297 | error_setg_errno(errp, errno, "error trying to access %s", fname); |
| 298 | } else if (module_load_dso(fname, export_symbols, errp)) { |
| 299 | rv = 1; /* module successfully loaded */ |
| 300 | } |
| 301 | g_free(fname); |
| 302 | goto out; |
| 303 | } |
| 304 | rv = 0; /* module not found */ |
| 305 | |
| 306 | out: |
| 307 | if (rv <= 0) { |
| 308 | g_hash_table_remove(loaded_modules, module_name); |
| 309 | g_free(module_name); |
| 310 | } |
| 311 | for (i = 0; i < n_dirs; i++) { |
| 312 | g_free(dirs[i]); |
| 313 | } |
| 314 | return rv; |
| 315 | } |
| 316 | |
| 317 | static bool module_loaded_qom_all; |
| 318 | |
| 319 | int module_load_qom(const char *type, Error **errp) |
| 320 | { |
| 321 | const QemuModinfo *modinfo; |
| 322 | const char **sl; |
| 323 | int rv = 0; |
| 324 | |
| 325 | if (!type) { |
| 326 | error_setg(errp, "%s", "type is NULL"); |
| 327 | return -1; |
| 328 | } |
| 329 | |
| 330 | trace_module_lookup_object_type(type); |
| 331 | for (modinfo = module_info; modinfo->name != NULL; modinfo++) { |
| 332 | if (!modinfo->objs) { |
| 333 | continue; |
| 334 | } |
| 335 | if (!module_check_arch(modinfo)) { |
| 336 | continue; |
| 337 | } |
| 338 | for (sl = modinfo->objs; *sl != NULL; sl++) { |
| 339 | if (strcmp(type, *sl) == 0) { |
| 340 | if (rv > 0) { |
| 341 | error_setg(errp, "multiple modules providing '%s'", type); |
| 342 | return -1; |
| 343 | } |
| 344 | rv = module_load("", modinfo->name, errp); |
| 345 | if (rv < 0) { |
| 346 | return rv; |
| 347 | } |
| 348 | } |
| 349 | } |
| 350 | } |
| 351 | return rv; |
| 352 | } |
| 353 | |
| 354 | void module_load_qom_all(void) |
| 355 | { |
| 356 | const QemuModinfo *modinfo; |
| 357 | |
| 358 | if (module_loaded_qom_all) { |
| 359 | return; |
| 360 | } |
| 361 | |
| 362 | for (modinfo = module_info; modinfo->name != NULL; modinfo++) { |
| 363 | Error *local_err = NULL; |
| 364 | if (!modinfo->objs) { |
| 365 | continue; |
| 366 | } |
| 367 | if (!module_check_arch(modinfo)) { |
| 368 | continue; |
| 369 | } |
| 370 | if (module_load("", modinfo->name, &local_err) < 0) { |
| 371 | error_report_err(local_err); |
| 372 | } |
| 373 | } |
| 374 | module_loaded_qom_all = true; |
| 375 | } |
| 376 | |
| 377 | void qemu_load_module_for_opts(const char *group) |
| 378 | { |
| 379 | const QemuModinfo *modinfo; |
| 380 | const char **sl; |
| 381 | |
| 382 | for (modinfo = module_info; modinfo->name != NULL; modinfo++) { |
| 383 | if (!modinfo->opts) { |
| 384 | continue; |
| 385 | } |
| 386 | for (sl = modinfo->opts; *sl != NULL; sl++) { |
| 387 | if (strcmp(group, *sl) == 0) { |
| 388 | Error *local_err = NULL; |
| 389 | if (module_load("", modinfo->name, &local_err) < 0) { |
| 390 | error_report_err(local_err); |
| 391 | } |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | } |
| 396 | |
| 397 | #else |
| 398 | |
| 399 | const QemuModinfo qemu_modinfo[] = {}; |
| 400 | |
| 401 | void module_init_info(const QemuModinfo *info) {} |
| 402 | void module_allow_arch(const char *arch) {} |
| 403 | void qemu_load_module_for_opts(const char *group) {} |
| 404 | int module_load(const char *prefix, const char *name, Error **errp) { return 2; } |
| 405 | int module_load_qom(const char *type, Error **errp) { return 2; } |
| 406 | void module_load_qom_all(void) {} |
| 407 | |
| 408 | #endif |