Statistics
| Branch: | Revision:

root / qapi / opts-visitor.c @ 1de7afc9

History | View | Annotate | Download (10.6 kB)

1
/*
2
 * Options Visitor
3
 *
4
 * Copyright Red Hat, Inc. 2012
5
 *
6
 * Author: Laszlo Ersek <lersek@redhat.com>
7
 *
8
 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
9
 * See the COPYING.LIB file in the top-level directory.
10
 *
11
 */
12

    
13
#include "qemu-common.h"
14
#include "qapi/qmp/qerror.h"
15
#include "qapi/opts-visitor.h"
16
#include "qemu/queue.h"
17
#include "qemu/option_int.h"
18
#include "qapi/visitor-impl.h"
19

    
20

    
21
struct OptsVisitor
22
{
23
    Visitor visitor;
24

    
25
    /* Ownership remains with opts_visitor_new()'s caller. */
26
    const QemuOpts *opts_root;
27

    
28
    unsigned depth;
29

    
30
    /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value
31
     * is a non-empty GQueue, enumerating all QemuOpt occurrences with that
32
     * name. */
33
    GHashTable *unprocessed_opts;
34

    
35
    /* The list currently being traversed with opts_start_list() /
36
     * opts_next_list(). The list must have a struct element type in the
37
     * schema, with a single mandatory scalar member. */
38
    GQueue *repeated_opts;
39
    bool repeated_opts_first;
40

    
41
    /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for
42
     * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does
43
     * not survive or escape the OptsVisitor object.
44
     */
45
    QemuOpt *fake_id_opt;
46
};
47

    
48

    
49
static void
50
destroy_list(gpointer list)
51
{
52
  g_queue_free(list);
53
}
54

    
55

    
56
static void
57
opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
58
{
59
    GQueue *list;
60

    
61
    list = g_hash_table_lookup(unprocessed_opts, opt->name);
62
    if (list == NULL) {
63
        list = g_queue_new();
64

    
65
        /* GHashTable will never try to free the keys -- we supply NULL as
66
         * "key_destroy_func" in opts_start_struct(). Thus cast away key
67
         * const-ness in order to suppress gcc's warning.
68
         */
69
        g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list);
70
    }
71

    
72
    /* Similarly, destroy_list() doesn't call g_queue_free_full(). */
73
    g_queue_push_tail(list, (gpointer)opt);
74
}
75

    
76

    
77
static void
78
opts_start_struct(Visitor *v, void **obj, const char *kind,
79
                  const char *name, size_t size, Error **errp)
80
{
81
    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
82
    const QemuOpt *opt;
83

    
84
    *obj = g_malloc0(size > 0 ? size : 1);
85
    if (ov->depth++ > 0) {
86
        return;
87
    }
88

    
89
    ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal,
90
                                                 NULL, &destroy_list);
91
    QTAILQ_FOREACH(opt, &ov->opts_root->head, next) {
92
        /* ensured by qemu-option.c::opts_do_parse() */
93
        assert(strcmp(opt->name, "id") != 0);
94

    
95
        opts_visitor_insert(ov->unprocessed_opts, opt);
96
    }
97

    
98
    if (ov->opts_root->id != NULL) {
99
        ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt);
100

    
101
        ov->fake_id_opt->name = "id";
102
        ov->fake_id_opt->str = ov->opts_root->id;
103
        opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
104
    }
105
}
106

    
107

    
108
static gboolean
109
ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data)
110
{
111
    return TRUE;
112
}
113

    
114

    
115
static void
116
opts_end_struct(Visitor *v, Error **errp)
117
{
118
    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
119
    GQueue *any;
120

    
121
    if (--ov->depth > 0) {
122
        return;
123
    }
124

    
125
    /* we should have processed all (distinct) QemuOpt instances */
126
    any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL);
127
    if (any) {
128
        const QemuOpt *first;
129

    
130
        first = g_queue_peek_head(any);
131
        error_set(errp, QERR_INVALID_PARAMETER, first->name);
132
    }
133
    g_hash_table_destroy(ov->unprocessed_opts);
134
    ov->unprocessed_opts = NULL;
135
    g_free(ov->fake_id_opt);
136
    ov->fake_id_opt = NULL;
