Statistics
| Branch: | Revision:

root / qerror.c @ 19bf7c87

History | View | Annotate | Download (15 kB)

1
/*
2
 * QError Module
3
 *
4
 * Copyright (C) 2009 Red Hat Inc.
5
 *
6
 * Authors:
7
 *  Luiz Capitulino <lcapitulino@redhat.com>
8
 *
9
 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10
 * See the COPYING.LIB file in the top-level directory.
11
 */
12

    
13
#include "monitor.h"
14
#include "qjson.h"
15
#include "qerror.h"
16
#include "qemu-common.h"
17

    
18
static void qerror_destroy_obj(QObject *obj);
19

    
20
static const QType qerror_type = {
21
    .code = QTYPE_QERROR,
22
    .destroy = qerror_destroy_obj,
23
};
24

    
25
/**
26
 * The 'desc' parameter is a printf-like string, the format of the format
27
 * string is:
28
 *
29
 * %(KEY)
30
 *
31
 * Where KEY is a QDict key, which has to be passed to qerror_from_info().
32
 *
33
 * Example:
34
 *
35
 * "foo error on device: %(device) slot: %(slot_nr)"
36
 *
37
 * A single percent sign can be printed if followed by a second one,
38
 * for example:
39
 *
40
 * "running out of foo: %(foo)%%"
41
 *
42
 * Please keep the entries in alphabetical order.
43
 * Use "sed -n '/^static.*qerror_table\[\]/,/^};/s/QERR_/&/gp' qerror.c | sort -c"
44
 * to check.
45
 */
