]> granicus.if.org Git - libevent/commitdiff
API to replace all calls to exit() with a user-supplied fatal-error handler.
authorNick Mathewson <nickm@torproject.org>
Mon, 26 Oct 2009 19:59:51 +0000 (19:59 +0000)
committerNick Mathewson <nickm@torproject.org>
Mon, 26 Oct 2009 19:59:51 +0000 (19:59 +0000)
Also, add unit tests for logging.

svn:r1462

ChangeLog
include/event2/event.h
log.c
test/regress_util.c

index 6ad1a1d66eed2e425f876822ddc86a85b327d91d..6c775ae234688569117f219ec01f712e5078dfe8 100644 (file)
--- 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:
index 1e7026d2bfb27cce298da72535a28b367fe36ab8..27ca6ba881ae906db0d976301f6eca0a8904e5ae 100644 (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.
 
diff --git a/log.c b/log.c
index db6209908e229b964b5a2d7a7453e6ce7f4594fd..292ec2f87f3ddc227be2cf9571168c533aebe2c9 100644 (file)
--- 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
index 999326fbb5784fdd5e410e0fdc213ddfb1abee20..4593a4328daa4a2597cc8214268202d437409b43 100644 (file)
 #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,
 };