1 #include "qemu/osdep.h" 2 3 #include "qemu/cutils.h" 4 #include "qapi/error.h" 5 #include "qapi/qapi-commands-qom.h" 6 #include "qapi/qapi-visit-qom.h" 7 #include "qapi/qmp/qdict.h" 8 #include "qapi/qmp/qerror.h" 9 #include "qapi/qmp/qjson.h" 10 #include "qapi/qobject-input-visitor.h" 11 #include "qapi/qobject-output-visitor.h" 12 #include "qom/object_interfaces.h" 13 #include "qemu/help_option.h" 14 #include "qemu/id.h" 15 #include "qemu/module.h" 16 #include "qemu/option.h" 17 #include "qemu/qemu-print.h" 18 #include "qapi/opts-visitor.h" 19 #include "qemu/config-file.h" 20 21 bool user_creatable_complete(UserCreatable *uc, Error **errp) 22 { 23 UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc); 24 Error *err = NULL; 25 26 if (ucc->complete) { 27 ucc->complete(uc, &err); 28 error_propagate(errp, err); 29 } 30 return !err; 31 } 32 33 bool user_creatable_can_be_deleted(UserCreatable *uc) 34 { 35 36 UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc); 37 38 if (ucc->can_be_deleted) { 39 return ucc->can_be_deleted(uc); 40 } else { 41 return true; 42 } 43 } 44 45 static void object_set_properties_from_qdict(Object *obj, const QDict *qdict, 46 Visitor *v, Error **errp) 47 { 48 const QDictEntry *e; 49 50 if (!visit_start_struct(v, NULL, NULL, 0, errp)) { 51 return; 52 } 53 for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { 54 if (!object_property_set(obj, e->key, v, errp)) { 55 goto out; 56 } 57 } 58 visit_check_struct(v, errp); 59 out: 60 visit_end_struct(v, NULL); 61 } 62 63 void object_set_properties_from_keyval(Object *obj, const QDict *qdict, 64 bool from_json, Error **errp) 65 { 66 Visitor *v; 67 if (from_json) { 68 v = qobject_input_visitor_new(QOBJECT(qdict)); 69 } else { 70 v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); 71 } 72 object_set_properties_from_qdict(obj, qdict, v, errp); 73 visit_free(v); 74 } 75 76 Object *user_creatable_add_type(const char *type, const char *id, 77 const QDict *qdict, 78 Visitor *v, Error **errp) 79 { 80 ERRP_GUARD(); 81 Object *obj; 82 ObjectClass *klass; 83 Error *local_err = NULL; 84 85 if (id != NULL && !id_wellformed(id)) { 86 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id", "an identifier"); 87 error_append_hint(errp, "Identifiers consist of letters, digits, " 88 "'-', '.', '_', starting with a letter.\n"); 89 return NULL; 90 } 91 92 klass = object_class_by_name(type); 93 if (!klass) { 94 error_setg(errp, "invalid object type: %s", type); 95 return NULL; 96 } 97 98 if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) { 99 error_setg(errp, "object type '%s' isn't supported by object-add", 100 type); 101 return NULL; 102 } 103 104 if (object_class_is_abstract(klass)) { 105 error_setg(errp, "object type '%s' is abstract", type); 106 return NULL; 107 } 108 109 assert(qdict); 110 obj = object_new(type); 111 object_set_properties_from_qdict(obj, qdict, v, &local_err); 112 if (local_err) { 113 goto out; 114 } 115 116 if (id != NULL) { 117 object_property_try_add_child(object_get_objects_root(), 118 id, obj, &local_err); 119 if (local_err) { 120 goto out; 121 } 122 } 123 124 if (!user_creatable_complete(USER_CREATABLE(obj), &local_err)) { 125 if (id != NULL) { 126 object_property_del(object_get_objects_root(), id); 127 } 128 goto out; 129 } 130 out: 131 if (local_err) { 132 error_propagate(errp, local_err); 133 object_unref(obj); 134 return NULL; 135 } 136 return obj; 137 } 138 139 void user_creatable_add_qapi(ObjectOptions *options, Error **errp) 140 { 141 Visitor *v; 142 QObject *qobj; 143 QDict *props; 144 Object *obj; 145 146 v = qobject_output_visitor_new(&qobj); 147 visit_type_ObjectOptions(v, NULL, &options, &error_abort); 148 visit_complete(v, &qobj); 149 visit_free(v); 150 151 props = qobject_to(QDict, qobj); 152 qdict_del(props, "qom-type"); 153 qdict_del(props, "id"); 154 155 v = qobject_input_visitor_new(QOBJECT(props)); 156 obj = user_creatable_add_type(ObjectType_str(options->qom_type), 157 options->id, props, v, errp); 158 object_unref(obj); 159 qobject_unref(qobj); 160 visit_free(v); 161 } 162 163 char *object_property_help(const char *name, const char *type, 164 QObject *defval, const char *description) 165 { 166 GString *str = g_string_new(NULL); 167 168 g_string_append_printf(str, " %s=<%s>", name, type); 169 if (description || defval) { 170 if (str->len < 24) { 171 g_string_append_printf(str, "%*s", 24 - (int)str->len, ""); 172 } 173 g_string_append(str, " - "); 174 } 175 if (description) { 176 g_string_append(str, description); 177 } 178 if (defval) { 179 g_autofree char *def_json = g_string_free(qobject_to_json(defval), 180 false); 181 g_string_append_printf(str, " (default: %s)", def_json); 182 } 183 184 return g_string_free(str, false); 185 } 186 187 static void user_creatable_print_types(void) 188 { 189 GSList *l, *list; 190 191 qemu_printf("List of user creatable objects:\n"); 192 list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false); 193 for (l = list; l != NULL; l = l->next) { 194 ObjectClass *oc = OBJECT_CLASS(l->data); 195 qemu_printf(" %s\n", object_class_get_name(oc)); 196 } 197 g_slist_free(list); 198 } 199 200 bool type_print_class_properties(const char *type) 201 { 202 ObjectClass *klass; 203 ObjectPropertyIterator iter; 204 ObjectProperty *prop; 205 GPtrArray *array; 206 int i; 207 208 klass = object_class_by_name(type); 209 if (!klass) { 210 return false; 211 } 212 213 array = g_ptr_array_new(); 214 object_class_property_iter_init(&iter, klass); 215 while ((prop = object_property_iter_next(&iter))) { 216 if (!prop->set) { 217 continue; 218 } 219 220 g_ptr_array_add(array, 221 object_property_help(prop->name, prop->type, 222 prop->defval, prop->description)); 223 } 224 g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); 225 if (array->len > 0) { 226 qemu_printf("%s options:\n", type); 227 } else { 228 qemu_printf("There are no options for %s.\n", type); 229 } 230 for (i = 0; i < array->len; i++) { 231 qemu_printf("%s\n", (char *)array->pdata[i]); 232 } 233 g_ptr_array_set_free_func(array, g_free); 234 g_ptr_array_free(array, true); 235 return true; 236 } 237 238 bool user_creatable_print_help(const char *type, QemuOpts *opts) 239 { 240 if (is_help_option(type)) { 241 user_creatable_print_types(); 242 return true; 243 } 244 245 if (qemu_opt_has_help_opt(opts)) { 246 return type_print_class_properties(type); 247 } 248 249 return false; 250 } 251 252 static void user_creatable_print_help_from_qdict(QDict *args) 253 { 254 const char *type = qdict_get_try_str(args, "qom-type"); 255 256 if (!type || !type_print_class_properties(type)) { 257 user_creatable_print_types(); 258 } 259 } 260 261 ObjectOptions *user_creatable_parse_str(const char *optarg, Error **errp) 262 { 263 ERRP_GUARD(); 264 QObject *obj; 265 bool help; 266 Visitor *v; 267 ObjectOptions *options; 268 269 if (optarg[0] == '{') { 270 obj = qobject_from_json(optarg, errp); 271 if (!obj) { 272 return NULL; 273 } 274 v = qobject_input_visitor_new(obj); 275 } else { 276 QDict *args = keyval_parse(optarg, "qom-type", &help, errp); 277 if (*errp) { 278 return NULL; 279 } 280 if (help) { 281 user_creatable_print_help_from_qdict(args); 282 qobject_unref(args); 283 return NULL; 284 } 285 286 obj = QOBJECT(args); 287 v = qobject_input_visitor_new_keyval(obj); 288 } 289 290 visit_type_ObjectOptions(v, NULL, &options, errp); 291 visit_free(v); 292 qobject_unref(obj); 293 294 return options; 295 } 296 297 bool user_creatable_add_from_str(const char *optarg, Error **errp) 298 { 299 ERRP_GUARD(); 300 ObjectOptions *options; 301 302 options = user_creatable_parse_str(optarg, errp); 303 if (!options) { 304 return false; 305 } 306 307 user_creatable_add_qapi(options, errp); 308 qapi_free_ObjectOptions(options); 309 return !*errp; 310 } 311 312 void user_creatable_process_cmdline(const char *optarg) 313 { 314 if (!user_creatable_add_from_str(optarg, &error_fatal)) { 315 /* Help was printed */ 316 exit(EXIT_SUCCESS); 317 } 318 } 319 320 bool user_creatable_del(const char *id, Error **errp) 321 { 322 QemuOptsList *opts_list; 323 Object *container; 324 Object *obj; 325 326 container = object_get_objects_root(); 327 obj = object_resolve_path_component(container, id); 328 if (!obj) { 329 error_setg(errp, "object '%s' not found", id); 330 return false; 331 } 332 333 if (!user_creatable_can_be_deleted(USER_CREATABLE(obj))) { 334 error_setg(errp, "object '%s' is in use, can not be deleted", id); 335 return false; 336 } 337 338 /* 339 * if object was defined on the command-line, remove its corresponding 340 * option group entry 341 */ 342 opts_list = qemu_find_opts_err("object", NULL); 343 if (opts_list) { 344 qemu_opts_del(qemu_opts_find(opts_list, id)); 345 } 346 347 object_unparent(obj); 348 return true; 349 } 350 351 void user_creatable_cleanup(void) 352 { 353 object_unparent(object_get_objects_root()); 354 } 355 356 static void register_types(void) 357 { 358 static const TypeInfo uc_interface_info = { 359 .name = TYPE_USER_CREATABLE, 360 .parent = TYPE_INTERFACE, 361 .class_size = sizeof(UserCreatableClass), 362 }; 363 364 type_register_static(&uc_interface_info); 365 } 366 367 type_init(register_types) 368