46
static const QErrorStringTable qerror_table[] = {
47
    {
48
        .error_fmt = QERR_BAD_BUS_FOR_DEVICE,
49
        .desc      = "Device '%(device)' can't go on a %(bad_bus_type) bus",
50
    },
51
    {
52
        .error_fmt = QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
53
        .desc      = "Block format '%(format)' used by device '%(name)' does not support feature '%(feature)'",
54
    },
55
    {
56
        .error_fmt = QERR_BUS_NOT_FOUND,
57
        .desc      = "Bus '%(bus)' not found",
58
    },
59
    {
60
        .error_fmt = QERR_BUS_NO_HOTPLUG,
61
        .desc      = "Bus '%(bus)' does not support hotplugging",
62
    },
63
    {
64
        .error_fmt = QERR_COMMAND_NOT_FOUND,
65
        .desc      = "The command %(name) has not been found",
66
    },
67
    {
68
        .error_fmt = QERR_COMMAND_DISABLED,
69
        .desc      = "The command %(name) has been disabled for this instance",
70
    },
71
    {
72
        .error_fmt = QERR_DEVICE_ENCRYPTED,
73
        .desc      = "Device '%(device)' is encrypted",
74
    },
75
    {
76
        .error_fmt = QERR_DEVICE_INIT_FAILED,
77
        .desc      = "Device '%(device)' could not be initialized",
78
    },
79
    {
80
        .error_fmt = QERR_DEVICE_IN_USE,
81
        .desc      = "Device '%(device)' is in use",
82
    },
83
    {
84
        .error_fmt = QERR_DEVICE_FEATURE_BLOCKS_MIGRATION,
85
        .desc      = "Migration is disabled when using feature '%(feature)' in device '%(device)'",
86
    },
87
    {
88
        .error_fmt = QERR_DEVICE_LOCKED,
89
        .desc      = "Device '%(device)' is locked",
90
    },
91
    {
92
        .error_fmt = QERR_DEVICE_MULTIPLE_BUSSES,
93
        .desc      = "Device '%(device)' has multiple child busses",
94
    },
95
    {
96
        .error_fmt = QERR_DEVICE_NOT_ACTIVE,
97
        .desc      = "Device '%(device)' has not been activated",
98
    },
99
    {
100
        .error_fmt = QERR_DEVICE_NOT_ENCRYPTED,
101
        .desc      = "Device '%(device)' is not encrypted",
102
    },
103
    {
104
        .error_fmt = QERR_DEVICE_NOT_FOUND,
105
        .desc      = "Device '%(device)' not found",
106
    },
107
    {
108
        .error_fmt = QERR_DEVICE_NOT_REMOVABLE,
109
        .desc      = "Device '%(device)' is not removable",
110
    },
111
    {
112
        .error_fmt = QERR_DEVICE_NO_BUS,
113
        .desc      = "Device '%(device)' has no child bus",
114
    },
115
    {
116
        .error_fmt = QERR_DEVICE_NO_HOTPLUG,
117
        .desc      = "Device '%(device)' does not support hotplugging",
118
    },
119
    {
120
        .error_fmt = QERR_DUPLICATE_ID,
121
        .desc      = "Duplicate ID '%(id)' for %(object)",
122
    },
123
    {
124
        .error_fmt = QERR_FD_NOT_FOUND,
125
        .desc      = "File descriptor named '%(name)' not found",
126
    },
127
    {
128
        .error_fmt = QERR_FD_NOT_SUPPLIED,
129
        .desc      = "No file descriptor supplied via SCM_RIGHTS",
130
    },
131
    {
132
        .error_fmt = QERR_FEATURE_DISABLED,
133
        .desc      = "The feature '%(name)' is not enabled",
134
    },
135
    {
136
        .error_fmt = QERR_INVALID_BLOCK_FORMAT,
137
        .desc      = "Invalid block format '%(name)'",
138
    },
139
    {
140
        .error_fmt = QERR_INVALID_PARAMETER,
141
        .desc      = "Invalid parameter '%(name)'",
142
    },
143
    {
144
        .error_fmt = QERR_INVALID_PARAMETER_TYPE,
145
        .desc      = "Invalid parameter type, expected: %(expected)",
146
    },
147
    {
148
        .error_fmt = QERR_INVALID_PARAMETER_VALUE,
149
        .desc      = "Parameter '%(name)' expects %(expected)",
150
    },
151
    {
152
        .error_fmt = QERR_INVALID_PASSWORD,
153
        .desc      = "Password incorrect",
154
    },
155
    {
156
        .error_fmt = QERR_IO_ERROR,
157
        .desc      = "An IO error has occurred",
158
    },
159
    {
160
        .error_fmt = QERR_JSON_PARSING,
161
        .desc      = "Invalid JSON syntax",
162
    },
163
    {
164
        .error_fmt = QERR_JSON_PARSE_ERROR,
165
        .desc      = "JSON parse error, %(message)",
166

    
167
    },
168
    {
169
        .error_fmt = QERR_KVM_MISSING_CAP,
170
        .desc      = "Using KVM without %(capability), %(feature) unavailable",
171
    },
172
    {
173
        .error_fmt = QERR_MIGRATION_EXPECTED,
174
        .desc      = "An incoming migration is expected before this command can be executed",
175
    },
176
    {
177
        .error_fmt = QERR_MISSING_PARAMETER,
178
        .desc      = "Parameter '%(name)' is missing",
179
    },
180
    {
181
        .error_fmt = QERR_NO_BUS_FOR_DEVICE,
182
        .desc      = "No '%(bus)' bus found for device '%(device)'",
183
    },
184
    {
185
        .error_fmt = QERR_OPEN_FILE_FAILED,
186
        .desc      = "Could not open '%(filename)'",
187
    },
188
    {
189
        .error_fmt = QERR_PERMISSION_DENIED,
190
        .desc      = "Insufficient permission to perform this operation",
191
    },
192
    {
193
        .error_fmt = QERR_PROPERTY_NOT_FOUND,
194
        .desc      = "Property '%(device).%(property)' not found",
195
    },
196
    {
197
        .error_fmt = QERR_PROPERTY_VALUE_BAD,
198
        .desc      = "Property '%(device).%(property)' doesn't take value '%(value)'",
199
    },
200
    {
201
        .error_fmt = QERR_PROPERTY_VALUE_IN_USE,
202
        .desc      = "Property '%(device).%(property)' can't take value '%(value)', it's in use",
203
    },
204
    {
205
        .error_fmt = QERR_PROPERTY_VALUE_NOT_FOUND,
206
        .desc      = "Property '%(device).%(property)' can't find value '%(value)'",
207
    },
208
    {
209
        .error_fmt = QERR_PROPERTY_VALUE_OUT_OF_RANGE,
210
        .desc      = "Property '%(device).%(property)' doesn't take "
211
                     "value %(value) (minimum: %(min), maximum: %(max)'",
212
    },
213
    {
214
        .error_fmt = QERR_QMP_BAD_INPUT_OBJECT,
215
        .desc      = "Expected '%(expected)' in QMP input",
216
    },
217
    {
218
        .error_fmt = QERR_QMP_BAD_INPUT_OBJECT_MEMBER,
219
        .desc      = "QMP input object member '%(member)' expects '%(expected)'",
220
    },
221
    {
222
        .error_fmt = QERR_QMP_EXTRA_MEMBER,
223
        .desc      = "QMP input object member '%(member)' is unexpected",
224
    },
225
    {
226
        .error_fmt = QERR_RESET_REQUIRED,
227
        .desc      = "Resetting the Virtual Machine is required",
228
    },
229
    {
230
        .error_fmt = QERR_SET_PASSWD_FAILED,
231
        .desc      = "Could not set password",
232
    },
233
    {
234
        .error_fmt = QERR_ADD_CLIENT_FAILED,
235
        .desc      = "Could not add client",
236
    },
237
    {
238
        .error_fmt = QERR_TOO_MANY_FILES,
239
        .desc      = "Too many open files",
240
    },
241
    {
242
        .error_fmt = QERR_UNDEFINED_ERROR,
243
        .desc      = "An undefined error has occurred",
244
    },
245
    {
246
        .error_fmt = QERR_UNSUPPORTED,
247
        .desc      = "this feature or command is not currently supported",
248
    },
249
    {
250
        .error_fmt = QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
251
        .desc      = "'%(device)' uses a %(format) feature which is not "
252
                     "supported by this qemu version: %(feature)",
253
    },
254
    {
255
        .error_fmt = QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION,
256
        .desc      = "Migration is disabled when VirtFS export path '%(path)' "
257
                     "is mounted in the guest using mount_tag '%(tag)'",
258
    },
259
    {
260
        .error_fmt = QERR_VNC_SERVER_FAILED,
261
        .desc      = "Could not start VNC server on %(target)",
262
    },
263
    {
264
        .error_fmt = QERR_QGA_LOGGING_FAILED,
265
        .desc      = "Guest agent failed to log non-optional log statement",
266
    },
267
    {
268
        .error_fmt = QERR_QGA_COMMAND_FAILED,
269
        .desc      = "Guest agent command failed, error was '%(message)'",
270
    },
271
    {
272
        .error_fmt = QERR_INVALID_PARAMETER_COMBINATION,
273
        .desc      = "Invalid parameter combination",
274
    },
275
    {}
276
};
277

    
278
/**
279
 * qerror_new(): Create a new QError
280
 *
281
 * Return strong reference.
282
 */
