From: Nick Mathewson Date: Mon, 26 Oct 2009 19:59:51 +0000 (+0000) Subject: API to replace all calls to exit() with a user-supplied fatal-error handler. X-Git-Tag: release-2.0.3-alpha~82 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a8267663de2feb27051682cb51f9596f59613e5f;p=libevent API to replace all calls to exit() with a user-supplied fatal-error handler. Also, add unit tests for logging. svn:r1462 --- diff --git a/ChangeLog b/ChangeLog index 6ad1a1d6..6c775ae2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -32,6 +32,7 @@ Changes in 2.0.3-alpha: o Fix a bug when using a specialized memory allocator on win32. o Have the win32 select() backend label TCP-socket-connected events as EV_WRITE, not EV_READ. This should bring it in line with the other backends, and improve portability. Patch from Christopher Davis. o Stop using enums as arguments or return values when what we mean is a bitfield of enum values. C++ doesn't believe that you can OR two enum values together and get another enum, and C++ takes its typing seriously. Patch from Christopher Davis. + o Add an API to replace all fatal calls to exit() with a user-provided panic function. Changes in 2.0.2-alpha: diff --git a/include/event2/event.h b/include/event2/event.h index 1e7026d2..27ca6ba8 100644 --- a/include/event2/event.h +++ b/include/event2/event.h @@ -240,6 +240,21 @@ typedef void (*event_log_cb)(int severity, const char *msg); */ void event_set_log_callback(event_log_cb cb); +/** + Override Libevent's behavior in the event of a fatal internal error. + + By default, Libevent will call exit(1) if a programming error makes it + impossible to continue correct operation. This function allows you to supply + another callback instead. Note that if the function is ever invoked, + something is wrong with your program, or with Libevent: any subsequent calls + to Libevent may result in undefined behavior. + + Libevent will (almost) always log an _EVENT_LOG_ERR message before calling + this function; look at the last log message to see why Libevent has died. + */ +typedef void (*event_fatal_cb)(int err); +void event_set_fatal_callback(event_fatal_cb cb); + /** Associate a different event base with an event. diff --git a/log.c b/log.c index db620990..292ec2f8 100644 --- a/log.c +++ b/log.c @@ -67,6 +67,23 @@ static void _warn_helper(int severity, const char *errstr, const char *fmt, va_list ap); static void event_log(int severity, const char *msg); +static event_fatal_cb fatal_fn = NULL; + +void +event_set_fatal_callback(event_fatal_cb cb) +{ + fatal_fn = cb; +} + +static void +event_exit(int errcode) +{ + if (fatal_fn) + fatal_fn(errcode); + else + exit(errcode); +} + void event_err(int eval, const char *fmt, ...) { @@ -75,7 +92,7 @@ event_err(int eval, const char *fmt, ...) va_start(ap, fmt); _warn_helper(_EVENT_LOG_ERR, strerror(errno), fmt, ap); va_end(ap); - exit(eval); + event_exit(eval); } void @@ -97,7 +114,7 @@ event_sock_err(int eval, evutil_socket_t sock, const char *fmt, ...) va_start(ap, fmt); _warn_helper(_EVENT_LOG_ERR, evutil_socket_error_to_string(err), fmt, ap); va_end(ap); - exit(eval); + event_exit(eval); } void @@ -119,7 +136,7 @@ event_errx(int eval, const char *fmt, ...) va_start(ap, fmt); _warn_helper(_EVENT_LOG_ERR, NULL, fmt, ap); va_end(ap); - exit(eval); + event_exit(eval); } void diff --git a/test/regress_util.c b/test/regress_util.c index 999326fb..4593a432 100644 --- a/test/regress_util.c +++ b/test/regress_util.c @@ -46,9 +46,11 @@ #include #include +#include "event2/event.h" #include "event2/util.h" #include "../ipv6-internal.h" #include "../util-internal.h" +#include "../log-internal.h" #include "regress.h" @@ -325,6 +327,122 @@ end: ; } +static int logsev = 0; +static char *logmsg = NULL; + +static void +logfn(int severity, const char *msg) +{ + logsev = severity; + tt_want(msg); + if (msg) + logmsg = strdup(msg); +} + +static int exited = 0; +static int exitcode = 0; +static void +fatalfn(int c) +{ + exited = 1; + exitcode = c; +} + +static void +test_evutil_log(void *ptr) +{ + evutil_socket_t fd = -1; + char buf[128]; + + event_set_log_callback(logfn); + event_set_fatal_callback(fatalfn); +#define RESET() do { \ + logsev = exited = exitcode = 0; \ + if (logmsg) free(logmsg); \ + logmsg = NULL; \ + } while (0) +#define LOGEQ(sev,msg) do { \ + tt_int_op(logsev,==,sev); \ + tt_assert(logmsg != NULL); \ + tt_str_op(logmsg,==,msg); \ + } while (0) + + event_errx(2, "Fatal error; too many kumquats (%d)", 5); + LOGEQ(_EVENT_LOG_ERR, "Fatal error; too many kumquats (5)"); + tt_int_op(exitcode,==,2); + RESET(); + + event_warnx("Far too many %s (%d)", "wombats", 99); + LOGEQ(_EVENT_LOG_WARN, "Far too many wombats (99)"); + tt_int_op(exited,==,0); + RESET(); + + event_msgx("Connecting lime to coconut"); + LOGEQ(_EVENT_LOG_MSG, "Connecting lime to coconut"); + tt_int_op(exited,==,0); + RESET(); + + event_debug("A millisecond passed! We should log that!"); +#ifdef USE_DEBUG + LOGEQ(_EVENT_LOG_DEBUG, "A millisecond passed! We should log that!"); +#else + tt_int_op(logsev,==,0); + tt_ptr_op(logmsg,==,NULL); +#endif + RESET(); + + /* Try with an errno. */ + errno = ENOENT; + event_warn("Couldn't open %s", "/bad/file"); + evutil_snprintf(buf, sizeof(buf), + "Couldn't open /bad/file: %s",strerror(ENOENT)); + LOGEQ(_EVENT_LOG_WARN,buf); + tt_int_op(exited, ==, 0); + RESET(); + + errno = ENOENT; + event_err(5,"Couldn't open %s", "/very/bad/file"); + evutil_snprintf(buf, sizeof(buf), + "Couldn't open /very/bad/file: %s",strerror(ENOENT)); + LOGEQ(_EVENT_LOG_ERR,buf); + tt_int_op(exitcode, ==, 5); + RESET(); + + /* Try with a socket errno. */ + fd = socket(AF_INET, SOCK_STREAM, 0); +#ifdef WIN32 + evutil_snprintf(buf, sizeof(buf), + "Unhappy socket: Resource temporarily unavailable"); + EVUTIL_SET_SOCKET_ERROR(fd, WSAEWOULDBLOCK); +#else + evutil_snprintf(buf, sizeof(buf), + "Unhappy socket: %s", strerror(EAGAIN)); + errno = EAGAIN; +#endif + event_sock_warn(fd, "Unhappy socket"); + LOGEQ(_EVENT_LOG_WARN, buf); + tt_int_op(exited,==,0); + RESET(); + +#ifdef WIN32 + EVUTIL_SET_SOCKET_ERROR(fd, WSAEWOULDBLOCK); +#else + errno = EAGAIN; +#endif + event_sock_err(200, fd, "Unhappy socket"); + LOGEQ(_EVENT_LOG_ERR, buf); + tt_int_op(exitcode,==,200); + RESET(); + +#undef RESET +#undef LOGEQ +end: + if (logmsg) + free(logmsg); + if (fd >= 0) + EVUTIL_CLOSESOCKET(fd); +} + struct testcase_t util_testcases[] = { { "ipv4_parse", regress_ipv4_parse, 0, NULL, NULL }, { "ipv6_parse", regress_ipv6_parse, 0, NULL, NULL }, @@ -332,6 +450,7 @@ struct testcase_t util_testcases[] = { { "evutil_snprintf", test_evutil_snprintf, 0, NULL, NULL }, { "evutil_strtoll", test_evutil_strtoll, 0, NULL, NULL }, { "evutil_casecmp", test_evutil_casecmp, 0, NULL, NULL }, + { "log", test_evutil_log, TT_FORK, NULL, NULL }, END_OF_TESTCASES, };