Home | History | Annotate | Download | only in qemu
      1 /*
      2  * QError: QEMU Error data-type.
      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 #include "qjson.h"
     13 #include "qerror.h"
     14 #include "qstring.h"
     15 #include "sysemu.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 static const QErrorStringTable qerror_table[] = {
     43     {
     44         .error_fmt = QERR_COMMAND_NOT_FOUND,
     45         .desc      = "The command %(name) has not been found",
     46     },
     47     {
     48         .error_fmt = QERR_DEVICE_ENCRYPTED,
     49         .desc      = "The %(device) is encrypted",
     50     },
     51     {
     52         .error_fmt = QERR_DEVICE_LOCKED,
     53         .desc      = "Device %(device) is locked",
     54     },
     55     {
     56         .error_fmt = QERR_DEVICE_NOT_ACTIVE,
     57         .desc      = "The %(device) device has not been activated by the guest",
     58     },
     59     {
     60         .error_fmt = QERR_DEVICE_NOT_FOUND,
     61         .desc      = "The %(device) device has not been found",
     62     },
     63     {
     64         .error_fmt = QERR_DEVICE_NOT_REMOVABLE,
     65         .desc      = "Device %(device) is not removable",
     66     },
     67     {
     68         .error_fmt = QERR_FD_NOT_FOUND,
     69         .desc      = "Failed to find file descriptor named %(name)",
     70     },
     71     {
     72         .error_fmt = QERR_FD_NOT_SUPPLIED,
     73         .desc      = "No file descriptor supplied via SCM_RIGHTS",
     74     },
     75     {
     76         .error_fmt = QERR_OPEN_FILE_FAILED,
     77         .desc      = "Could not open '%(filename)'",
     78     },
     79     {
     80         .error_fmt = QERR_INVALID_BLOCK_FORMAT,
     81         .desc      = "Invalid block format %(name)",
     82     },
     83     {
     84         .error_fmt = QERR_INVALID_PARAMETER,
     85         .desc      = "Invalid parameter %(name)",
     86     },
     87     {
     88         .error_fmt = QERR_INVALID_PARAMETER_TYPE,
     89         .desc      = "Invalid parameter type, expected: %(expected)",
     90     },
     91     {
     92         .error_fmt = QERR_INVALID_PASSWORD,
     93         .desc      = "The entered password is invalid",
     94     },
     95     {
     96         .error_fmt = QERR_JSON_PARSING,
     97         .desc      = "Invalid JSON syntax",
     98     },
     99     {
    100         .error_fmt = QERR_KVM_MISSING_CAP,
    101         .desc      = "Using KVM without %(capability), %(feature) unavailable",
    102     },
    103     {
    104         .error_fmt = QERR_MISSING_PARAMETER,
    105         .desc      = "Parameter %(name) is missing",
    106     },
    107     {
    108         .error_fmt = QERR_QMP_BAD_INPUT_OBJECT,
    109         .desc      = "Bad QMP input object",
    110     },
    111     {
    112         .error_fmt = QERR_SET_PASSWD_FAILED,
    113         .desc      = "Could not set password",
    114     },
    115     {
    116         .error_fmt = QERR_TOO_MANY_FILES,
    117         .desc      = "Too many open files",
    118     },
    119     {
    120         .error_fmt = QERR_UNDEFINED_ERROR,
    121         .desc      = "An undefined error has ocurred",
    122     },
    123     {
    124         .error_fmt = QERR_VNC_SERVER_FAILED,
    125         .desc      = "Could not start VNC server on %(target)",
    126     },
    127     {}
    128 };
    129 
    130 /**
    131  * qerror_new(): Create a new QError
    132  *
    133  * Return strong reference.
    134  */
    135 QError *qerror_new(void)
    136 {
    137     QError *qerr;
    138 
    139     qerr = qemu_mallocz(sizeof(*qerr));
    140     QOBJECT_INIT(qerr, &qerror_type);
    141 
    142     return qerr;
    143 }
    144 
    145 static void qerror_abort(const QError *qerr, const char *fmt, ...)
    146 {
    147     va_list ap;
    148 
    149     fprintf(stderr, "qerror: bad call in function '%s':\n", qerr->func);
    150     fprintf(stderr, "qerror: -> ");
    151 
    152     va_start(ap, fmt);
    153     vfprintf(stderr, fmt, ap);
    154     va_end(ap);
    155 
    156     fprintf(stderr, "\nqerror: call at %s:%d\n", qerr->file, qerr->linenr);
    157     abort();
    158 }
    159 
    160 static void qerror_set_data(QError *qerr, const char *fmt, va_list *va)
    161 {
    162     QObject *obj;
    163 
    164     obj = qobject_from_jsonv(fmt, va);
    165     if (!obj) {
    166         qerror_abort(qerr, "invalid format '%s'", fmt);
    167     }
    168     if (qobject_type(obj) != QTYPE_QDICT) {
    169         qerror_abort(qerr, "error format is not a QDict '%s'", fmt);
    170     }
    171 
    172     qerr->error = qobject_to_qdict(obj);
    173 
    174     obj = qdict_get(qerr->error, "class");
    175     if (!obj) {
    176         qerror_abort(qerr, "missing 'class' key in '%s'", fmt);
    177     }
    178     if (qobject_type(obj) != QTYPE_QSTRING) {
    179         qerror_abort(qerr, "'class' key value should be a QString");
    180     }
    181 
    182     obj = qdict_get(qerr->error, "data");
    183     if (!obj) {
    184         qerror_abort(qerr, "missing 'data' key in '%s'", fmt);
    185     }
    186     if (qobject_type(obj) != QTYPE_QDICT) {
    187         qerror_abort(qerr, "'data' key value should be a QDICT");
    188     }
    189 }
    190 
    191 static void qerror_set_desc(QError *qerr, const char *fmt)
    192 {
    193     int i;
    194 
    195     // FIXME: inefficient loop
    196 
    197     for (i = 0; qerror_table[i].error_fmt; i++) {
    198         if (strcmp(qerror_table[i].error_fmt, fmt) == 0) {
    199             qerr->entry = &qerror_table[i];
    200             return;
    201         }
    202     }
    203 
    204     qerror_abort(qerr, "error format '%s' not found", fmt);
    205 }
    206 
    207 /**
    208  * qerror_from_info(): Create a new QError from error information
    209  *
    210  * The information consists of:
    211  *
    212  * - file   the file name of where the error occurred
    213  * - linenr the line number of where the error occurred
    214  * - func   the function name of where the error occurred
    215  * - fmt    JSON printf-like dictionary, there must exist keys 'class' and
    216  *          'data'
    217  * - va     va_list of all arguments specified by fmt
    218  *
    219  * Return strong reference.
    220  */
    221 QError *qerror_from_info(const char *file, int linenr, const char *func,
    222                          const char *fmt, va_list *va)
    223 {
    224     QError *qerr;
    225 
    226     qerr = qerror_new();
    227     qerr->linenr = linenr;
    228     qerr->file = file;
    229     qerr->func = func;
    230 
    231     if (!fmt) {
    232         qerror_abort(qerr, "QDict not specified");
    233     }
    234 
    235     qerror_set_data(qerr, fmt, va);
    236     qerror_set_desc(qerr, fmt);
    237 
    238     return qerr;
    239 }
    240 
    241 static void parse_error(const QError *qerror, int c)
    242 {
    243     qerror_abort(qerror, "expected '%c' in '%s'", c, qerror->entry->desc);
    244 }
    245 
    246 static const char *append_field(QString *outstr, const QError *qerror,
    247                                 const char *start)
    248 {
    249     QObject *obj;
    250     QDict *qdict;
    251     QString *key_qs;
    252     const char *end, *key;
    253 
    254     if (*start != '%')
    255         parse_error(qerror, '%');
    256     start++;
    257     if (*start != '(')
    258         parse_error(qerror, '(');
    259     start++;
    260 
    261     end = strchr(start, ')');
    262     if (!end)
    263         parse_error(qerror, ')');
    264 
    265     key_qs = qstring_from_substr(start, 0, end - start - 1);
    266     key = qstring_get_str(key_qs);
    267 
    268     qdict = qobject_to_qdict(qdict_get(qerror->error, "data"));
    269     obj = qdict_get(qdict, key);
    270     if (!obj) {
    271         qerror_abort(qerror, "key '%s' not found in QDict", key);
    272     }
    273 
    274     switch (qobject_type(obj)) {
    275         case QTYPE_QSTRING:
    276             qstring_append(outstr, qdict_get_str(qdict, key));
    277             break;
    278         case QTYPE_QINT:
    279             qstring_append_int(outstr, qdict_get_int(qdict, key));
    280             break;
    281         default:
    282             qerror_abort(qerror, "invalid type '%c'", qobject_type(obj));
    283     }
    284 
    285     QDECREF(key_qs);
    286     return ++end;
    287 }
    288 
    289 /**
    290  * qerror_human(): Format QError data into human-readable string.
    291  *
    292  * Formats according to member 'desc' of the specified QError object.
    293  */
    294 QString *qerror_human(const QError *qerror)
    295 {
    296     const char *p;
    297     QString *qstring;
    298 
    299     assert(qerror->entry != NULL);
    300 
    301     qstring = qstring_new();
    302 
    303     for (p = qerror->entry->desc; *p != '\0';) {
    304         if (*p != '%') {
    305             qstring_append_chr(qstring, *p++);
    306         } else if (*(p + 1) == '%') {
    307             qstring_append_chr(qstring, '%');
    308             p += 2;
    309         } else {
    310             p = append_field(qstring, qerror, p);
    311         }
    312     }
    313 
    314     return qstring;
    315 }
    316 
    317 /**
    318  * qerror_print(): Print QError data
    319  *
    320  * This function will print the member 'desc' of the specified QError object,
    321  * it uses qemu_error() for this, so that the output is routed to the right
    322  * place (ie. stderr or Monitor's device).
    323  */
    324 void qerror_print(const QError *qerror)
    325 {
    326     QString *qstring = qerror_human(qerror);
    327     qemu_error("%s\n", qstring_get_str(qstring));
    328     QDECREF(qstring);
    329 }
    330 
    331 /**
    332  * qobject_to_qerror(): Convert a QObject into a QError
    333  */
    334 QError *qobject_to_qerror(const QObject *obj)
    335 {
    336     if (qobject_type(obj) != QTYPE_QERROR) {
    337         return NULL;
    338     }
    339 
    340     return container_of(obj, QError, base);
    341 }
    342 
    343 /**
    344  * qerror_destroy_obj(): Free all memory allocated by a QError
    345  */
    346 static void qerror_destroy_obj(QObject *obj)
    347 {
    348     QError *qerr;
    349 
    350     assert(obj != NULL);
    351     qerr = qobject_to_qerror(obj);
    352 
    353     QDECREF(qerr->error);
    354     qemu_free(qerr);
    355 }
    356