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