137
}
138

    
139

    
140
static GQueue *
141
lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
142
{
143
    GQueue *list;
144

    
145
    list = g_hash_table_lookup(ov->unprocessed_opts, name);
146
    if (!list) {
147
        error_set(errp, QERR_MISSING_PARAMETER, name);
148
    }
149
    return list;
150
}
151

    
152

    
153
static void
154
opts_start_list(Visitor *v, const char *name, Error **errp)
155
{
156
    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
157

    
158
    /* we can't traverse a list in a list */
159
    assert(ov->repeated_opts == NULL);
160
    ov->repeated_opts = lookup_distinct(ov, name, errp);
161
    ov->repeated_opts_first = (ov->repeated_opts != NULL);
162
}
163

    
164

    
165
static GenericList *
166
opts_next_list(Visitor *v, GenericList **list, Error **errp)
167
{
168
    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
169
    GenericList **link;
170

    
171
    if (ov->repeated_opts_first) {
172
        ov->repeated_opts_first = false;
173
        link = list;
174
    } else {
175
        const QemuOpt *opt;
176

    
177
        opt = g_queue_pop_head(ov->repeated_opts);
178
        if (g_queue_is_empty(ov->repeated_opts)) {
179
            g_hash_table_remove(ov->unprocessed_opts, opt->name);
180
            return NULL;
181
        }
182
        link = &(*list)->next;
183
    }
184

    
185
    *link = g_malloc0(sizeof **link);
186
    return *link;
187
}
188

    
189

    
190
static void
191
opts_end_list(Visitor *v, Error **errp)
192
{
193
    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
194

    
195
    ov->repeated_opts = NULL;
196
}
197

    
198

    
199
static const QemuOpt *
200
lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
201
{
202
    if (ov->repeated_opts == NULL) {
203
        GQueue *list;
204

    
205
        /* the last occurrence of any QemuOpt takes effect when queried by name
206
         */
207
        list = lookup_distinct(ov, name, errp);
208
        return list ? g_queue_peek_tail(list) : NULL;
209
    }
210
    return g_queue_peek_head(ov->repeated_opts);
211
}
212

    
213

    
214
static void
215
processed(OptsVisitor *ov, const char *name)
216
{
217
    if (ov->repeated_opts == NULL) {
218
        g_hash_table_remove(ov->unprocessed_opts, name);
219
    }
220
}
221

    
222

    
223
static void
224
opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
225
{
226
    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
227
    const QemuOpt *opt;
228

    
229
    opt = lookup_scalar(ov, name, errp);
230
    if (!opt) {
231
        return;
232
    }
233
    *obj = g_strdup(opt->str ? opt->str : "");
234
    processed(ov, name);
235
}
236

    
237

    
238
/* mimics qemu-option.c::parse_option_bool() */
239
static void
240
opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
241
{
242
    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
243
    const QemuOpt *opt;
244

    
245
    opt = lookup_scalar(ov, name, errp);
246
    if (!opt) {
247
        return;
248
    }
249

    
250
    if (opt->str) {
251
        if (strcmp(opt->str, "on") == 0 ||
252
            strcmp(opt->str, "yes") == 0 ||
253
            strcmp(opt->str, "y") == 0) {
254
            *obj = true;
255
        } else if (strcmp(opt->str, "off") == 0 ||
256
            strcmp(opt->str, "no") == 0 ||
257
            strcmp(opt->str, "n") == 0) {
258
            *obj = false;
259
        } else {
260
            error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
261
                "on|yes|y|off|no|n");
262
            return;
263
        }
264
    } else {
265
        *obj = true;
266
    }
267

    
268
    processed(ov, name);
269
}
270

    
271

    
272
static void
273
opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
274
{
275
    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
276
    const QemuOpt *opt;
277
    const char *str;
278
    long long val;
279
    char *endptr;
280

    
281
    opt = lookup_scalar(ov, name, errp);
282
    if (!opt) {
283
        return;
284
    }
285
    str = opt->str ? opt->str : "";
286

    
287
    errno = 0;
288
    val = strtoll(str, &endptr, 0);
289
    if (*str != '\0' && *endptr == '\0' && errno == 0 && INT64_MIN <= val &&
290
        val <= INT64_MAX) {
291
        *obj = val;
292
        processed(ov, name);
293
        return;
294
    }
295
    error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, "an int64 value");