283
QError *qerror_new(void)
284
{
285
    QError *qerr;
286

    
287
    qerr = g_malloc0(sizeof(*qerr));
288
    QOBJECT_INIT(qerr, &qerror_type);
289

    
290
    return qerr;
291
}
292

    
293
static void GCC_FMT_ATTR(2, 3) qerror_abort(const QError *qerr,
294
                                            const char *fmt, ...)
295
{
296
    va_list ap;
297

    
298
    fprintf(stderr, "qerror: bad call in function '%s':\n", qerr->func);
299
    fprintf(stderr, "qerror: -> ");
300

    
301
    va_start(ap, fmt);
302
    vfprintf(stderr, fmt, ap);
303
    va_end(ap);
304

    
305
    fprintf(stderr, "\nqerror: call at %s:%d\n", qerr->file, qerr->linenr);
306
    abort();
307
}
308

    
309
static void GCC_FMT_ATTR(2, 0) qerror_set_data(QError *qerr,
310
                                               const char *fmt, va_list *va)
311
{
312
    QObject *obj;
313

    
314
    obj = qobject_from_jsonv(fmt, va);
315
    if (!obj) {
316
        qerror_abort(qerr, "invalid format '%s'", fmt);
317
    }
318
    if (qobject_type(obj) != QTYPE_QDICT) {
319
        qerror_abort(qerr, "error format is not a QDict '%s'", fmt);
320
    }
321

    
322
    qerr->error = qobject_to_qdict(obj);
323

    
324
    obj = qdict_get(qerr->error, "class");
325
    if (!obj) {
326
        qerror_abort(qerr, "missing 'class' key in '%s'", fmt);
327
    }
328
    if (qobject_type(obj) != QTYPE_QSTRING) {
329
        qerror_abort(qerr, "'class' key value should be a QString");
330
    }
331
    
332
    obj = qdict_get(qerr->error, "data");
333
    if (!obj) {
334
        qerror_abort(qerr, "missing 'data' key in '%s'", fmt);
335
    }
336
    if (qobject_type(obj) != QTYPE_QDICT) {
337
        qerror_abort(qerr, "'data' key value should be a QDICT");
338
    }
339
}
340

    
341
static void qerror_set_desc(QError *qerr, const char *fmt)
342
{
343
    int i;
344

    
345
    // FIXME: inefficient loop
346

    
347
    for (i = 0; qerror_table[i].error_fmt; i++) {
348
        if (strcmp(qerror_table[i].error_fmt, fmt) == 0) {
349
            qerr->entry = &qerror_table[i];
350
            return;
351
        }
352
    }
353

    
354
    qerror_abort(qerr, "error format '%s' not found", fmt);
355
}
356

    
357
/**
358
 * qerror_from_info(): Create a new QError from error information
359
 *
360
 * The information consists of:
361
 *
362
 * - file   the file name of where the error occurred
363
 * - linenr the line number of where the error occurred
364
 * - func   the function name of where the error occurred
365
 * - fmt    JSON printf-like dictionary, there must exist keys 'class' and
366
 *          'data'
367
 * - va     va_list of all arguments specified by fmt
368
 *
369
 * Return strong reference.
370
 */
