1
0
mirror of https://github.com/haiwen/seafile-server.git synced 2025-06-26 15:11:34 +00:00
seafile-server/server/listen-mgr.c
2016-08-19 13:54:16 +08:00

276 lines
7.4 KiB
C

#include "common.h"
#include <event2/event.h>
#include <event2/listener.h>
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <event2/bufferevent.h>
#include <event2/buffer_compat.h>
#include <event2/bufferevent_struct.h>
#endif
#include "seafile-session.h"
#include "utils.h"
#include "net.h"
#include "listen-mgr.h"
#include "block-tx-server.h"
#define DEBUG_FLAG SEAFILE_DEBUG_OTHER
#include "log.h"
#define DEFAULT_SERVER_PORT 12001
#define TOKEN_LEN 37 /* a uuid */
#define CHECK_EXPIRE_INTERVAL 1
#define READ_TOKEN_TIMEOUT 180 /* bufferevent read timeout */
struct _SeafListenManagerPriv {
GHashTable *token_hash;
struct evconnlistener *listener;
CcnetTimer *check_timer;
};
typedef struct {
int ttl;
ConnAcceptedCB func;
void *user_data;
} CallBackStruct;
static void accept_connection (struct evconnlistener *listener,
evutil_socket_t connfd,
struct sockaddr *saddr,
int socklen,
void *vmanager);
static int token_expire_pulse (void * vmanager);
static void read_cb (struct bufferevent *bufev, void *user_data);
static void error_cb (struct bufferevent *bufev, short what, void *user_data);
static int
get_listen_port (SeafileSession *session)
{
char *port_str;
int port = 0;
port_str = g_key_file_get_string (session->config, "network", "port", NULL);
if (port_str) {
port = atoi(port_str);
if (port <= 0 || port > 65535)
port = DEFAULT_SERVER_PORT;
g_free(port_str);
}
return port;
}
SeafListenManager *
seaf_listen_manager_new (SeafileSession *session)
{
SeafListenManager *mgr;
mgr = g_new0 (SeafListenManager, 1);
mgr->port = get_listen_port(session);
mgr->priv = g_new0 (SeafListenManagerPriv, 1);
mgr->priv->token_hash = g_hash_table_new_full (
g_str_hash, g_str_equal, g_free, g_free);
return mgr;
}
int
seaf_listen_manager_start (SeafListenManager *mgr)
{
evutil_socket_t listenfd;
unsigned flags;
SeafListenManagerPriv *priv = mgr->priv;
if (mgr->port == 0) {
return 0;
}
listenfd = ccnet_net_bind_tcp (mgr->port, 1);
if (listenfd < 0) {
seaf_warning ("[listen mgr] failed to bind port %d\n", mgr->port);
return -1;
}
flags = LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_EXEC;
/* start to listen on block transfer port */
priv->listener = evconnlistener_new (NULL, /* base */
accept_connection, mgr, /* cb & arg */
flags, /* flags */
-1, /* backlog */
listenfd); /* socket */
if (!priv->listener) {
seaf_warning ("[listen mgr] failed to start evlistener\n");
evutil_closesocket (listenfd);
return -1;
}
priv->check_timer = ccnet_timer_new (token_expire_pulse, mgr,
CHECK_EXPIRE_INTERVAL * 1000);
seaf_message ("listen on port %d for block tranfer\n", mgr->port);
return 0;
}
static void
accept_connection (struct evconnlistener *listener,
evutil_socket_t connfd,
struct sockaddr *saddr, int socklen,
void *vmanager)
{
struct bufferevent *bufev;
struct timeval tv;
tv.tv_sec = READ_TOKEN_TIMEOUT;
tv.tv_usec = 0;
bufev = bufferevent_socket_new (NULL, connfd, 0);
bufferevent_setcb (bufev, read_cb, NULL, error_cb, vmanager);
bufferevent_setwatermark (bufev, EV_READ, TOKEN_LEN, TOKEN_LEN);
bufferevent_set_timeouts (bufev, &tv, NULL);
bufferevent_enable (bufev, EV_READ);
/* no write is needed here*/
bufferevent_disable (bufev, EV_WRITE);
}
static void
read_cb (struct bufferevent *bufev, void *user_data)
{
char *token;
CallBackStruct *cbstruct;
SeafListenManager *mgr = user_data;
size_t len = EVBUFFER_LENGTH(bufev->input);
evutil_socket_t connfd = bufferevent_getfd(bufev);
/* we set the high & low watermark to TOKEN_LEN, so the received data can
* only be this length. */
if (len != TOKEN_LEN) {
seaf_warning ("[listen mgr] token with incorrect length received: %d\n",
(int)len);
goto error;
}
token = (char *)(EVBUFFER_DATA (bufev->input));
/* Switch to new block protocol */
if (strcmp (token, BLOCK_PROTOCOL_SIGNATURE) == 0) {
if (ccnet_net_make_socket_blocking (connfd) < 0) {
seaf_warning ("[listen mgr] Failed to set socket blocking.\n");
goto error;
}
if (block_tx_server_start (connfd) < 0) {
seaf_warning ("Failed to start block tx server.\n");
goto error;
}
bufferevent_free (bufev);
return;
}
cbstruct = g_hash_table_lookup (mgr->priv->token_hash, token);
if (!cbstruct) {
seaf_warning ("[listen mgr] unknown token received: %s\n", token);
goto error;
}
/* The connfd should be non-blocking for adding to bufferevent.
* But now we want it to be blocking again.
*/
if (ccnet_net_make_socket_blocking (connfd) < 0) {
seaf_warning ("[listen mgr] Failed to set socket blocking.\n");
goto error;
}
/* client is now connected, execute the callback function */
cbstruct->func (connfd, cbstruct->user_data);
g_hash_table_remove (mgr->priv->token_hash, token);
bufferevent_free (bufev);
return;
error:
evutil_closesocket(connfd);
bufferevent_free (bufev);
}
static void
error_cb (struct bufferevent *bufev, short what, void *user_data)
{
if (what & BEV_EVENT_TIMEOUT)
seaf_warning ("[listen mgr] client timeout\n");
else
seaf_warning ("[listen mgr] error when reading token\n");
/* We don't specify BEV_OPT_CLOSE_ON_FREE, so we need to close the socket
* manually. */
evutil_closesocket(bufferevent_getfd(bufev));
bufferevent_free (bufev);
}
int
seaf_listen_manager_register_token (SeafListenManager *mgr,
const char *token,
ConnAcceptedCB cb,
void *cb_arg,
int timeout_sec)
{
CallBackStruct *cbstruct;
if (!token)
return -1;
if (timeout_sec <= 0)
return -1;
cbstruct = g_new0(CallBackStruct, 1);
cbstruct->func = cb;
cbstruct->user_data = cb_arg;
cbstruct->ttl = timeout_sec;
g_hash_table_insert (mgr->priv->token_hash, g_strdup(token), cbstruct);
return 0;
}
char *
seaf_listen_manager_generate_token (SeafListenManager *mgr)
{
return gen_uuid();
}
static gboolean
is_token_expired (gpointer key, gpointer value, gpointer user_data)
{
CallBackStruct *cbstruct = value;
if (cbstruct->ttl == 0) {
/* client doesn't connect before timeout, so token is expired */
seaf_warning ("[listen mgr] token timeout\n");
cbstruct->func (-1, cbstruct->user_data);
return TRUE;
}
--cbstruct->ttl;
return FALSE;
}
static int
token_expire_pulse (void * vmanager)
{
SeafListenManager *mgr = vmanager;
g_hash_table_foreach_remove (mgr->priv->token_hash,
(GHRFunc)is_token_expired,
NULL);
return TRUE;
}