terom@3: #ifndef ERROR_H terom@3: #define ERROR_H terom@3: terom@34: /** terom@34: * @file terom@34: * terom@3: * Error-handling functions terom@3: */ terom@3: #include terom@3: terom@34: /** terom@34: * The type used for error codes is an explicitly *unsigned* int, meaning that error codes themselves are positive. terom@34: * Negative error codes (as signed ints) 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@30: /** terom@30: * Ways to interpret error_info.extra terom@7: */ terom@7: enum error_extra_types { terom@34: /** No extra info */ terom@30: ERR_EXTRA_NONE = 0, terom@7: terom@30: /** libc errno, using strerror() */ terom@30: ERR_EXTRA_ERRNO, terom@30: terom@30: /** libc resolver, using gai_strerror() */ terom@30: ERR_EXTRA_GAI, terom@30: terom@34: /** GnuTLS, using gnutls_strerror() */ terom@30: ERR_EXTRA_GNUTLS, terom@56: terom@56: /** Static error message string */ terom@56: ERR_EXTRA_STR, terom@7: }; terom@7: terom@30: /** terom@3: * List of defined error codes, organized mostly by function name terom@3: */ terom@3: enum error_code { terom@30: _ERR_INVALID = 0x000000, terom@3: terom@30: /** stdlib.h functions */ terom@30: _ERR_STDLIB = 0x000100, terom@30: ERR_CALLOC, terom@37: ERR_STRDUP, terom@71: ERR_SIGACTION, terom@108: ERR_ACCESS_READ, terom@30: terom@30: /** DNS resolver */ terom@30: _ERR_RESOLVER = 0x000200, terom@30: ERR_GETADDRINFO, terom@30: ERR_GETADDRINFO_EMPTY, terom@30: terom@155: /** socket/IO errors */ terom@30: _ERR_SOCK = 0x000300, terom@155: ERR_SOCKET, ///< socket(2) failed terom@155: ERR_CONNECT, ///< connect(2) error - either direct or async terom@155: ERR_READ, ///< read(2) error - will probably show up as an ERR_WRITE as well terom@155: ERR_WRITE, ///< write(2) error - data was unsent, will probably show up as an ERR_READ as well terom@155: ERR_WRITE_EOF, ///< write(2) gave EOF - zero bytes written terom@155: ERR_FCNTL, ///< fcntl(2) failed terom@155: ERR_CLOSE, ///< close(2) failed, some written data was probably not sent terom@155: ERR_GETSOCKOPT, ///< getsockopt(2) failed terom@155: ERR_OPEN, ///< open(2) failed terom@4: terom@56: /** @see sock_gnutls_error_code */ terom@30: _ERR_GNUTLS = 0x000400, terom@10: terom@30: /** Libevent errors */ terom@30: _ERR_LIBEVENT = 0x000500, terom@30: ERR_EVENT_NEW, terom@30: ERR_EVENT_ADD, terom@156: ERR_EVENT_DEL, terom@17: terom@30: /** Evsql errors */ terom@30: _ERR_EVSQL = 0x000600, terom@30: ERR_EVSQL_NEW_PQ, terom@68: ERR_EVSQL_QUERY_EXEC, terom@23: terom@37: /** irc_proto errors */ terom@30: _ERR_IRC_LINE = 0x000700, terom@30: ERR_LINE_TOO_LONG, terom@30: ERR_LINE_INVALID_TOKEN, terom@37: ERR_INVALID_NM, terom@37: ERR_INVALID_NICK_LENGTH, terom@27: terom@27: /** irc_conn errors */ terom@30: _ERR_IRC_CONN = 0x000800, terom@30: ERR_IRC_CONN_REGISTER_STATE, terom@48: ERR_IRC_CONN_QUIT_STATE, terom@48: terom@48: /** irc_net errors */ terom@48: _ERR_IRC_NET = 0x000900, terom@98: ERR_IRC_NET_INFO, terom@97: ERR_IRC_NET_STATE, terom@55: terom@56: /** @see module_error_code */ terom@55: _ERR_MODULE = 0x000a00, terom@63: terom@83: /** config errors */ terom@83: _ERR_CONFIG = 0x000b00, terom@120: ERR_CONFIG_NAME, ///< unknown option name terom@120: ERR_CONFIG_TYPE, ///< invalid value type for parameter terom@120: ERR_CONFIG_REQUIRED, ///< missing value for required parameter terom@120: ERR_CONFIG_VALUE, ///< invalid value terom@120: ERR_CONFIG_PARAMS, ///< invalid number of parameters terom@93: terom@93: /** lua errors */ terom@93: _ERR_LUA = 0x000c00, terom@93: ERR_LUA_MEM, terom@93: ERR_LUA_SYNTAX, terom@98: ERR_LUA_RUN, terom@98: ERR_LUA_ERR, terom@106: ERR_LUA_FILE, terom@83: terom@97: /** irc_chan errors */ terom@97: _ERR_IRC_CHAN = 0x000d00, terom@97: ERR_IRC_CHAN_STATE, terom@97: terom@121: /** pcre errors */ terom@121: _ERR_PCRE = 0x000e00, terom@121: ERR_PCRE_COMPILE, ///< pcre_compile: terom@127: ERR_PCRE_EXEC, ///< pcre_exec: terom@121: terom@129: /** str errors */ terom@129: _ERR_STR = 0x000f00, terom@129: terom@155: /** Transport errors */ terom@155: _ERR_TRANSPORT = 0x001000, terom@155: terom@63: /** General errors */ terom@63: _ERR_GENERAL = 0xffff00, terom@156: ERR_MISC, ///< general error terom@156: ERR_CMD_OPT, ///< invalid commandline option terom@98: ERR_UNKNOWN, terom@156: ERR_DUP_NAME, ///< duplicate name terom@156: ERR_EOF, ///< end of file terom@164: ERR_MEM, ///< memory allocation error terom@164: ERR_NOT_IMPLEMENTED, ///< function not implemented terom@30: }; terom@30: terom@30: /** terom@30: * Table of error descriptions terom@30: */ terom@30: struct error_desc { terom@30: /** The flat error code */ terom@30: err_t code; terom@30: terom@30: /** The short name */ terom@30: const char *name; terom@30: terom@30: /** How to interpret .extra */ terom@30: enum error_extra_types extra_type; terom@3: }; terom@3: terom@34: /** terom@3: * An error code and associated extra infos terom@3: */ terom@3: struct error_info { terom@34: /** The base error code */ terom@3: err_t code; terom@56: terom@56: union { terom@56: /** Additional detail info, usually some third-party error code, as defined by the code's ERR_EXTRA_* */ terom@56: int extra; terom@3: terom@56: /** Additional info, stored as a pointer to a static string (note how dangerous this is) */ terom@56: const char *extra_str; terom@56: }; terom@3: }; terom@3: terom@34: /** terom@154: * The public names terom@154: */ terom@154: typedef struct error_info error_t; terom@154: terom@154: /** terom@7: * Translate an err_t into a function name. terom@6: */ terom@6: const char *error_name (err_t code); terom@6: terom@34: /** 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@34: /** 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@34: /** Evaulates to error_info.code as lvalue */ terom@4: #define ERROR_CODE(err_info_ptr) ((err_info_ptr)->code) terom@3: terom@34: /** Evaulates to error_info.extra as lvalue */ terom@4: #define ERROR_EXTRA(err_info_ptr) ((err_info_ptr)->extra) terom@3: terom@34: /** 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@34: /** Compare error_info.code != 0 */ terom@4: #define IS_ERROR(err_info_ptr) (!!(err_info_ptr)->code) terom@3: terom@34: /** 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@34: /** 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@34: /** 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@34: /** 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@56: /** terom@56: * Set error_info.code to err_code, and .extra_str to str. The given string pointer should remain valid while the error terom@56: * is being handled down-stack. terom@56: */ terom@56: #define _SET_ERROR_STR(err_info_ptr, err_code, err_str) (err_info_ptr)->code = (err_code); (err_info_ptr)->extra_str = (err_str) terom@56: #define SET_ERROR_STR(err_info_ptr, err_code, err_str) do { _SET_ERROR_STR(err_info_ptr, err_code, err_str); } while(0) terom@56: terom@34: /** 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@34: /** 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@56: #define RETURN_SET_ERROR_STR(err_info_ptr, err_code, err_str) do { _SET_ERROR_STR(err_info_ptr, err_code, err_str); return (err_code); } while (0) terom@3: terom@34: /** 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@87: #define JUMP_SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra); goto error; } while (0) terom@87: #define JUMP_SET_ERROR_ERRNO(err_info_ptr, err_code) do { _SET_ERROR_ERRNO(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@57: #define JUMP_SET_ERROR_STR(err_info_ptr, err_code, err_str) do { _SET_ERROR_STR(err_info_ptr, err_code, err_str); goto error; } while (0) terom@5: terom@100: /** terom@100: * Macro used to mark code segments that should never be executed (e.g. switch-default), kind of like assert terom@100: */ terom@143: #include terom@143: #define NOT_REACHED() abort() terom@100: terom@3: #endif