# HG changeset patch # User Tero Marttila # Date 1235277856 -7200 # Node ID cc94ae754e2af4b54c07b09214b8e5a3c1f6571d # Parent a834f055993996369155a71ba47215f2c731ac60 error handling magic diff -r a834f0559939 -r cc94ae754e2a src/error.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/error.h Sun Feb 22 06:44:16 2009 +0200 @@ -0,0 +1,76 @@ +#ifndef ERROR_H +#define ERROR_H + +/* + * Error-handling functions + */ +#include + +/* + * Type used for error codes is an explicitly *unsigned* int, meaning that error codes themselves are positive. + * Negative error codes also exist in some places, and they are just a negative err_t. + */ +typedef unsigned int err_t; + +/* + * List of defined error codes, organized mostly by function name + */ +enum error_code { + ERR_CALLOC = 0x000100, + + // network resolver errors + ERR_GETADDRINFO = 0x000200, + ERR_GETADDRINFO_EMPTY = 0x000201, /* No valid results */ + + // low-level network errors + ERR_SOCKET = 0x000301, + ERR_CONNECT = 0x000302, + + // low-level IO errors + ERR_READ = 0x000401, + ERR_WRITE = 0x000402, +}; + +/* + * An error code and associated extra infos + */ +struct error_info { + /* The base error code */ + err_t code; + + /* Additional detail info, usually some third-part error code */ + unsigned int extra; +}; + +/** No error, evaulates as logical false */ +#define SUCCESS (0) + +/* Evaulates to error_info.code as lvalue */ +#define ERROR_CODE(err_info) ((err_info).code) + +/* Evaulates to error_info.extra as lvalue */ +#define ERROR_EXTRA(err_info) ((err_info).extra) + +/* Set error_info.code to SUCCESS, evaulates as zero */ +#define RESET_ERROR(err_info) ((err_info).code = SUCCESS) + +/* Compare error_info.code != 0 */ +#define IS_ERROR(err_info) (!!(err_info).code) + +/* Set error_info.code, but leave err_extra as-is. Evaluates to err_code */ +#define SET_ERROR(err_info, err_code) ((err_info).code = (err_code)) + +/* Set error_info.code/extra. XXX: should evaluate to err_code */ +#define _SET_ERROR_EXTRA(err_info, err_code, err_extra) (err_info).code = (err_code); err_info.extra = (err_extra) +#define SET_ERROR_EXTRA(err_info, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info, err_code, err_extra); } while (0) + +/* Set error.info.code to err_code, and .extra to errno. XXX: should evaulate to err_code */ +#define _SET_ERROR_ERRNO(err_info, err_code) _SET_ERROR_EXTRA(err_info, err_code, errno); +#define SET_ERROR_ERRNO(err_info, err_code) SET_ERROR_EXTRA(err_info, err_code, errno); + +/* Ss above, but also return err_code from func. XXX: use 'return SET_ERROR...' instead */ +#define RETURN_SET_ERROR(err_info, err_code) do { _SET_ERROR(err_info, err_code); return (err_code); } while (0) +#define RETURN_SET_ERROR_EXTRA(err_info, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info, err_code, err_extra); return (err_code); } while (0) +#define RETURN_SET_ERROR_ERRNO(err_info, err_code) do { _SET_ERROR_ERRNO(err_info, err_code); return (err_code); } while (0) + +#endif diff -r a834f0559939 -r cc94ae754e2a src/sock.c --- a/src/sock.c Sun Feb 22 05:27:29 2009 +0200 +++ b/src/sock.c Sun Feb 22 06:44:16 2009 +0200 @@ -2,21 +2,41 @@ #include "sock_internal.h" #include "sock_gnutls.h" -void sock_init (void) +#include + +err_t sock_init (void) { - // XXX: just call directly for now - sock_gnutls_init(); + err_t err; + + // XXX: just call these all directly for now + + if ((err = sock_gnutls_init())) + return err; } -int sock_stream_read (struct sock_stream *sock, void *buf, size_t len) +void sock_stream_init (struct sock_stream *sock, struct sock_stream_type *type) +{ + // be strict + assert(sock->type == NULL); + + // store type + sock->type = type; +} + +err_t sock_stream_read (struct sock_stream *sock, void *buf, size_t len) { // proxy off to method handler return sock->type->methods.read(sock, buf, len); } -int sock_stream_write (struct sock_stream *sock, const void *buf, size_t len) +err_t sock_stream_write (struct sock_stream *sock, const void *buf, size_t len) { // proxy off to method handler return sock->type->methods.write(sock, buf, len); } +void sock_stream_error (struct sock_stream *sock, struct error_info *err) +{ + // copy from SOCK_ER + *err = SOCK_ERR(sock); +} diff -r a834f0559939 -r cc94ae754e2a src/sock.h --- a/src/sock.h Sun Feb 22 05:27:29 2009 +0200 +++ b/src/sock.h Sun Feb 22 06:44:16 2009 +0200 @@ -4,6 +4,7 @@ /* * Low-level socket-related functions */ +#include "error.h" #include /* @@ -12,14 +13,19 @@ struct sock_stream; /* - * A simple blocking TCP connect to the given host/service, using getaddrinfo. + * Initialize the socket module's global state. Call this before calling any other sock_* functions. + */ +err_t sock_init (void); + +/* + * A simple blocking TCP connect to the given host/service, using getaddrinfo. The connected socket is returned via + * *sock_ptr. In case of errors, additional error information is stored in *err + * + * @return zero on success, nonzero on error * * XXX: blocking - * XXX: exits on error - * - * Returns the socket handle. */ -struct sock_stream *sock_tcp_connect (const char *host, const char *service); +err_t sock_tcp_connect (struct sock_stream **sock_ptr, const char *host, const char *service, struct error_info *err); /* * A simple blocking SSL connect to the given host/service. @@ -28,19 +34,19 @@ * XXX: doesn't do any certificate verification. * XXX: exits on error * - * Returns the socket handle. + * Returns the socket handle, or NULL on errors. */ struct sock_stream *sock_ssl_connect (const char *host, const char *service); /* - * Initialize the socket module's global state - */ -void sock_init (void); - -/* * The generic read/write API for stream sockets. */ -int sock_stream_read (struct sock_stream *sock, void *buf, size_t len); -int sock_stream_write (struct sock_stream *sock, const void *buf, size_t len); +err_t sock_stream_read (struct sock_stream *sock, void *buf, size_t len); +err_t sock_stream_write (struct sock_stream *sock, const void *buf, size_t len); + +/** + * Get last err_info for \a sock, returned via \a *err. + */ +void sock_stream_error (struct sock_stream *sock, struct error_info *err); #endif diff -r a834f0559939 -r cc94ae754e2a src/sock_gnutls.c --- a/src/sock_gnutls.c Sun Feb 22 05:27:29 2009 +0200 +++ b/src/sock_gnutls.c Sun Feb 22 06:44:16 2009 +0200 @@ -12,7 +12,7 @@ errx(1, "%s: %s", func, gnutls_strerror(_err)); } -static int sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t len) +static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t len) { struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); @@ -20,7 +20,7 @@ return gnutls_record_recv(sock->session, buf, len); } -static int sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t len) +static err_t sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t len) { struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); @@ -51,8 +51,7 @@ gnutls_certificate_allocate_credentials(&ctx->xcred); } -// XXX: errors -void sock_gnutls_init (void) +err_t sock_gnutls_init (void) { int _err; @@ -62,6 +61,9 @@ // init _sock_gnutls_ctx sock_gnutls_client_ctx_anon(&_sock_gnutls_client_ctx); + + // done + return SUCCESS; } diff -r a834f0559939 -r cc94ae754e2a src/sock_gnutls.h --- a/src/sock_gnutls.h Sun Feb 22 05:27:29 2009 +0200 +++ b/src/sock_gnutls.h Sun Feb 22 06:44:16 2009 +0200 @@ -35,10 +35,11 @@ #define SOCK_GNUTLS_BASE(sock_ptr) (&(sock_ptr)->base_tcp.base) #define SOCK_GNUTLS_TCP(sock_ptr) (&(sock_ptr)->base_tcp) +#define SOCK_GNUTLS_ERR(sock_ptr) SOCK_ERR(SOCK_GNUTLS_BASE(sock_ptr)) /* * Initialize the global gnutls state */ -void sock_gnutls_init (void); +err_t sock_gnutls_init (void); #endif /* SOCK_GNUTLS_H */ diff -r a834f0559939 -r cc94ae754e2a src/sock_internal.h --- a/src/sock_internal.h Sun Feb 22 05:27:29 2009 +0200 +++ b/src/sock_internal.h Sun Feb 22 06:44:16 2009 +0200 @@ -10,10 +10,10 @@ /* method table */ struct sock_stream_methods { /* Normal read(2) */ - int (*read) (struct sock_stream *sock, void *buf, size_t len); + err_t (*read) (struct sock_stream *sock, void *buf, size_t len); /* Normal write(2) */ - int (*write) (struct sock_stream *sock, const void *buf, size_t len); + err_t (*write) (struct sock_stream *sock, const void *buf, size_t len); } methods; }; @@ -25,9 +25,21 @@ * as appropriate. */ struct sock_stream { + /* The sock_stream_type for this socket */ struct sock_stream_type *type; + + /* Last error info */ + struct error_info err; }; #define SOCK_FROM_BASE(sock, type) ((type*) sock) +#define SOCK_ERR(sock) ((sock)->err) + +/* + * Initialize a sock_stream with the given sock_stream_type. + * + * The sock_stream should be initialized to zero. It is a bug to call this twice. + */ +void sock_stream_init (struct sock_stream *sock, struct sock_stream_type *type); #endif /* SOCK_INTERNAL_H */ diff -r a834f0559939 -r cc94ae754e2a src/sock_tcp.c --- a/src/sock_tcp.c Sun Feb 22 05:27:29 2009 +0200 +++ b/src/sock_tcp.c Sun Feb 22 06:44:16 2009 +0200 @@ -7,66 +7,82 @@ #include #include #include -#include +#include /* - * Our sock_stream_type.methods.read implementation + * Our sock_stream_methods.read method */ -static int sock_tcp_read (struct sock_stream *base_sock, void *buf, size_t len) +static err_t sock_tcp_read (struct sock_stream *base_sock, void *buf, size_t len) { struct sock_tcp *sock = SOCK_FROM_BASE(base_sock, struct sock_tcp); + int ret; + + // map directly to read(2) + if ((ret = read(sock->fd, buf, len)) < 0) + // errno + RETURN_SET_ERROR_ERRNO(SOCK_TCP_ERR(sock), ERR_READ); - return read(sock->fd, buf, len); + else + // bytes read + return ret; } /* - * Our sock_stream_type.methods.write implementation + * Our sock_stream_methods.write method */ -static int sock_tcp_write (struct sock_stream *base_sock, const void *buf, size_t len) +static err_t sock_tcp_write (struct sock_stream *base_sock, const void *buf, size_t len) { struct sock_tcp *sock = SOCK_FROM_BASE(base_sock, struct sock_tcp); + int ret; + + // map directly to write(2) + if ((ret = write(sock->fd, buf, len)) < 0) + // errno + RETURN_SET_ERROR_ERRNO(SOCK_TCP_ERR(sock), ERR_WRITE); - return write(sock->fd, buf, len); + else + // bytes read + return ret; } /* * Our sock_stream_type - * - * XXX: move to sock_tcp.h */ struct sock_stream_type sock_tcp_type = { .methods.read = &sock_tcp_read, .methods.write = &sock_tcp_write, }; -struct sock_tcp* sock_tcp_alloc (void) +err_t sock_tcp_alloc (struct sock_tcp **sock_ptr) { - struct sock_tcp *sock; - // alloc - if ((sock = calloc(1, sizeof(*sock))) == NULL) - errx(1, "calloc"); + if ((*sock_ptr = calloc(1, sizeof(**sock_ptr))) == NULL) + return ERR_CALLOC; - // initialize base - sock->base.type = &sock_tcp_type; + // initialize base with sock_tcp_type + sock_stream_init(SOCK_TCP_BASE(*sock_ptr), &sock_tcp_type); // done - return sock; + return SUCCESS; } -int sock_tcp_init_fd (struct sock_tcp *sock, int fd) +err_t sock_tcp_init_fd (struct sock_tcp *sock, int fd) { + // valid fd -XXX: err instead? + assert(fd >= 0); + // initialize sock->fd = fd; // done - return 0; + return SUCCESS; } -int sock_tcp_init_connect (struct sock_tcp *sock, const char *hostname, const char *service) +err_t sock_tcp_init_connect (struct sock_tcp *sock, const char *hostname, const char *service) { struct addrinfo hints, *res, *r; - int _err; + int err; + RESET_ERROR(SOCK_TCP_ERR(sock)); // hints memset(&hints, 0, sizeof(hints)); @@ -74,40 +90,85 @@ hints.ai_socktype = SOCK_STREAM; // resolve - if ((_err = getaddrinfo(hostname, service, &hints, &res))) - errx(1, "getaddrinfo: %s", gai_strerror(_err)); + if ((err = getaddrinfo(hostname, service, &hints, &res))) + RETURN_SET_ERROR_EXTRA(SOCK_TCP_ERR(sock), ERR_GETADDRINFO, err); - // use + // try each result in turn for (r = res; r; r = r->ai_next) { - // XXX: wrong - if ((sock->fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) < 0) - err(1, "socket"); + // create the socket + if ((sock->fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) < 0) { + // remember error + SET_ERROR_ERRNO(SOCK_TCP_ERR(sock), ERR_SOCKET); - if (connect(sock->fd, r->ai_addr, r->ai_addrlen)) - err(1, "connect"); + // skip to next one + continue; + } + + // connect to remote address + if (connect(sock->fd, r->ai_addr, r->ai_addrlen)) { + // remember error + SET_ERROR_ERRNO(SOCK_TCP_ERR(sock), ERR_CONNECT); + + // close/invalidate socket + close(sock->fd); + sock->fd = -1; + // skip to next one + continue; + } + + // valid socket, use this break; } - // ensure we got some valid socket - if (sock->fd < 0) - errx(1, "no valid socket"); + // ensure we got some valid socket, else return last error code + if (sock->fd < 0) { + // did we hit some error? + if (IS_ERROR(SOCK_TCP_ERR(sock))) + // return last error + return ERROR_CODE(SOCK_TCP_ERR(sock)); + + else + // no results + return SET_ERROR(SOCK_TCP_ERR(sock), ERR_GETADDRINFO_EMPTY); + } // ok, done return 0; } -// XXX: error handling -struct sock_stream* sock_tcp_connect (const char *host, const char *service) +err_t sock_tcp_connect (struct sock_stream **sock_ptr, const char *host, const char *service, struct error_info *err_info) { struct sock_tcp *sock; + err_t err; // allocate - sock = sock_tcp_alloc(); + if ((err = sock_tcp_alloc(&sock))) + return err; // connect - sock_tcp_init_connect(sock, host, service); + if ((err = sock_tcp_init_connect(sock, host, service))) { + // set *err_info + *err_info = SOCK_TCP_ERR(sock); - // done - return SOCK_TCP_BASE(sock); + // cleanup + sock_tcp_release(sock); + + // return error code + return err; + } + + // good + *sock_ptr = SOCK_TCP_BASE(sock); + + return 0; } + +void sock_tcp_release (struct sock_tcp *sock) +{ + // must not be connected + assert(sock->fd < 0); + + // free + free(sock); +} diff -r a834f0559939 -r cc94ae754e2a src/sock_tcp.h --- a/src/sock_tcp.h Sun Feb 22 05:27:29 2009 +0200 +++ b/src/sock_tcp.h Sun Feb 22 06:44:16 2009 +0200 @@ -18,20 +18,26 @@ }; #define SOCK_TCP_BASE(sock_ptr) (&(sock_ptr)->base) +#define SOCK_TCP_ERR(sock_ptr) SOCK_ERR(SOCK_TCP_BASE(sock_ptr)) /* * Allocate a new blank sock_tcp with a correctly initialized base */ -struct sock_tcp* sock_tcp_alloc (void); +err_t sock_tcp_alloc (struct sock_tcp **sock_ptr); /* * Initialize a blank sock_tcp with a given already-existing fd */ -int sock_tcp_init_fd (struct sock_tcp *sock, int fd); +err_t sock_tcp_init_fd (struct sock_tcp *sock, int fd); /* * Initialize a blank sock_tcp by connecting */ -int sock_tcp_init_connect (struct sock_tcp *sock, const char *hostname, const char *service); +err_t sock_tcp_init_connect (struct sock_tcp *sock, const char *hostname, const char *service); + +/* + * Release a non-connected sock_tcp + */ +void sock_tcp_release (struct sock_tcp *sock); #endif /* SOCK_TCP_H */