summaryrefslogtreecommitdiffstats
path: root/sbin/hastd
diff options
context:
space:
mode:
authorpjd <pjd@FreeBSD.org>2010-04-29 21:55:20 +0000
committerpjd <pjd@FreeBSD.org>2010-04-29 21:55:20 +0000
commit1f717ed2d0f18a2bfb45acde8630100453f6b8a3 (patch)
treee2629e559f7cef7e6482332d02cdfdd40c59cc5d /sbin/hastd
parentb520866979ef8cadf97e3db819b45046bc782c19 (diff)
downloadFreeBSD-src-1f717ed2d0f18a2bfb45acde8630100453f6b8a3.zip
FreeBSD-src-1f717ed2d0f18a2bfb45acde8630100453f6b8a3.tar.gz
Default connection timeout is way too long. To make it shorter we have to
make socket non-blocking, connect() and if we get EINPROGRESS, we have to wait using select(). Very complex, but I know no other way to define connection timeout for a given socket. Reported by: hiroshi@soupacific.com MFC after: 3 days
Diffstat (limited to 'sbin/hastd')
-rw-r--r--sbin/hastd/proto_tcp4.c78
1 files changed, 75 insertions, 3 deletions
diff --git a/sbin/hastd/proto_tcp4.c b/sbin/hastd/proto_tcp4.c
index 2fba996..5af82d5 100644
--- a/sbin/hastd/proto_tcp4.c
+++ b/sbin/hastd/proto_tcp4.c
@@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
#include <assert.h>
#include <errno.h>
+#include <fcntl.h>
#include <netdb.h>
#include <stdbool.h>
#include <stdint.h>
@@ -47,6 +48,7 @@ __FBSDID("$FreeBSD$");
#include "hast.h"
#include "pjdlog.h"
#include "proto_impl.h"
+#include "subr.h"
#define TCP4_CTX_MAGIC 0x7c441c
struct tcp4_ctx {
@@ -222,18 +224,88 @@ static int
tcp4_connect(void *ctx)
{
struct tcp4_ctx *tctx = ctx;
+ struct timeval tv;
+ fd_set fdset;
+ socklen_t esize;
+ int error, flags, ret;
assert(tctx != NULL);
assert(tctx->tc_magic == TCP4_CTX_MAGIC);
assert(tctx->tc_side == TCP4_SIDE_CLIENT);
assert(tctx->tc_fd >= 0);
- if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
- sizeof(tctx->tc_sin)) < 0) {
+ flags = fcntl(tctx->tc_fd, F_GETFL);
+ if (flags == -1) {
+ KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
+ "fcntl(F_GETFL) failed"));
+ return (errno);
+ }
+ /*
+ * We make socket non-blocking so we have decided about connection
+ * timeout.
+ */
+ flags |= O_NONBLOCK;
+ if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
+ KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
+ "fcntl(F_SETFL, O_NONBLOCK) failed"));
return (errno);
}
- return (0);
+ if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
+ sizeof(tctx->tc_sin)) == 0) {
+ error = 0;
+ goto done;
+ }
+ if (errno != EINPROGRESS) {
+ error = errno;
+ pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
+ goto done;
+ }
+ /*
+ * Connection can't be established immediately, let's wait
+ * for HAST_TIMEOUT seconds.
+ */
+ tv.tv_sec = HAST_TIMEOUT;
+ tv.tv_usec = 0;
+again:
+ FD_ZERO(&fdset);
+ FD_SET(tctx->tc_fd, &fdset);
+ ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
+ if (ret == 0) {
+ error = ETIMEDOUT;
+ goto done;
+ } else if (ret == -1) {
+ if (errno == EINTR)
+ goto again;
+ error = errno;
+ pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
+ goto done;
+ }
+ assert(ret > 0);
+ assert(FD_ISSET(tctx->tc_fd, &fdset));
+ esize = sizeof(error);
+ if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
+ &esize) == -1) {
+ error = errno;
+ pjdlog_common(LOG_DEBUG, 1, errno,
+ "getsockopt(SO_ERROR) failed");
+ goto done;
+ }
+ if (error != 0) {
+ pjdlog_common(LOG_DEBUG, 1, error,
+ "getsockopt(SO_ERROR) returned error");
+ goto done;
+ }
+ error = 0;
+done:
+ flags &= ~O_NONBLOCK;
+ if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
+ if (error == 0)
+ error = errno;
+ pjdlog_common(LOG_DEBUG, 1, errno,
+ "fcntl(F_SETFL, ~O_NONBLOCK) failed");
+ }
+ return (error);
}
static int
OpenPOWER on IntegriCloud