summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpiaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2009-06-16 20:22:07 +0800
committerpiaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2009-06-16 20:22:07 +0800
commitec0614310d3794a283e5fc86144d8b78a9cb3779 (patch)
tree5b0098dceb69dd0c4ef232d6e6118a92f288a635
parente5ee21752e651dd5e658632e5d1869b360d573d2 (diff)
downloadpttbbs-ec0614310d3794a283e5fc86144d8b78a9cb3779.tar
pttbbs-ec0614310d3794a283e5fc86144d8b78a9cb3779.tar.gz
pttbbs-ec0614310d3794a283e5fc86144d8b78a9cb3779.tar.bz2
pttbbs-ec0614310d3794a283e5fc86144d8b78a9cb3779.tar.lz
pttbbs-ec0614310d3794a283e5fc86144d8b78a9cb3779.tar.xz
pttbbs-ec0614310d3794a283e5fc86144d8b78a9cb3779.tar.zst
pttbbs-ec0614310d3794a283e5fc86144d8b78a9cb3779.zip
* add asynchronous ack mode
* refine options git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@4635 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
-rw-r--r--daemon/logind/logind.c170
1 files changed, 153 insertions, 17 deletions
diff --git a/daemon/logind/logind.c b/daemon/logind/logind.c
index 9ae607a9..5f31806e 100644
--- a/daemon/logind/logind.c
+++ b/daemon/logind/logind.c
@@ -14,7 +14,8 @@
// 3. [done] regular check text screen files instead of HUP?
// 4. [done] re-start mbbsd if pipe broken?
// 5. [drop] clean mbbsd pid log files?
-// 6. handle non-block i/o
+// 6. [done] handle non-block i/o
+// 7. [done] asynchronous tunnel handshake
#include <stdio.h>
#include <ctype.h>
@@ -53,6 +54,10 @@
#define OVERLOAD_SLEEP_SEC (60)
#endif
+#ifndef ACK_TIMEOUT_SEC
+#define ACK_TIMEOUT_SEC (5*60)
+#endif
+
#ifndef BAN_SLEEP_SEC
#define BAN_SLEEP_SEC (60)
#endif
@@ -87,7 +92,8 @@ int g_overload = 0;
int g_banned = 0;
int g_verbose = 0;
int g_opened_fd= 0;
-int g_nonblock = 1; // default on
+int g_nonblock = 1;
+int g_async_ack= 1;
// retry service
char g_retry_cmd[PATHLEN];
@@ -1053,16 +1059,25 @@ retry_service()
system(g_retry_cmd);
}
+static int
+login_conn_end_ack(login_conn_ctx *conn, void *ack, int fd);
+
static int
-start_service(int fd, login_ctx *ctx)
+start_service(int fd, login_conn_ctx *conn)
{
login_data ld = {0};
int ack = 0;
+ login_ctx *ctx;
if (!g_tunnel)
return 0;
- ld.cb = sizeof(ld);
+ assert(conn);
+ ctx = &conn->ctx;
+
+ ld.cb = sizeof(ld);
+ ld.ack = (void*)conn;
+
strlcpy(ld.userid, ctx->userid, sizeof(ld.userid));
strlcpy(ld.hostip, ctx->hostip, sizeof(ld.hostip));
strlcpy(ld.port, ctx->port, sizeof(ld.port));
@@ -1100,14 +1115,14 @@ start_service(int fd, login_ctx *ctx)
return ack;
}
- // wait service to response
- if (toread(g_tunnel, &ack, sizeof(ack)) <= 0)
+ // wait (or async) service to response
+ if (!login_conn_end_ack(conn, ld.ack, fd))
{
if (g_verbose) fprintf(stderr, LOG_PREFIX
"failed in toread\r\n");
return ack;
}
- return ack;
+ return 1;
}
static int
@@ -1144,7 +1159,7 @@ auth_start(int fd, login_conn_ctx *conn)
draw_auth_success(conn, isfree);
- if (!start_service(fd, ctx))
+ if (!start_service(fd, conn))
{
// too bad, we can't start service.
retry_service();
@@ -1190,7 +1205,7 @@ auth_start(int fd, login_conn_ctx *conn)
///////////////////////////////////////////////////////////////////////
// Event callbacks
-static struct event ev_sighup, ev_tunnel;
+static struct event ev_sighup, ev_tunnel, ev_ack;
static void
sighup_cb(int signal, short event, void *arg)
@@ -1208,6 +1223,7 @@ endconn_cb(int fd, short event, void *arg)
if (g_verbose) fprintf(stderr, LOG_PREFIX
"login_conn_remove: removed connection (%s@%s) #%d...",
conn->ctx.userid, conn->ctx.hostip, fd);
+
event_del(&conn->ev);
bufferevent_free(conn->bufev);
close(fd);
@@ -1242,6 +1258,87 @@ login_conn_remove(login_conn_ctx *conn, int fd, int sleep_sec)
}
}
+static void *
+get_tunnel_ack(int tunnel, int event)
+{
+ void *arg = NULL;
+
+ if (!(event & EV_READ) ||
+ toread(tunnel, &arg, sizeof(arg)) < sizeof(arg) ||
+ !arg)
+ {
+ // sorry... broken, let's shutdown the tunnel.
+ if (g_verbose)
+ fprintf(stderr, LOG_PREFIX "get_tunnel_ack: tunnel (%d) is broken.\r\n", tunnel);
+
+ close(tunnel);
+ return arg;
+ }
+
+ return arg;
+
+}
+
+static void
+ack_cb(int tunnel, short event, void *arg)
+{
+ login_conn_ctx *conn = NULL;
+ assert(sizeof(arg) == sizeof(conn));
+
+ if (g_verbose) fprintf(stderr, LOG_PREFIX "ack_cb is invoked: tunnel=%d\r\n", tunnel);
+ arg = get_tunnel_ack(tunnel, event);
+ if (!arg)
+ return;
+
+ // XXX success connection.
+ conn = (login_conn_ctx*) arg;
+ login_conn_remove(conn, conn->telnet.fd, 0);
+}
+
+
+static int
+login_conn_end_ack(login_conn_ctx *conn, void *ack, int fd)
+{
+ // fprintf(stderr, LOG_PREFIX "login_conn_end_ack: enter.\r\n");
+
+ if (g_async_ack)
+ {
+ // simply wait for ack_cb to complete
+ // fprintf(stderr, LOG_PREFIX "login_conn_end_ack: async mode.\r\n");
+
+ // set a safe timeout
+ login_conn_remove(conn, fd, ACK_TIMEOUT_SEC);
+
+ } else {
+ // wait service to complete
+ void *rack = NULL;
+
+ // fprintf(stderr, LOG_PREFIX "login_conn_end_ack: sync mode.\r\n");
+ if (!g_tunnel)
+ return 0;
+
+ rack = get_tunnel_ack(g_tunnel, EV_READ);
+ if (!rack)
+ return 0;
+
+ if (rack != ack)
+ {
+ // critical error!
+ fprintf(stderr, LOG_PREFIX
+ "login_conn_end_ack: failed in ack value (%08lX != %08lX).\r\n",
+ (unsigned long)rack, (unsigned long)ack);
+
+ // XXX TODO close tunnel?
+ close(g_tunnel);
+ return 0;
+ }
+
+ // safe to close.
+ login_conn_remove(conn, fd, 0);
+ }
+ return 1;
+}
+
static void
client_cb(int fd, short event, void *arg)
{
@@ -1364,8 +1461,10 @@ client_cb(int fd, short event, void *arg)
case LOGIN_HANDLE_START_AUTH:
if ((r = auth_start(fd, conn)) != AUTH_RESULT_RETRY)
{
- login_conn_remove(conn, fd,
- (r == AUTH_RESULT_OK) ? 0 : AUTHFAIL_SLEEP_SEC);
+ // for AUTH_RESULT_OK, the connection is handled in
+ // login_conn_end_ack.
+ if (r != AUTH_RESULT_OK)
+ login_conn_remove(conn, fd, AUTHFAIL_SLEEP_SEC);
return;
}
break;
@@ -1469,12 +1568,21 @@ tunnel_cb(int fd, short event, void *arg)
if ((cfd = accept(fd, NULL, NULL)) < 0 )
return;
+ // got new tunnel
+ fprintf(stderr, LOG_PREFIX "new tunnel established.\r\n");
_set_connection_opt(cfd);
- // got new tunnel
if (g_tunnel)
+ {
close(g_tunnel);
+ event_del(&ev_ack);
+ }
g_tunnel = cfd;
+ if (g_async_ack)
+ {
+ event_set(&ev_ack, g_tunnel, EV_READ | EV_PERSIST, ack_cb, NULL);
+ event_add(&ev_ack, NULL);
+ }
}
///////////////////////////////////////////////////////////////////////
@@ -1610,45 +1718,73 @@ main(int argc, char *argv[])
Signal(SIGPIPE, SIG_IGN);
- while ( (ch = getopt(argc, argv, "f:p:t:l:r:hdDvb")) != -1 )
+ while ( (ch = getopt(argc, argv, "f:p:t:l:r:hdDvba")) != -1 )
{
switch( ch ){
case 'f':
config_file = optarg;
break;
+
case 'l':
log_file = optarg;
break;
+
case 'p':
if (optarg) port = atoi(optarg);
break;
+
case 't':
strlcpy(tunnel_path, optarg, sizeof(tunnel_path));
break;
+
case 'r':
strlcpy(g_retry_cmd, optarg, sizeof(g_retry_cmd));
break;
+
case 'd':
as_daemon = 1;
break;
+
case 'D':
as_daemon = 0;
break;
+
+ case 'a':
+ g_async_ack = 1;
+ break;
+
+ case 'A':
+ g_async_ack = 0;
+ break;
+
case 'b':
+ g_nonblock = 1;
+ break;
+
+ case 'B':
g_nonblock = 0;
+ break;
+
case 'v':
g_verbose++;
break;
+
case 'h':
default:
fprintf(stderr,
- "usage: %s [-bvdD] [-l log_file] [-f conf] [-p port] [-t tunnel] [-c client_command]\r\n", argv[0]);
+ "usage: %s [-aAbBvdD] [-l log_file] [-f conf] [-p port] [-t tunnel] [-c client_command]\r\n", argv[0]);
fprintf(stderr,
"\t-v: provide verbose messages\r\n"
- "\t-d: enter daemon mode\r\n"
- "\t-D: do not enter daemon mode\r\n"
- "\t-b: use blocked socket mode (disable non-blocking)\r\n"
+ "\t-d: enter daemon mode%s\r\n"
+ "\t-D: do not enter daemon mode%s\r\n"
+ "\t-a: use asynchronous service ack%s\r\n"
+ "\t-A: do not use async service ack%s\r\n"
+ "\t-b: use non-blocking socket mode%s\r\n"
+ "\t-B: use blocking socket mode%s\r\n"
"\t-f: read configuration from file (default: %s)\r\n",
+ as_daemon ? " (default)" : "", !as_daemon ? " (default)" : "",
+ g_async_ack ? " (default)" : "", !g_async_ack ? " (default)" : "",
+ g_nonblock ? " (default)" : "", !g_nonblock ? " (default)" : "",
BBSHOME "/" FN_CONF_BINDPORTS);
fprintf(stderr,
"\t-l: log meesages into log_file\r\n"