371
QError *qerror_from_info(const char *file, int linenr, const char *func,
372
                         const char *fmt, va_list *va)
373
{
374
    QError *qerr;
375

    
376
    qerr = qerror_new();
377
    loc_save(&qerr->loc);
378
    qerr->linenr = linenr;
379
    qerr->file = file;
380
    qerr->func = func;
381

    
382
    if (!fmt) {
383
        qerror_abort(qerr, "QDict not specified");
384
    }
385

    
386
    qerror_set_data(qerr, fmt, va);
387
    qerror_set_desc(qerr, fmt);
388

    
389
    return qerr;
390
}
391

    
392
static void parse_error(const QErrorStringTable *entry, int c)
393
{
394
    fprintf(stderr, "expected '%c' in '%s'", c, entry->desc);
395
    abort();
396
}
397

    
398
static const char *append_field(QDict *error, QString *outstr,
399
                                const QErrorStringTable *entry,
400
                                const char *start)
401
{
402
    QObject *obj;
403
    QDict *qdict;
404
    QString *key_qs;
405
    const char *end, *key;
406

    
407
    if (*start != '%')
408
        parse_error(entry, '%');
409
    start++;
410
    if (*start != '(')
411
        parse_error(entry, '(');
412
    start++;
413

    
414
    end = strchr(start, ')');
415
    if (!end)
416
        parse_error(entry, ')');
417

    
418
    key_qs = qstring_from_substr(start, 0, end - start - 1);
419
    key = qstring_get_str(key_qs);
420

    
421
    qdict = qobject_to_qdict(qdict_get(error, "data"));
422
    obj = qdict_get(qdict, key);
423
    if (!obj) {
424
        abort();
425
    }
426

    
427
    switch (qobject_type(obj)) {
428
        case QTYPE_QSTRING:
429
            qstring_append(outstr, qdict_get_str(qdict, key));
430
            break;
431
        case QTYPE_QINT:
432
            qstring_append_int(outstr, qdict_get_int(qdict, key));
433
            break;
434
        default:
435
            abort();
436
    }
437

    
438
    QDECREF(key_qs);
439
    return ++end;
440
}
441

    
442
static QString *qerror_format_desc(QDict *error,
443
                                   const QErrorStringTable *entry)
