terom@3: #ifndef ERROR_H terom@3: #define ERROR_H terom@3: terom@3: /* terom@3: * Error-handling functions terom@3: */ terom@3: #include terom@3: terom@3: /* terom@3: * Type used for error codes is an explicitly *unsigned* int, meaning that error codes themselves are positive. terom@3: * Negative error codes also exist in some places, and they are just a negative err_t. terom@3: */ terom@3: typedef unsigned int err_t; terom@3: terom@3: /* terom@7: * Bitmask of error_info.extra meanings terom@7: */ terom@7: enum error_extra_types { terom@7: // bit offset of ERR_EXTRA_* mask in error_code terom@7: _ERR_EXTRA_OFFSET = 3 * 8, terom@7: terom@7: // mask of bits used for the error_extra_types value terom@7: _ERR_EXTRA_MASK = 0xff << _ERR_EXTRA_OFFSET, terom@7: terom@7: ERR_EXTRA_NONE = 0x00 << _ERR_EXTRA_OFFSET, terom@7: ERR_EXTRA_ERRNO = 0x01 << _ERR_EXTRA_OFFSET, terom@7: ERR_EXTRA_GAI = 0x02 << _ERR_EXTRA_OFFSET, terom@7: ERR_EXTRA_GNUTLS = 0x03 << _ERR_EXTRA_OFFSET, terom@7: terom@7: }; terom@7: terom@7: terom@7: #define _ERROR_CODE(name, code, extra) name = (code | ERR_EXTRA_ ## extra) terom@7: /* terom@3: * List of defined error codes, organized mostly by function name terom@3: */ terom@3: enum error_code { terom@4: /* Core functions */ terom@7: _ERROR_CODE( ERR_CALLOC, 0x000100, NONE ), terom@3: terom@4: /* Network resolver errors */ terom@7: _ERROR_CODE( ERR_GETADDRINFO, 0x000200, GAI ), terom@7: _ERROR_CODE( ERR_GETADDRINFO_EMPTY, 0x000201, GAI ), terom@3: terom@4: /* Low-level network errors */ terom@7: _ERROR_CODE( ERR_SOCKET, 0x000301, ERRNO ), terom@7: _ERROR_CODE( ERR_CONNECT, 0x000302, ERRNO ), terom@3: terom@4: /* Low-level IO errors */ terom@7: _ERROR_CODE( ERR_READ, 0x000401, ERRNO ), terom@8: _ERROR_CODE( ERR_READ_EOF, 0x000402, NONE ), terom@8: _ERROR_CODE( ERR_WRITE, 0x000403, ERRNO ), terom@12: _ERROR_CODE( ERR_WRITE_EOF, 0x000404, NONE ), terom@12: _ERROR_CODE( ERR_FCNTL, 0x000405, ERRNO ), terom@4: terom@4: /* GnuTLS errors */ terom@7: _ERROR_CODE( ERR_GNUTLS_CERT_ALLOC_CRED, 0x010101, GNUTLS ), terom@7: _ERROR_CODE( ERR_GNUTLS_GLOBAL_INIT, 0x010102, GNUTLS ), terom@7: _ERROR_CODE( ERR_GNUTLS_INIT, 0x010103, GNUTLS ), terom@7: _ERROR_CODE( ERR_GNUTLS_SET_DEFAULT_PRIORITY, 0x010104, GNUTLS ), terom@7: _ERROR_CODE( ERR_GNUTLS_CRED_SET, 0x010105, GNUTLS ), terom@7: _ERROR_CODE( ERR_GNUTLS_HANDSHAKE, 0x010106, GNUTLS ), terom@10: _ERROR_CODE( ERR_GNUTLS_RECORD_SEND, 0x010107, GNUTLS ), terom@10: _ERROR_CODE( ERR_GNUTLS_RECORD_RECV, 0x010108, GNUTLS ), terom@12: _ERROR_CODE( ERR_GNUTLS_RECORD_GET_DIRECTION, 0x010109, GNUTLS ), terom@10: terom@10: /* Libevent errors */ terom@10: _ERROR_CODE( ERR_EVENT_NEW, 0x010201, NONE ), terom@11: _ERROR_CODE( ERR_EVENT_ADD, 0x010202, NONE ), terom@7: terom@7: // mask of bits used for the error_code value terom@7: _ERROR_CODE_MASK = 0xffffff, terom@3: }; terom@3: terom@3: /* terom@3: * An error code and associated extra infos terom@3: */ terom@3: struct error_info { terom@3: /* The base error code */ terom@3: err_t code; terom@3: terom@3: /* Additional detail info, usually some third-part error code */ terom@3: unsigned int extra; terom@3: }; terom@3: terom@6: /* terom@7: * Translate an err_t into a function name. terom@6: */ terom@6: const char *error_name (err_t code); terom@6: terom@7: /* terom@7: * Maximum length of error messages returned by error_msg (including NUL byte) terom@7: */ terom@7: #define ERROR_MSG_MAXLEN 1024 terom@7: terom@7: /* terom@7: * Translate an error_info into a message. terom@7: * terom@7: * This is returned as a pointer into a statically allocated buffer. It is not re-entrant. terom@7: */ terom@8: const char *error_msg (const struct error_info *err); terom@7: terom@3: /** No error, evaulates as logical false */ terom@3: #define SUCCESS (0) terom@3: terom@3: /* Evaulates to error_info.code as lvalue */ terom@4: #define ERROR_CODE(err_info_ptr) ((err_info_ptr)->code) terom@3: terom@3: /* Evaulates to error_info.extra as lvalue */ terom@4: #define ERROR_EXTRA(err_info_ptr) ((err_info_ptr)->extra) terom@3: terom@3: /* Set error_info.code to SUCCESS, evaulates as zero */ terom@4: #define RESET_ERROR(err_info_ptr) ((err_info_ptr)->code = SUCCESS) terom@3: terom@3: /* Compare error_info.code != 0 */ terom@4: #define IS_ERROR(err_info_ptr) (!!(err_info_ptr)->code) terom@3: terom@10: /* Compare the err_code/err_extra for an err_info */ terom@10: #define MATCH_ERROR(err_info_ptr, err_code, err_extra) ((err_info_ptr)->code == (err_code) && (err_info_ptr)->extra == (err_extra)) terom@10: terom@3: /* Set error_info.code, but leave err_extra as-is. Evaluates to err_code */ terom@4: #define SET_ERROR(err_info_ptr, err_code) ((err_info_ptr)->code = (err_code)) terom@3: terom@3: /* Set error_info.code/extra. XXX: should evaluate to err_code */ terom@4: #define _SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra) (err_info_ptr)->code = (err_code); (err_info_ptr)->extra = (err_extra) terom@4: #define SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra); } while (0) terom@3: terom@5: /* Set error_info.code to err_code, and .extra to errno. XXX: should evaulate to err_code */ terom@4: #define _SET_ERROR_ERRNO(err_info_ptr, err_code) _SET_ERROR_EXTRA(err_info_ptr, err_code, errno); terom@4: #define SET_ERROR_ERRNO(err_info_ptr, err_code) SET_ERROR_EXTRA(err_info_ptr, err_code, errno); terom@3: terom@5: /* Set error_info from another error_info. Evaluates to the new error_info */ terom@5: #define SET_ERROR_INFO(err_info_ptr, from_ptr) (*err_info_ptr = *from_ptr) terom@5: terom@5: /* Same as above, but also return err_code from func. XXX: use 'return SET_ERROR...' instead */ terom@5: #define RETURN_SET_ERROR(err_info_ptr, err_code) do { SET_ERROR(err_info_ptr, err_code); return (err_code); } while (0) terom@4: #define RETURN_SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra); return (err_code); } while (0) terom@4: #define RETURN_SET_ERROR_ERRNO(err_info_ptr, err_code) do { _SET_ERROR_ERRNO(err_info_ptr, err_code); return (err_code); } while (0) terom@8: #define RETURN_SET_ERROR_INFO(err_info_ptr, from_ptr) do { SET_ERROR_INFO(err_info_ptr, from_ptr); return (from_ptr->code); } while (0) terom@3: terom@5: /* Same as above, but also do a 'goto error' */ terom@5: #define JUMP_SET_ERROR(err_info_ptr, err_code) do { SET_ERROR(err_info_ptr, err_code); goto error; } while (0) terom@5: #define JUMP_SET_ERROR_INFO(err_info_ptr, from_ptr) do { SET_ERROR_INFO(err_info_ptr, from_ptr); goto error; } while (0) terom@5: terom@3: #endif