296
}
297

    
298

    
299
static void
300
opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
301
{
302
    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
303
    const QemuOpt *opt;
304
    const char *str;
305

    
306
    opt = lookup_scalar(ov, name, errp);
307
    if (!opt) {
308
        return;
309
    }
310

    
311
    str = opt->str;
312
    if (str != NULL) {
313
        while (isspace((unsigned char)*str)) {
314
            ++str;
315
        }
316

    
317
        if (*str != '-' && *str != '\0') {
318
            unsigned long long val;
319
            char *endptr;
320

    
321
            /* non-empty, non-negative subject sequence */
322
            errno = 0;
323
            val = strtoull(str, &endptr, 0);
324
            if (*endptr == '\0' && errno == 0 && val <= UINT64_MAX) {
325
                *obj = val;
326
                processed(ov, name);
327
                return;
328
            }
329
        }
330
    }
331
    error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
332
              "an uint64 value");
333
}
334

    
335

    
336
static void
337
opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
338
{
339
    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
340
    const QemuOpt *opt;
341
    int64_t val;
342
    char *endptr;
343

    
344
    opt = lookup_scalar(ov, name, errp);
345
    if (!opt) {
346
        return;
347
    }
348

    
349
    val = strtosz_suffix(opt->str ? opt->str : "", &endptr,
350
                         STRTOSZ_DEFSUFFIX_B);
351
    if (val != -1 && *endptr == '\0') {
352
        *obj = val;
353
        processed(ov, name);
354
        return;
355
    }
356
    error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
357
              "a size value representible as a non-negative int64");
358
}
359

    
360

    
361
static void
362
opts_start_optional(Visitor *v, bool *present, const char *name,
363
                       Error **errp)
364
{
365
    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
366

    
367
    /* we only support a single mandatory scalar field in a list node */
368
    assert(ov->repeated_opts == NULL);
369
    *present = (lookup_distinct(ov, name, NULL) != NULL);
370
}
371

    
372

    
373
OptsVisitor *
374
opts_visitor_new(const QemuOpts *opts)
375
{
376
    OptsVisitor *ov;
377

    
378
    ov = g_malloc0(sizeof *ov);
379

    
380
    ov->visitor.start_struct = &opts_start_struct;
381
    ov->visitor.end_struct   = &opts_end_struct;
382

    
383
    ov->visitor.start_list = &opts_start_list;
384
    ov->visitor.next_list  = &opts_next_list;
385
    ov->visitor.end_list   = &opts_end_list;
386

    
387
    /* input_type_enum() covers both "normal" enums and union discriminators.
388
     * The union discriminator field is always generated as "type"; it should
389
     * match the "type" QemuOpt child of any QemuOpts.
390
     *
391
     * input_type_enum() will remove the looked-up key from the
392
     * "unprocessed_opts" hash even if the lookup fails, because the removal is
393
     * done earlier in opts_type_str(). This should be harmless.
394
     */
395
    ov->visitor.type_enum = &input_type_enum;
396

    
397
    ov->visitor.type_int    = &opts_type_int;
398
    ov->visitor.type_uint64 = &opts_type_uint64;
399
    ov->visitor.type_size   = &opts_type_size;
400
    ov->visitor.type_bool   = &opts_type_bool;
401
    ov->visitor.type_str    = &opts_type_str;
402

    
403
    /* type_number() is not filled in, but this is not the first visitor to
404
     * skip some mandatory methods... */
405

    
406
    ov->visitor.start_optional = &opts_start_optional;
407

    
408
    ov->opts_root = opts;
409

    
410
    return ov;
411
}
412

    
413

    
414
void
415
opts_visitor_cleanup(OptsVisitor *ov)
416
{
417
    if (ov->unprocessed_opts != NULL) {
418
        g_hash_table_destroy(ov->unprocessed_opts);
419
    }
420
    g_free(ov->fake_id_opt);
421
    g_free(ov);
422
}
423

    
424

    
425
Visitor *
426
opts_get_visitor(OptsVisitor *ov)
427
{
428
    return &ov->visitor;
429
}