444
{
445
    QString *qstring;
446
    const char *p;
447

    
448
    assert(entry != NULL);
449

    
450
    qstring = qstring_new();
451

    
452
    for (p = entry->desc; *p != '\0';) {
453
        if (*p != '%') {
454
            qstring_append_chr(qstring, *p++);
455
        } else if (*(p + 1) == '%') {
456
            qstring_append_chr(qstring, '%');
457
            p += 2;
458
        } else {
459
            p = append_field(error, qstring, entry, p);
460
        }
461
    }
462

    
463
    return qstring;
464
}
465

    
466
QString *qerror_format(const char *fmt, QDict *error)
467
{
468
    const QErrorStringTable *entry = NULL;
469
    int i;
470

    
471
    for (i = 0; qerror_table[i].error_fmt; i++) {
472
        if (strcmp(qerror_table[i].error_fmt, fmt) == 0) {
473
            entry = &qerror_table[i];
474
            break;
475
        }
476
    }
477

    
478
    return qerror_format_desc(error, entry);
479
}
480

    
481
/**
482
 * qerror_human(): Format QError data into human-readable string.
483
 */
484
QString *qerror_human(const QError *qerror)
485
{
486
    return qerror_format_desc(qerror->error, qerror->entry);
487
}
488

    
489
/**
490
 * qerror_print(): Print QError data
491
 *
492
 * This function will print the member 'desc' of the specified QError object,
493
 * it uses error_report() for this, so that the output is routed to the right
494
 * place (ie. stderr or Monitor's device).
495
 */
496
void qerror_print(QError *qerror)
497
{
498
    QString *qstring = qerror_human(qerror);
499
    loc_push_restore(&qerror->loc);
500
    error_report("%s", qstring_get_str(qstring));
501
    loc_pop(&qerror->loc);
502
    QDECREF(qstring);
503
}
504

    
505
void qerror_report_internal(const char *file, int linenr, const char *func,
506
                            const char *fmt, ...)
507
{
508
    va_list va;
509
    QError *qerror;
510

    
511
    va_start(va, fmt);
512
    qerror = qerror_from_info(file, linenr, func, fmt, &va);
513
    va_end(va);
514

    
515
    if (monitor_cur_is_qmp()) {
516
        monitor_set_error(cur_mon, qerror);
517
    } else {
518
        qerror_print(qerror);
519
        QDECREF(qerror);
520
    }
521
}
522

    
523
/* Evil... */
524
struct Error
525
{
526
    QDict *obj;
527
    const char *fmt;
528
    char *msg;
529
};
530

    
531
void qerror_report_err(Error *err)
532
{
533
    QError *qerr;
534
    int i;
535

    
536
    qerr = qerror_new();
537
    loc_save(&qerr->loc);
538
    QINCREF(err->obj);
539
    qerr->error = err->obj;
540

    
541
    for (i = 0; qerror_table[i].error_fmt; i++) {
542
        if (strcmp(qerror_table[i].error_fmt, err->fmt) == 0) {
543
            qerr->entry = &qerror_table[i];
544
            break;
545
        }
546
    }
547

    
548
    if (monitor_cur_is_qmp()) {
549
        monitor_set_error(cur_mon, qerr);
550
    } else {
551
        qerror_print(qerr);
552
        QDECREF(qerr);
553
    }
554
}
555

    
556
/**
557
 * qobject_to_qerror(): Convert a QObject into a QError
558
 */
559
QError *qobject_to_qerror(const QObject *obj)
560
{
561
    if (qobject_type(obj) != QTYPE_QERROR) {
562
        return NULL;
563
    }
564

    
565
    return container_of(obj, QError, base);
566
}
567

    
568
/**
569
 * qerror_destroy_obj(): Free all memory allocated by a QError
570
 */
571
static void qerror_destroy_obj(QObject *obj)
572
{
573
    QError *qerr;
574

    
575
    assert(obj != NULL);
576
    qerr = qobject_to_qerror(obj);
577

    
578
    QDECREF(qerr->error);
579
    g_free(qerr);
580
}