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