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