Home | History | Annotate | Download | only in qemu
      1 /*
      2  * QError Module
      3  *
      4  * Copyright (C) 2009 Red Hat Inc.
      5  *
      6  * Authors:
      7  *  Luiz Capitulino <lcapitulino (at) 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_BUS_NOT_FOUND,
     53         .desc      = "Bus '%(bus)' not found",
     54     },
     55     {
     56         .error_fmt = QERR_BUS_NO_HOTPLUG,
     57         .desc      = "Bus '%(bus)' does not support hotplugging",
     58     },
     59     {
     60         .error_fmt = QERR_COMMAND_NOT_FOUND,
     61         .desc      = "The command %(name) has not been found",
     62     },
     63     {
     64         .error_fmt = QERR_DEVICE_ENCRYPTED,
     65         .desc      = "Device '%(device)' is encrypted",
     66     },
     67     {
     68         .error_fmt = QERR_DEVICE_INIT_FAILED,
     69         .desc      = "Device '%(device)' could not be initialized",
     70     },
     71     {
     72         .error_fmt = QERR_DEVICE_IN_USE,
     73         .desc      = "Device '%(device)' is in use",
     74     },
     75     {
     76         .error_fmt = QERR_DEVICE_LOCKED,
     77         .desc      = "Device '%(device)' is locked",
     78     },
     79     {
     80         .error_fmt = QERR_DEVICE_MULTIPLE_BUSSES,
     81         .desc      = "Device '%(device)' has multiple child busses",
     82     },
     83     {
     84         .error_fmt = QERR_DEVICE_NOT_ACTIVE,
     85         .desc      = "Device '%(device)' has not been activated",
     86     },
     87     {
     88         .error_fmt = QERR_DEVICE_NOT_ENCRYPTED,
     89         .desc      = "Device '%(device)' is not encrypted",
     90     },
     91     {
     92         .error_fmt = QERR_DEVICE_NOT_FOUND,
     93         .desc      = "Device '%(device)' not found",
     94     },
     95     {
     96         .error_fmt = QERR_DEVICE_NOT_REMOVABLE,
     97         .desc      = "Device '%(device)' is not removable",
     98     },
     99     {
    100         .error_fmt = QERR_DEVICE_NO_BUS,
    101         .desc      = "Device '%(device)' has no child bus",
    102     },
    103     {
    104         .error_fmt = QERR_DEVICE_NO_HOTPLUG,
    105         .desc      = "Device '%(device)' does not support hotplugging",
    106     },
    107     {
    108         .error_fmt = QERR_DUPLICATE_ID,
    109         .desc      = "Duplicate ID '%(id)' for %(object)",
    110     },
    111     {
    112         .error_fmt = QERR_FD_NOT_FOUND,
    113         .desc      = "File descriptor named '%(name)' not found",
    114     },
    115     {
    116         .error_fmt = QERR_FD_NOT_SUPPLIED,
    117         .desc      = "No file descriptor supplied via SCM_RIGHTS",
    118     },
    119     {
    120         .error_fmt = QERR_INVALID_BLOCK_FORMAT,
    121         .desc      = "Invalid block format '%(name)'",
    122     },
    123     {
    124         .error_fmt = QERR_INVALID_PARAMETER,
    125         .desc      = "Invalid parameter '%(name)'",
    126     },
    127     {
    128         .error_fmt = QERR_INVALID_PARAMETER_TYPE,
    129         .desc      = "Invalid parameter type, expected: %(expected)",
    130     },
    131     {
    132         .error_fmt = QERR_INVALID_PARAMETER_VALUE,
    133         .desc      = "Parameter '%(name)' expects %(expected)",
    134     },
    135     {
    136         .error_fmt = QERR_INVALID_PASSWORD,
    137         .desc      = "Password incorrect",
    138     },
    139     {
    140         .error_fmt = QERR_JSON_PARSING,
    141         .desc      = "Invalid JSON syntax",
    142     },
    143     {
    144         .error_fmt = QERR_KVM_MISSING_CAP,
    145         .desc      = "Using KVM without %(capability), %(feature) unavailable",
    146     },
    147     {
    148         .error_fmt = QERR_MIGRATION_EXPECTED,
    149         .desc      = "An incoming migration is expected before this command can be executed",
    150     },
    151     {
    152         .error_fmt = QERR_MISSING_PARAMETER,
    153         .desc      = "Parameter '%(name)' is missing",
    154     },
    155     {
    156         .error_fmt = QERR_NO_BUS_FOR_DEVICE,
    157         .desc      = "No '%(bus)' bus found for device '%(device)'",
    158     },
    159     {
    160         .error_fmt = QERR_OPEN_FILE_FAILED,
    161         .desc      = "Could not open '%(filename)'",
    162     },
    163     {
    164         .error_fmt = QERR_PROPERTY_NOT_FOUND,
    165         .desc      = "Property '%(device).%(property)' not found",
    166     },
    167     {
    168         .error_fmt = QERR_PROPERTY_VALUE_BAD,
    169         .desc      = "Property '%(device).%(property)' doesn't take value '%(value)'",
    170     },
    171     {
    172         .error_fmt = QERR_PROPERTY_VALUE_IN_USE,
    173         .desc      = "Property '%(device).%(property)' can't take value '%(value)', it's in use",
    174     },
    175     {
    176         .error_fmt = QERR_PROPERTY_VALUE_NOT_FOUND,
    177         .desc      = "Property '%(device).%(property)' can't find value '%(value)'",
    178     },
    179     {
    180         .error_fmt = QERR_QMP_BAD_INPUT_OBJECT,
    181         .desc      = "Expected '%(expected)' in QMP input",
    182     },
    183     {
    184         .error_fmt = QERR_QMP_BAD_INPUT_OBJECT_MEMBER,
    185         .desc      = "QMP input object member '%(member)' expects '%(expected)'",
    186     },
    187     {
    188         .error_fmt = QERR_QMP_EXTRA_MEMBER,
    189         .desc      = "QMP input object member '%(member)' is unexpected",
    190     },
    191     {
    192         .error_fmt = QERR_SET_PASSWD_FAILED,
    193         .desc      = "Could not set password",
    194     },
    195     {
    196         .error_fmt = QERR_TOO_MANY_FILES,
    197         .desc      = "Too many open files",
    198     },
    199     {
    200         .error_fmt = QERR_UNDEFINED_ERROR,
    201         .desc      = "An undefined error has ocurred",
    202     },
    203     {
    204         .error_fmt = QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
    205         .desc      = "'%(device)' uses a %(format) feature which is not "
    206                      "supported by this qemu version: %(feature)",
    207     },
    208     {
    209         .error_fmt = QERR_VNC_SERVER_FAILED,
    210         .desc      = "Could not start VNC server on %(target)",
    211     },
    212     {}
    213 };
    214 
    215 /**
    216  * qerror_new(): Create a new QError
    217  *
    218  * Return strong reference.
    219  */
    220 QError *qerror_new(void)
    221 {
    222     QError *qerr;
    223 
    224     qerr = qemu_mallocz(sizeof(*qerr));
    225     QOBJECT_INIT(qerr, &qerror_type);
    226 
    227     return qerr;
    228 }
    229 
    230 static void GCC_FMT_ATTR(2, 3) qerror_abort(const QError *qerr,
    231                                             const char *fmt, ...)
    232 {
    233     va_list ap;
    234 
    235     fprintf(stderr, "qerror: bad call in function '%s':\n", qerr->func);
    236     fprintf(stderr, "qerror: -> ");
    237 
    238     va_start(ap, fmt);
    239     vfprintf(stderr, fmt, ap);
    240     va_end(ap);
    241 
    242     fprintf(stderr, "\nqerror: call at %s:%d\n", qerr->file, qerr->linenr);
    243     abort();
    244 }
    245 
    246 static void GCC_FMT_ATTR(2, 0) qerror_set_data(QError *qerr,
    247                                                const char *fmt, va_list *va)
    248 {
    249     QObject *obj;
    250 
    251     obj = qobject_from_jsonv(fmt, va);
    252     if (!obj) {
    253         qerror_abort(qerr, "invalid format '%s'", fmt);
    254     }
    255     if (qobject_type(obj) != QTYPE_QDICT) {
    256         qerror_abort(qerr, "error format is not a QDict '%s'", fmt);
    257     }
    258 
    259     qerr->error = qobject_to_qdict(obj);
    260 
    261     obj = qdict_get(qerr->error, "class");
    262     if (!obj) {
    263         qerror_abort(qerr, "missing 'class' key in '%s'", fmt);
    264     }
    265     if (qobject_type(obj) != QTYPE_QSTRING) {
    266         qerror_abort(qerr, "'class' key value should be a QString");
    267     }
    268 
    269     obj = qdict_get(qerr->error, "data");
    270     if (!obj) {
    271         qerror_abort(qerr, "missing 'data' key in '%s'", fmt);
    272     }
    273     if (qobject_type(obj) != QTYPE_QDICT) {
    274         qerror_abort(qerr, "'data' key value should be a QDICT");
    275     }
    276 }
    277 
    278 static void qerror_set_desc(QError *qerr, const char *fmt)
    279 {
    280     int i;
    281 
    282     // FIXME: inefficient loop
    283 
    284     for (i = 0; qerror_table[i].error_fmt; i++) {
    285         if (strcmp(qerror_table[i].error_fmt, fmt) == 0) {
    286             qerr->entry = &qerror_table[i];
    287             return;
    288         }
    289     }
    290 
    291     qerror_abort(qerr, "error format '%s' not found", fmt);
    292 }
    293 
    294 /**
    295  * qerror_from_info(): Create a new QError from error information
    296  *
    297  * The information consists of:
    298  *
    299  * - file   the file name of where the error occurred
    300  * - linenr the line number of where the error occurred
    301  * - func   the function name of where the error occurred
    302  * - fmt    JSON printf-like dictionary, there must exist keys 'class' and
    303  *          'data'
    304  * - va     va_list of all arguments specified by fmt
    305  *
    306  * Return strong reference.
    307  */
    308 QError *qerror_from_info(const char *file, int linenr, const char *func,
    309                          const char *fmt, va_list *va)
    310 {
    311     QError *qerr;
    312 
    313     qerr = qerror_new();
    314     loc_save(&qerr->loc);
    315     qerr->linenr = linenr;
    316     qerr->file = file;
    317     qerr->func = func;
    318 
    319     if (!fmt) {
    320         qerror_abort(qerr, "QDict not specified");
    321     }
    322 
    323     qerror_set_data(qerr, fmt, va);
    324     qerror_set_desc(qerr, fmt);
    325 
    326     return qerr;
    327 }
    328 
    329 static void parse_error(const QError *qerror, int c)
    330 {
    331     qerror_abort(qerror, "expected '%c' in '%s'", c, qerror->entry->desc);
    332 }
    333 
    334 static const char *append_field(QString *outstr, const QError *qerror,
    335                                 const char *start)
    336 {
    337     QObject *obj;
    338     QDict *qdict;
    339     QString *key_qs;
    340     const char *end, *key;
    341 
    342     if (*start != '%')
    343         parse_error(qerror, '%');
    344     start++;
    345     if (*start != '(')
    346         parse_error(qerror, '(');
    347     start++;
    348 
    349     end = strchr(start, ')');
    350     if (!end)
    351         parse_error(qerror, ')');
    352 
    353     key_qs = qstring_from_substr(start, 0, end - start - 1);
    354     key = qstring_get_str(key_qs);
    355 
    356     qdict = qobject_to_qdict(qdict_get(qerror->error, "data"));
    357     obj = qdict_get(qdict, key);
    358     if (!obj) {
    359         qerror_abort(qerror, "key '%s' not found in QDict", key);
    360     }
    361 
    362     switch (qobject_type(obj)) {
    363         case QTYPE_QSTRING:
    364             qstring_append(outstr, qdict_get_str(qdict, key));
    365             break;
    366         case QTYPE_QINT:
    367             qstring_append_int(outstr, qdict_get_int(qdict, key));
    368             break;
    369         default:
    370             qerror_abort(qerror, "invalid type '%c'", qobject_type(obj));
    371     }
    372 
    373     QDECREF(key_qs);
    374     return ++end;
    375 }
    376 
    377 /**
    378  * qerror_human(): Format QError data into human-readable string.
    379  *
    380  * Formats according to member 'desc' of the specified QError object.
    381  */
    382 QString *qerror_human(const QError *qerror)
    383 {
    384     const char *p;
    385     QString *qstring;
    386 
    387     assert(qerror->entry != NULL);
    388 
    389     qstring = qstring_new();
    390 
    391     for (p = qerror->entry->desc; *p != '\0';) {
    392         if (*p != '%') {
    393             qstring_append_chr(qstring, *p++);
    394         } else if (*(p + 1) == '%') {
    395             qstring_append_chr(qstring, '%');
    396             p += 2;
    397         } else {
    398             p = append_field(qstring, qerror, p);
    399         }
    400     }
    401 
    402     return qstring;
    403 }
    404 
    405 /**
    406  * qerror_print(): Print QError data
    407  *
    408  * This function will print the member 'desc' of the specified QError object,
    409  * it uses error_report() for this, so that the output is routed to the right
    410  * place (ie. stderr or Monitor's device).
    411  */
    412 void qerror_print(QError *qerror)
    413 {
    414     QString *qstring = qerror_human(qerror);
    415     loc_push_restore(&qerror->loc);
    416     error_report("%s", qstring_get_str(qstring));
    417     loc_pop(&qerror->loc);
    418     QDECREF(qstring);
    419 }
    420 
    421 void qerror_report_internal(const char *file, int linenr, const char *func,
    422                             const char *fmt, ...)
    423 {
    424     va_list va;
    425     QError *qerror;
    426 
    427     va_start(va, fmt);
    428     qerror = qerror_from_info(file, linenr, func, fmt, &va);
    429     va_end(va);
    430 
    431     if (monitor_cur_is_qmp()) {
    432         monitor_set_error(cur_mon, qerror);
    433     } else {
    434         qerror_print(qerror);
    435         QDECREF(qerror);
    436     }
    437 }
    438 
    439 /**
    440  * qobject_to_qerror(): Convert a QObject into a QError
    441  */
    442 QError *qobject_to_qerror(const QObject *obj)
    443 {
    444     if (qobject_type(obj) != QTYPE_QERROR) {
    445         return NULL;
    446     }
    447 
    448     return container_of(obj, QError, base);
    449 }
    450 
    451 /**
    452  * qerror_destroy_obj(): Free all memory allocated by a QError
    453  */
    454 static void qerror_destroy_obj(QObject *obj)
    455 {
    456     QError *qerr;
    457 
    458     assert(obj != NULL);
    459     qerr = qobject_to_qerror(obj);
    460 
    461     QDECREF(qerr->error);
    462     qemu_free(qerr);
    463 }
    464