xref: /qemu/qom/object_interfaces.c (revision 8d56d0fd2fcb5a99cd2f722d983c9c1199ff19b0)
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