On 9/8/22 20:30, Claudio Fontana wrote: > improve error handling during module load, by changing: > > bool module_load_one(const char *prefix, const char *lib_name); > void module_load_qom_one(const char *type); > > to: > > bool module_load_one(const char *prefix, const char *name, Error **errp); > bool module_load_qom_one(const char *type, Error **errp); > > module_load_qom_one has been introduced in: > > commit 28457744c345 ("module: qom module support"), which built on top of > module_load_one, but discarded the bool return value. Restore it. > > Adapt all callers to emit errors, or ignore them, or fail hard, > as appropriate in each context. > > Signed-off-by: Claudio Fontana <cfont...@suse.de> > --- > audio/audio.c | 9 ++- > block.c | 15 ++++- > block/dmg.c | 18 +++++- > hw/core/qdev.c | 10 ++- > include/qemu/module.h | 38 ++++++++++-- > qom/object.c | 18 +++++- > softmmu/qtest.c | 6 +- > ui/console.c | 18 +++++- > util/module.c | 140 ++++++++++++++++++++++++------------------ > 9 files changed, 194 insertions(+), 78 deletions(-) > > diff --git a/audio/audio.c b/audio/audio.c > index 76b8735b44..cff7464c07 100644 > --- a/audio/audio.c > +++ b/audio/audio.c > @@ -72,6 +72,7 @@ void audio_driver_register(audio_driver *drv) > audio_driver *audio_driver_lookup(const char *name) > { > struct audio_driver *d; > + Error *local_err = NULL; > > QLIST_FOREACH(d, &audio_drivers, next) { > if (strcmp(name, d->name) == 0) { > @@ -79,7 +80,13 @@ audio_driver *audio_driver_lookup(const char *name) > } > } > > - audio_module_load_one(name); > + if (!audio_module_load_one(name, &local_err)) { > + if (local_err) { > + error_report_err(local_err); > + } > + return NULL; > + } > + > QLIST_FOREACH(d, &audio_drivers, next) { > if (strcmp(name, d->name) == 0) { > return d; > diff --git a/block.c b/block.c > index bc85f46eed..8b610c6d95 100644 > --- a/block.c > +++ b/block.c > @@ -464,7 +464,14 @@ BlockDriver *bdrv_find_format(const char *format_name) > /* The driver isn't registered, maybe we need to load a module */ > for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) { > if (!strcmp(block_driver_modules[i].format_name, format_name)) { > - block_module_load_one(block_driver_modules[i].library_name); > + Error *local_err = NULL; > + if (!block_module_load_one(block_driver_modules[i].library_name, > + &local_err)) { > + if (local_err) { > + error_report_err(local_err); > + } > + return NULL; > + } > break; > } > } > @@ -976,7 +983,11 @@ BlockDriver *bdrv_find_protocol(const char *filename, > for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) { > if (block_driver_modules[i].protocol_name && > !strcmp(block_driver_modules[i].protocol_name, protocol)) { > - block_module_load_one(block_driver_modules[i].library_name); > + Error *local_err = NULL; > + if (!block_module_load_one(block_driver_modules[i].library_name, > + &local_err) && local_err) { > + error_report_err(local_err); > + } > break; > } > } > diff --git a/block/dmg.c b/block/dmg.c > index 98db18d82a..11d184d39c 100644 > --- a/block/dmg.c > +++ b/block/dmg.c > @@ -434,6 +434,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, > int flags, > uint64_t plist_xml_offset, plist_xml_length; > int64_t offset; > int ret; > + Error *local_err = NULL; > > ret = bdrv_apply_auto_read_only(bs, NULL, errp); > if (ret < 0) { > @@ -446,8 +447,21 @@ static int dmg_open(BlockDriverState *bs, QDict > *options, int flags, > return -EINVAL; > } > > - block_module_load_one("dmg-bz2"); > - block_module_load_one("dmg-lzfse"); > + if (!block_module_load_one("dmg-bz2", &local_err)) { > + if (local_err) { > + error_report_err(local_err); > + return -EINVAL; > + } > + warn_report("dmg-bz2 module not present, bz2 decomp unavailable"); > + } > + local_err = NULL; > + if (!block_module_load_one("dmg-lzfse", &local_err)) { > + if (local_err) { > + error_report_err(local_err); > + return -EINVAL; > + } > + warn_report("dmg-lzfse module not present, lzfse decomp > unavailable"); > + }
Hi Richard, Kevin, does this look ok now? Thanks, Claudio > > s->n_chunks = 0; > s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; > diff --git a/hw/core/qdev.c b/hw/core/qdev.c > index 0806d8fcaa..5902c59c94 100644 > --- a/hw/core/qdev.c > +++ b/hw/core/qdev.c > @@ -148,7 +148,15 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState > *bus, Error **errp) > DeviceState *qdev_new(const char *name) > { > if (!object_class_by_name(name)) { > - module_load_qom_one(name); > + Error *local_err = NULL; > + if (!module_load_qom_one(name, &local_err)) { > + if (local_err) { > + error_report_err(local_err); > + } else { > + error_report("could not find a module for type '%s'", name); > + } > + abort(); > + } > } > return DEVICE(object_new(name)); > } > diff --git a/include/qemu/module.h b/include/qemu/module.h > index 8c012bbe03..78d4c4de96 100644 > --- a/include/qemu/module.h > +++ b/include/qemu/module.h > @@ -61,16 +61,44 @@ typedef enum { > #define fuzz_target_init(function) module_init(function, \ > MODULE_INIT_FUZZ_TARGET) > #define migration_init(function) module_init(function, MODULE_INIT_MIGRATION) > -#define block_module_load_one(lib) module_load_one("block-", lib) > -#define ui_module_load_one(lib) module_load_one("ui-", lib) > -#define audio_module_load_one(lib) module_load_one("audio-", lib) > +#define block_module_load_one(lib, errp) module_load_one("block-", lib, errp) > +#define ui_module_load_one(lib, errp) module_load_one("ui-", lib, errp) > +#define audio_module_load_one(lib, errp) module_load_one("audio-", lib, errp) > > void register_module_init(void (*fn)(void), module_init_type type); > void register_dso_module_init(void (*fn)(void), module_init_type type); > > void module_call_init(module_init_type type); > -bool module_load_one(const char *prefix, const char *lib_name); > -void module_load_qom_one(const char *type); > + > +/* > + * module_load_one: attempt to load a module from a set of directories > + * > + * directories searched are: > + * - getenv("QEMU_MODULE_DIR") > + * - get_relocated_path(CONFIG_QEMU_MODDIR); > + * - /var/run/qemu/${version_dir} > + * > + * prefix: a subsystem prefix, or the empty string ("audio-", ..., > "") > + * name: name of the module > + * errp: error to set in case the module is found, but load failed. > + * > + * Return value: true on success (found and loaded); > + * if module if found, but load failed, errp will be set. > + * if module is not found, errp will not be set. > + */ > +bool module_load_one(const char *prefix, const char *name, Error **errp); > + > +/* > + * module_load_qom_one: attempt to load a module to provide a QOM type > + * > + * type: the type to be provided > + * errp: error to set. > + * > + * Return value: true on success (found and loaded), false on failure. > + * If a module is simply not found for the type, > + * errp will not be set. > + */ > +bool module_load_qom_one(const char *type, Error **errp); > void module_load_qom_all(void); > void module_allow_arch(const char *arch); > > diff --git a/qom/object.c b/qom/object.c > index d34608558e..493e5ce231 100644 > --- a/qom/object.c > +++ b/qom/object.c > @@ -526,8 +526,14 @@ void object_initialize(void *data, size_t size, const > char *typename) > > #ifdef CONFIG_MODULES > if (!type) { > - module_load_qom_one(typename); > - type = type_get_by_name(typename); > + Error *local_err = NULL; > + if (!module_load_qom_one(typename, &local_err)) { > + if (local_err) { > + error_report_err(local_err); > + } > + } else { > + type = type_get_by_name(typename); > + } > } > #endif > if (!type) { > @@ -1033,7 +1039,13 @@ ObjectClass *module_object_class_by_name(const char > *typename) > oc = object_class_by_name(typename); > #ifdef CONFIG_MODULES > if (!oc) { > - module_load_qom_one(typename); > + Error *local_err = NULL; > + if (!module_load_qom_one(typename, &local_err)) { > + if (local_err) { > + error_report_err(local_err); > + } > + return NULL; > + } > oc = object_class_by_name(typename); > } > #endif > diff --git a/softmmu/qtest.c b/softmmu/qtest.c > index 76eb7bac56..bb83c7aae9 100644 > --- a/softmmu/qtest.c > +++ b/softmmu/qtest.c > @@ -753,12 +753,16 @@ static void qtest_process_command(CharBackend *chr, > gchar **words) > qtest_sendf(chr, "OK %"PRIi64"\n", > (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); > } else if (strcmp(words[0], "module_load") == 0) { > + Error *local_err = NULL; > g_assert(words[1] && words[2]); > > qtest_send_prefix(chr); > - if (module_load_one(words[1], words[2])) { > + if (module_load_one(words[1], words[2], &local_err)) { > qtest_sendf(chr, "OK\n"); > } else { > + if (local_err) { > + error_report_err(local_err); > + } > qtest_sendf(chr, "FAIL\n"); > } > } else if (qtest_enabled() && strcmp(words[0], "clock_set") == 0) { > diff --git a/ui/console.c b/ui/console.c > index 765892f84f..34dd206167 100644 > --- a/ui/console.c > +++ b/ui/console.c > @@ -2632,7 +2632,11 @@ bool qemu_display_find_default(DisplayOptions *opts) > > for (i = 0; i < (int)ARRAY_SIZE(prio); i++) { > if (dpys[prio[i]] == NULL) { > - ui_module_load_one(DisplayType_str(prio[i])); > + Error *local_err = NULL; > + if (!ui_module_load_one(DisplayType_str(prio[i]), &local_err) > + && local_err) { > + error_report_err(local_err); > + } > } > if (dpys[prio[i]] == NULL) { > continue; > @@ -2650,7 +2654,11 @@ void qemu_display_early_init(DisplayOptions *opts) > return; > } > if (dpys[opts->type] == NULL) { > - ui_module_load_one(DisplayType_str(opts->type)); > + Error *local_err = NULL; > + if (!ui_module_load_one(DisplayType_str(opts->type), &local_err) > + && local_err) { > + error_report_err(local_err); > + } > } > if (dpys[opts->type] == NULL) { > error_report("Display '%s' is not available.", > @@ -2680,7 +2688,11 @@ void qemu_display_help(void) > printf("none\n"); > for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) { > if (!dpys[idx]) { > - ui_module_load_one(DisplayType_str(idx)); > + Error *local_err = NULL; > + if (!ui_module_load_one(DisplayType_str(idx), &local_err) > + && local_err) { > + error_report_err(local_err); > + } > } > if (dpys[idx]) { > printf("%s\n", DisplayType_str(dpys[idx]->type)); > diff --git a/util/module.c b/util/module.c > index 8563edd626..90ea8adf7d 100644 > --- a/util/module.c > +++ b/util/module.c > @@ -21,6 +21,7 @@ > #include "qemu/module.h" > #include "qemu/cutils.h" > #include "qemu/config-file.h" > +#include "qapi/error.h" > #ifdef CONFIG_MODULE_UPGRADES > #include "qemu-version.h" > #endif > @@ -144,25 +145,22 @@ static bool module_check_arch(const QemuModinfo > *modinfo) > return true; > } > > -static int module_load_file(const char *fname, bool export_symbols) > +/* > + * module_load_dso: attempt to load an existing dso file > + * > + * fname: full pathname of the file to load > + * export_symbols: if true, add the symbols to the global name space > + * errp: error to set. > + * > + * Return value: true on success, false on error, and errp will be set. > + */ > +static bool module_load_dso(const char *fname, bool export_symbols, > + Error **errp) > { > GModule *g_module; > void (*sym)(void); > - const char *dsosuf = CONFIG_HOST_DSOSUF; > - int len = strlen(fname); > - int suf_len = strlen(dsosuf); > ModuleEntry *e, *next; > - int ret, flags; > - > - if (len <= suf_len || strcmp(&fname[len - suf_len], dsosuf)) { > - /* wrong suffix */ > - ret = -EINVAL; > - goto out; > - } > - if (access(fname, F_OK)) { > - ret = -ENOENT; > - goto out; > - } > + int flags; > > assert(QTAILQ_EMPTY(&dso_init_list)); > > @@ -172,46 +170,38 @@ static int module_load_file(const char *fname, bool > export_symbols) > } > g_module = g_module_open(fname, flags); > if (!g_module) { > - fprintf(stderr, "Failed to open module: %s\n", > - g_module_error()); > - ret = -EINVAL; > - goto out; > + error_setg(errp, "failed to open module: %s", g_module_error()); > + return false; > } > if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) { > - fprintf(stderr, "Failed to initialize module: %s\n", > - fname); > - /* Print some info if this is a QEMU module (but from different > build), > - * this will make debugging user problems easier. */ > + error_setg(errp, "failed to initialize module: %s", fname); > + /* > + * Print some info if this is a QEMU module (but from different > build), > + * this will make debugging user problems easier. > + */ > if (g_module_symbol(g_module, "qemu_module_dummy", (gpointer > *)&sym)) { > - fprintf(stderr, > - "Note: only modules from the same build can be > loaded.\n"); > + error_append_hint(errp, > + "Only modules from the same build can be loaded.\n"); > } > g_module_close(g_module); > - ret = -EINVAL; > - } else { > - QTAILQ_FOREACH(e, &dso_init_list, node) { > - e->init(); > - register_module_init(e->init, e->type); > - } > - ret = 0; > + return false; > } > > + QTAILQ_FOREACH(e, &dso_init_list, node) { > + e->init(); > + register_module_init(e->init, e->type); > + } > trace_module_load_module(fname); > QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) { > QTAILQ_REMOVE(&dso_init_list, e, node); > g_free(e); > } > -out: > - return ret; > + return true; > } > -#endif > > -bool module_load_one(const char *prefix, const char *lib_name) > +bool module_load_one(const char *prefix, const char *name, Error **errp) > { > bool success = false; > - > -#ifdef CONFIG_MODULES > - char *fname = NULL; > #ifdef CONFIG_MODULE_UPGRADES > char *version_dir; > #endif > @@ -219,14 +209,13 @@ bool module_load_one(const char *prefix, const char > *lib_name) > char *dirs[5]; > char *module_name; > int i = 0, n_dirs = 0; > - int ret; > bool export_symbols = false; > static GHashTable *loaded_modules; > const QemuModinfo *modinfo; > const char **sl; > > if (!g_module_supported()) { > - fprintf(stderr, "Module is not supported by system.\n"); > + error_setg(errp, "%s", "this platform does not support GLib > modules"); > return false; > } > > @@ -234,7 +223,7 @@ bool module_load_one(const char *prefix, const char > *lib_name) > loaded_modules = g_hash_table_new(g_str_hash, g_str_equal); > } > > - module_name = g_strdup_printf("%s%s", prefix, lib_name); > + module_name = g_strdup_printf("%s%s", prefix, name); > > if (g_hash_table_contains(loaded_modules, module_name)) { > g_free(module_name); > @@ -246,6 +235,8 @@ bool module_load_one(const char *prefix, const char > *lib_name) > if (modinfo->arch) { > if (strcmp(modinfo->name, module_name) == 0) { > if (!module_check_arch(modinfo)) { > + error_setg(errp, "module arch does not match: " > + "expected '%s', got '%s'", module_arch, > modinfo->arch); > return false; > } > } > @@ -254,7 +245,9 @@ bool module_load_one(const char *prefix, const char > *lib_name) > if (strcmp(modinfo->name, module_name) == 0) { > /* we depend on other module(s) */ > for (sl = modinfo->deps; *sl != NULL; sl++) { > - module_load_one("", *sl); > + if (!(module_load_one("", *sl, errp))) { > + return false; > + } > } > } else { > for (sl = modinfo->deps; *sl != NULL; sl++) { > @@ -283,16 +276,26 @@ bool module_load_one(const char *prefix, const char > *lib_name) > assert(n_dirs <= ARRAY_SIZE(dirs)); > > for (i = 0; i < n_dirs; i++) { > - fname = g_strdup_printf("%s/%s%s", > - dirs[i], module_name, CONFIG_HOST_DSOSUF); > - ret = module_load_file(fname, export_symbols); > - g_free(fname); > - fname = NULL; > - /* Try loading until loaded a module file */ > - if (!ret) { > - success = true; > + char *fname = g_strdup_printf("%s/%s%s", > + dirs[i], module_name, > CONFIG_HOST_DSOSUF); > + int ret = access(fname, F_OK); > + if (ret != 0) { > + if (errno == ENOENT || errno == ENOTDIR) { > + /* > + * if we don't find the module in this dir, try the next one. > + * If we don't find it in any dir, that can be fine too: user > + * did not install the module. We will return false in this > + * case, with no error set. > + */ > + continue; > + } > + /* most common is EACCES here */ > + error_setg_errno(errp, errno, "error trying to access %s", > fname); > break; > } > + success = module_load_dso(fname, export_symbols, errp); > + g_free(fname); > + break; > } > > if (!success) { > @@ -304,21 +307,20 @@ bool module_load_one(const char *prefix, const char > *lib_name) > g_free(dirs[i]); > } > > -#endif > return success; > } > > -#ifdef CONFIG_MODULES > - > static bool module_loaded_qom_all; > > -void module_load_qom_one(const char *type) > +bool module_load_qom_one(const char *type, Error **errp) > { > + bool found = false; > const QemuModinfo *modinfo; > const char **sl; > > if (!type) { > - return; > + error_setg(errp, "%s", "type is NULL"); > + return false; > } > > trace_module_lookup_object_type(type); > @@ -331,15 +333,26 @@ void module_load_qom_one(const char *type) > } > for (sl = modinfo->objs; *sl != NULL; sl++) { > if (strcmp(type, *sl) == 0) { > - module_load_one("", modinfo->name); > + if (found) { > + error_setg(errp, "multiple modules providing '%s'", > type); > + found = false; > + break; > + } > + found = module_load_one("", modinfo->name, errp); > + if (!found) { > + /* errp optionally set in module_load_one */ > + break; > + } > } > } > } > + return found; > } > > void module_load_qom_all(void) > { > const QemuModinfo *modinfo; > + Error *local_err = NULL; > > if (module_loaded_qom_all) { > return; > @@ -352,7 +365,9 @@ void module_load_qom_all(void) > if (!module_check_arch(modinfo)) { > continue; > } > - module_load_one("", modinfo->name); > + if (!module_load_one("", modinfo->name, &local_err) && local_err) { > + error_report_err(local_err); > + } > } > module_loaded_qom_all = true; > } > @@ -368,7 +383,11 @@ void qemu_load_module_for_opts(const char *group) > } > for (sl = modinfo->opts; *sl != NULL; sl++) { > if (strcmp(group, *sl) == 0) { > - module_load_one("", modinfo->name); > + Error *local_err = NULL; > + if (!module_load_one("", modinfo->name, &local_err) > + && local_err) { > + error_report_err(local_err); > + } > } > } > } > @@ -378,7 +397,8 @@ void qemu_load_module_for_opts(const char *group) > > void module_allow_arch(const char *arch) {} > void qemu_load_module_for_opts(const char *group) {} > -void module_load_qom_one(const char *type) {} > +bool module_load_one(const char *prefix, const char *name, Error **errp) { > return true; } > +bool module_load_qom_one(const char *type, Error **errp) { return true; } > void module_load_qom_all(void) {} > > #endif