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