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
user_creatable_complete(UserCreatable * uc,Error ** errp)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
user_creatable_can_be_deleted(UserCreatable * uc)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
object_set_properties_from_qdict(Object * obj,const QDict * qdict,Visitor * v,Error ** errp)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
object_set_properties_from_keyval(Object * obj,const QDict * qdict,bool from_json,Error ** errp)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
user_creatable_add_type(const char * type,const char * id,const QDict * qdict,Visitor * v,Error ** errp)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
user_creatable_add_qapi(ObjectOptions * options,Error ** errp)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
object_property_help(const char * name,const char * type,QObject * defval,const char * description)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
user_creatable_print_types(void)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
type_print_class_properties(const char * type)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
user_creatable_print_help(const char * type,QemuOpts * opts)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
user_creatable_print_help_from_qdict(QDict * args)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
user_creatable_parse_str(const char * str,Error ** errp)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
user_creatable_add_from_str(const char * str,Error ** errp)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
user_creatable_process_cmdline(const char * cmdline)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
user_creatable_del(const char * id,Error ** errp)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
user_creatable_cleanup(void)370 void user_creatable_cleanup(void)
371 {
372 object_unparent(object_get_objects_root());
373 }
374
register_types(void)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