API to replace all calls to exit() with a user-supplied fatal-error handler.

Also, add unit tests for logging.

svn:r1462
This commit is contained in:
Nick Mathewson 2009-10-26 19:59:51 +00:00
parent 38aec9ec7c
commit a8267663de
4 changed files with 155 additions and 3 deletions

View File

@ -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:

View File

@ -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.

23
log.c
View File

@ -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

View File

@ -46,9 +46,11 @@
#include <stdlib.h>
#include <string.h>
#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,
};