mirror of
https://github.com/haiwen/seafile-server.git
synced 2025-06-26 23:16:51 +00:00
703 lines
19 KiB
C
703 lines
19 KiB
C
#include "common.h"
|
|
#define DEBUG_FLAG SEAFILE_DEBUG_TRANSFER
|
|
#include "log.h"
|
|
|
|
#include "net.h"
|
|
|
|
#ifndef WIN32
|
|
#include <sys/select.h>
|
|
#endif
|
|
#include <event2/buffer.h>
|
|
#include <event2/util.h>
|
|
|
|
#include <ccnet.h>
|
|
#include <ccnet/job-mgr.h>
|
|
|
|
#include "seafile-session.h"
|
|
#include "block-tx-server.h"
|
|
#include "block-tx-utils.h"
|
|
#include "utils.h"
|
|
|
|
enum {
|
|
RECV_STATE_HANDSHAKE = 0,
|
|
RECV_STATE_AUTH,
|
|
RECV_STATE_HEADER,
|
|
RECV_STATE_CONTENT,
|
|
};
|
|
|
|
struct _BlockTxServer {
|
|
evutil_socket_t data_fd;
|
|
struct evbuffer *recv_buf;
|
|
|
|
int recv_state;
|
|
int session_key_len;
|
|
int command;
|
|
char curr_block_id[41];
|
|
|
|
/* Used by put block */
|
|
BlockHandle *block;
|
|
|
|
unsigned char key[ENC_KEY_SIZE];
|
|
unsigned char iv[ENC_BLOCK_SIZE];
|
|
|
|
unsigned char key_v2[ENC_KEY_SIZE];
|
|
unsigned char iv_v2[ENC_BLOCK_SIZE];
|
|
|
|
FrameParser parser;
|
|
|
|
gboolean break_loop;
|
|
|
|
int version;
|
|
|
|
char store_id[37];
|
|
int repo_version;
|
|
};
|
|
|
|
typedef struct _BlockTxServer BlockTxServer;
|
|
|
|
/* Handshake */
|
|
|
|
static int
|
|
send_handshake_response (BlockTxServer *server, int status)
|
|
{
|
|
HandshakeResponse rsp;
|
|
|
|
rsp.status = htonl (status);
|
|
rsp.version = htonl (BLOCK_PROTOCOL_VERSION);
|
|
|
|
if (sendn (server->data_fd, &rsp, sizeof(rsp)) < 0) {
|
|
seaf_warning ("Failed to send handshake response: %s.\n",
|
|
evutil_socket_error_to_string(evutil_socket_geterror(server->data_fd)));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
init_frame_parser (BlockTxServer *server)
|
|
{
|
|
FrameParser *parser = &server->parser;
|
|
|
|
if (server->version == 1) {
|
|
memcpy (parser->key, server->key, ENC_BLOCK_SIZE);
|
|
memcpy (parser->iv, server->iv, ENC_BLOCK_SIZE);
|
|
} else if (server->version == 2) {
|
|
memcpy (parser->key_v2, server->key_v2, ENC_KEY_SIZE);
|
|
memcpy (parser->iv_v2, server->iv_v2, ENC_BLOCK_SIZE);
|
|
}
|
|
|
|
parser->version = server->version;
|
|
parser->cbarg = server;
|
|
}
|
|
|
|
static int
|
|
process_session_key (BlockTxServer *server, unsigned char *enc_session_key)
|
|
{
|
|
char *enc_key_b64 = NULL, *key_b64 = NULL;
|
|
unsigned char *session_key = NULL;
|
|
gsize len;
|
|
SearpcClient *client = NULL;
|
|
int ret = 0;
|
|
|
|
client = ccnet_create_pooled_rpc_client (seaf->client_pool,
|
|
NULL,
|
|
"ccnet-rpcserver");
|
|
if (!client) {
|
|
seaf_warning ("Failed to create rpc client.\n");
|
|
send_handshake_response (server, STATUS_INTERNAL_SERVER_ERROR);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
enc_key_b64 = g_base64_encode (enc_session_key, server->session_key_len);
|
|
|
|
key_b64 = ccnet_privkey_decrypt (client, enc_key_b64);
|
|
if (!key_b64) {
|
|
seaf_warning ("Failed to decrypt session key.\n");
|
|
send_handshake_response (server, STATUS_INTERNAL_SERVER_ERROR);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
session_key = g_base64_decode (key_b64, &len);
|
|
|
|
if (server->version == 1)
|
|
blocktx_generate_encrypt_key (session_key, len, server->key, server->iv);
|
|
else if (server->version == 2)
|
|
blocktx_generate_encrypt_key (session_key, len, server->key_v2, server->iv_v2);
|
|
|
|
init_frame_parser (server);
|
|
|
|
out:
|
|
g_free (enc_key_b64);
|
|
g_free (key_b64);
|
|
g_free (session_key);
|
|
ccnet_rpc_client_free (client);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
handle_auth_req_content_cb (char *content, int clen, void *cbarg);
|
|
|
|
#define MAX_SESSION_KEY_SIZE 1024
|
|
|
|
static int
|
|
handle_handshake_request (BlockTxServer *server)
|
|
{
|
|
HandshakeRequest req;
|
|
struct evbuffer *input = server->recv_buf;
|
|
unsigned char *enc_session_key;
|
|
|
|
if (!server->session_key_len) {
|
|
if (evbuffer_get_length (input) < sizeof(req))
|
|
return 0;
|
|
|
|
evbuffer_remove (input, &req, sizeof(req));
|
|
|
|
req.version = ntohl (req.version);
|
|
server->version = MIN (req.version, BLOCK_PROTOCOL_VERSION);
|
|
if (server->version != 1 && server->version != 2) {
|
|
seaf_warning ("Bad block protocol version %d.\n", server->version);
|
|
send_handshake_response (server, STATUS_VERSION_MISMATCH);
|
|
return -1;
|
|
}
|
|
|
|
seaf_debug ("Block protocol version %d.\n", server->version);
|
|
|
|
server->session_key_len = ntohl (req.key_len);
|
|
if (server->session_key_len > MAX_SESSION_KEY_SIZE) {
|
|
seaf_warning ("Encrypted session key is too long: %d.\n",
|
|
server->session_key_len);
|
|
send_handshake_response (server, STATUS_BAD_REQUEST);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (evbuffer_get_length (input) < server->session_key_len)
|
|
return 0;
|
|
|
|
enc_session_key = g_malloc (server->session_key_len);
|
|
|
|
evbuffer_remove (input, enc_session_key, server->session_key_len);
|
|
|
|
if (process_session_key (server, enc_session_key) < 0) {
|
|
g_free (enc_session_key);
|
|
return -1;
|
|
}
|
|
g_free (enc_session_key);
|
|
|
|
if (send_handshake_response (server, STATUS_OK) < 0)
|
|
return -1;
|
|
|
|
seaf_debug ("recv_state set to AUTH.\n");
|
|
|
|
server->parser.content_cb = handle_auth_req_content_cb;
|
|
server->recv_state = RECV_STATE_AUTH;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Authentication */
|
|
|
|
static int
|
|
send_auth_response (BlockTxServer *server, int status)
|
|
{
|
|
AuthResponse rsp;
|
|
EVP_CIPHER_CTX *ctx;
|
|
int ret = 0;
|
|
|
|
rsp.status = htonl (status);
|
|
|
|
if (server->version == 1)
|
|
blocktx_encrypt_init (&ctx, server->key, server->iv);
|
|
else if (server->version == 2)
|
|
blocktx_encrypt_init (&ctx, server->key_v2, server->iv_v2);
|
|
|
|
if (send_encrypted_data_frame_begin (server->data_fd, sizeof(rsp)) < 0) {
|
|
seaf_warning ("Send auth response: failed to begin.\n");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (send_encrypted_data (ctx, server->data_fd, &rsp, sizeof(rsp)) < 0)
|
|
{
|
|
seaf_warning ("Send auth response: failed to send data.\n");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (send_encrypted_data_frame_end (ctx, server->data_fd) < 0) {
|
|
seaf_warning ("Send auth response: failed to end.\n");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
EVP_CIPHER_CTX_free (ctx);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
handle_block_header_content_cb (char *content, int clen, void *cbarg);
|
|
|
|
static int
|
|
handle_auth_req_content_cb (char *content, int clen, void *cbarg)
|
|
{
|
|
BlockTxServer *server = cbarg;
|
|
char *session_token = content;
|
|
SearpcClient *client = NULL;
|
|
char repo_id[37];
|
|
SeafRepo *repo;
|
|
|
|
if (session_token[clen - 1] != '\0') {
|
|
seaf_warning ("Invalid session token format.\n");
|
|
send_auth_response (server, STATUS_BAD_REQUEST);
|
|
return -1;
|
|
}
|
|
|
|
client = ccnet_create_pooled_rpc_client (seaf->client_pool,
|
|
NULL,
|
|
"ccnet-rpcserver");
|
|
if (!client) {
|
|
seaf_warning ("Failed to create rpc client.\n");
|
|
send_auth_response (server, STATUS_INTERNAL_SERVER_ERROR);
|
|
return -1;
|
|
}
|
|
|
|
if (seaf_token_manager_verify_token (seaf->token_mgr, client, NULL,
|
|
session_token, repo_id) < 0) {
|
|
seaf_warning ("Session token check failed.\n");
|
|
send_auth_response (server, STATUS_ACCESS_DENIED);
|
|
ccnet_rpc_client_free (client);
|
|
return -1;
|
|
}
|
|
|
|
ccnet_rpc_client_free (client);
|
|
|
|
repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
|
|
if (!repo) {
|
|
seaf_warning ("Failed to get repo %.8s.\n", repo_id);
|
|
return -1;
|
|
}
|
|
memcpy (server->store_id, repo->store_id, 36);
|
|
server->repo_version = repo->version;
|
|
seaf_repo_unref (repo);
|
|
|
|
if (send_auth_response (server, STATUS_OK) < 0)
|
|
return -1;
|
|
|
|
seaf_debug ("recv_state set to HEADER.\n");
|
|
|
|
server->parser.content_cb = handle_block_header_content_cb;
|
|
server->recv_state = RECV_STATE_HEADER;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_auth_request (BlockTxServer *server)
|
|
{
|
|
return handle_one_frame (server->recv_buf, &server->parser);
|
|
}
|
|
|
|
/* Block header */
|
|
|
|
static int
|
|
send_block_response_header (BlockTxServer *server, int status)
|
|
{
|
|
ResponseHeader header;
|
|
EVP_CIPHER_CTX *ctx;
|
|
int ret = 0;
|
|
|
|
header.status = htonl (status);
|
|
|
|
if (server->version == 1)
|
|
blocktx_encrypt_init (&ctx, server->key, server->iv);
|
|
else if (server->version == 2)
|
|
blocktx_encrypt_init (&ctx, server->key_v2, server->iv_v2);
|
|
|
|
if (send_encrypted_data_frame_begin (server->data_fd, sizeof(header)) < 0) {
|
|
seaf_warning ("Send block response header %s: failed to begin.\n",
|
|
server->curr_block_id);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (send_encrypted_data (ctx, server->data_fd,
|
|
&header, sizeof(header)) < 0)
|
|
{
|
|
seaf_warning ("Send block response header %s: failed to send data.\n",
|
|
server->curr_block_id);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (send_encrypted_data_frame_end (ctx, server->data_fd) < 0) {
|
|
seaf_warning ("Send block response header %s: failed to end.\n",
|
|
server->curr_block_id);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
EVP_CIPHER_CTX_free (ctx);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
send_block_content (BlockTxServer *server, int block_size);
|
|
|
|
static int
|
|
save_block_content_cb (char *content, int clen, int end, void *cbarg);
|
|
|
|
static int
|
|
handle_block_header_content_cb (char *content, int clen, void *cbarg)
|
|
{
|
|
BlockTxServer *server = cbarg;
|
|
RequestHeader *hdr;
|
|
|
|
if (clen != sizeof(RequestHeader)) {
|
|
seaf_warning ("Invalid block request header length %d.\n", clen);
|
|
send_block_response_header (server, STATUS_BAD_REQUEST);
|
|
return -1;
|
|
}
|
|
|
|
hdr = (RequestHeader *)content;
|
|
hdr->command = ntohl (hdr->command);
|
|
|
|
if (hdr->command != REQUEST_COMMAND_GET &&
|
|
hdr->command != REQUEST_COMMAND_PUT) {
|
|
seaf_warning ("Unknow command %d.\n", hdr->command);
|
|
send_block_response_header (server, STATUS_BAD_REQUEST);
|
|
return -1;
|
|
}
|
|
|
|
server->command = hdr->command;
|
|
memcpy (server->curr_block_id, hdr->block_id, 40);
|
|
|
|
if (server->command == REQUEST_COMMAND_GET) {
|
|
BlockMetadata *md;
|
|
int block_size;
|
|
|
|
seaf_debug ("Received GET request for block %s.\n", server->curr_block_id);
|
|
|
|
md = seaf_block_manager_stat_block (seaf->block_mgr,
|
|
server->store_id,
|
|
server->repo_version,
|
|
server->curr_block_id);
|
|
if (!md) {
|
|
seaf_warning ("Failed to stat block %s:%s.\n",
|
|
server->store_id, server->curr_block_id);
|
|
send_block_response_header (server, STATUS_NOT_FOUND);
|
|
return -1;
|
|
}
|
|
block_size = md->size;
|
|
g_free (md);
|
|
|
|
if (send_block_response_header (server, STATUS_OK) < 0)
|
|
return -1;
|
|
|
|
if (send_block_content (server, block_size) < 0)
|
|
return -1;
|
|
|
|
seaf_debug ("recv_state set to HEADER.\n");
|
|
|
|
server->recv_state = RECV_STATE_HEADER;
|
|
} else {
|
|
seaf_debug ("Received PUT request for block %s.\n", server->curr_block_id);
|
|
|
|
server->block = seaf_block_manager_open_block (seaf->block_mgr,
|
|
server->store_id,
|
|
server->repo_version,
|
|
server->curr_block_id,
|
|
BLOCK_WRITE);
|
|
if (!server->block) {
|
|
seaf_warning ("Failed to open block %s:%s for write.\n",
|
|
server->store_id, server->curr_block_id);
|
|
send_block_response_header (server, STATUS_INTERNAL_SERVER_ERROR);
|
|
return -1;
|
|
}
|
|
|
|
seaf_debug ("recv_state set to CONTENT.\n");
|
|
|
|
server->parser.fragment_cb = save_block_content_cb;
|
|
server->recv_state = RECV_STATE_CONTENT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_block_header (BlockTxServer *server)
|
|
{
|
|
return handle_one_frame (server->recv_buf, &server->parser);
|
|
}
|
|
|
|
/* Block content */
|
|
|
|
#define SEND_BUFFER_SIZE 4096
|
|
|
|
static int
|
|
send_encrypted_block (BlockTxServer *server,
|
|
BlockHandle *handle,
|
|
const char *block_id,
|
|
int size)
|
|
{
|
|
int n, remain;
|
|
int ret = 0;
|
|
EVP_CIPHER_CTX *ctx;
|
|
char send_buf[SEND_BUFFER_SIZE];
|
|
|
|
if (server->version == 1)
|
|
blocktx_encrypt_init (&ctx, server->key, server->iv);
|
|
else if (server->version == 2)
|
|
blocktx_encrypt_init (&ctx, server->key_v2, server->iv_v2);
|
|
|
|
if (send_encrypted_data_frame_begin (server->data_fd, size) < 0) {
|
|
seaf_warning ("Send block %s: failed to begin.\n", block_id);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
remain = size;
|
|
while (remain > 0) {
|
|
n = seaf_block_manager_read_block (seaf->block_mgr,
|
|
handle,
|
|
send_buf, SEND_BUFFER_SIZE);
|
|
if (n < 0) {
|
|
seaf_warning ("Failed to read block %s.\n", block_id);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (send_encrypted_data (ctx, server->data_fd, send_buf, n) < 0) {
|
|
seaf_warning ("Send block %s: failed to send data.\n", block_id);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
remain -= n;
|
|
}
|
|
|
|
if (send_encrypted_data_frame_end (ctx, server->data_fd) < 0) {
|
|
seaf_warning ("Send block %s: failed to end.\n", block_id);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
seaf_debug ("Send block %s done.\n", server->curr_block_id);
|
|
|
|
out:
|
|
EVP_CIPHER_CTX_free (ctx);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
send_block_content (BlockTxServer *server, int block_size)
|
|
{
|
|
BlockHandle *handle = NULL;
|
|
int ret = 0;
|
|
|
|
handle = seaf_block_manager_open_block (seaf->block_mgr,
|
|
server->store_id,
|
|
server->repo_version,
|
|
server->curr_block_id,
|
|
BLOCK_READ);
|
|
if (!handle) {
|
|
seaf_warning ("Failed to open block %s:%s.\n",
|
|
server->store_id, server->curr_block_id);
|
|
return -1;
|
|
}
|
|
|
|
ret = send_encrypted_block (server, handle, server->curr_block_id, block_size);
|
|
|
|
seaf_block_manager_close_block (seaf->block_mgr, handle);
|
|
seaf_block_manager_block_handle_free (seaf->block_mgr, handle);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
save_block_content_cb (char *content, int clen, int end, void *cbarg)
|
|
{
|
|
BlockTxServer *server = cbarg;
|
|
int n;
|
|
|
|
n = seaf_block_manager_write_block (seaf->block_mgr, server->block,
|
|
content, clen);
|
|
if (n < 0) {
|
|
seaf_warning ("Failed to write block %s.\n", server->curr_block_id);
|
|
send_block_response_header (server, STATUS_INTERNAL_SERVER_ERROR);
|
|
return -1;
|
|
}
|
|
|
|
if (end) {
|
|
if (seaf_block_manager_close_block (seaf->block_mgr, server->block) < 0) {
|
|
seaf_warning ("Failed to close block %s.\n", server->curr_block_id);
|
|
send_block_response_header (server, STATUS_INTERNAL_SERVER_ERROR);
|
|
return -1;
|
|
}
|
|
|
|
if (seaf_block_manager_commit_block (seaf->block_mgr, server->block) < 0) {
|
|
seaf_warning ("Failed to commit block %s.\n", server->curr_block_id);
|
|
send_block_response_header (server, STATUS_INTERNAL_SERVER_ERROR);
|
|
return -1;
|
|
}
|
|
|
|
seaf_block_manager_block_handle_free (seaf->block_mgr, server->block);
|
|
/* Set this handle to invalid. */
|
|
server->block = NULL;
|
|
|
|
send_block_response_header (server, STATUS_OK);
|
|
|
|
seaf_debug ("Receive block %s done.\n", server->curr_block_id);
|
|
seaf_debug ("recv_state set to HEADER.\n");
|
|
|
|
server->recv_state = RECV_STATE_HEADER;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
handle_block_content (BlockTxServer *server)
|
|
{
|
|
return handle_frame_fragments (server->recv_buf, &server->parser);
|
|
}
|
|
|
|
static void
|
|
recv_data_cb (BlockTxServer *server)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Let evbuffer determine how much data can be read. */
|
|
int n = evbuffer_read (server->recv_buf, server->data_fd, -1);
|
|
if (n == 0) {
|
|
seaf_debug ("Data connection is closed by the client. Transfer done.\n");
|
|
server->break_loop = TRUE;
|
|
return;
|
|
} else if (n < 0) {
|
|
seaf_warning ("Read data connection error: %s.\n",
|
|
evutil_socket_error_to_string(evutil_socket_geterror(server->data_fd)));
|
|
server->break_loop = TRUE;
|
|
return;
|
|
}
|
|
|
|
switch (server->recv_state) {
|
|
case RECV_STATE_HANDSHAKE:
|
|
ret = handle_handshake_request (server);
|
|
break;
|
|
case RECV_STATE_AUTH:
|
|
ret = handle_auth_request (server);
|
|
break;
|
|
case RECV_STATE_HEADER:
|
|
ret = handle_block_header (server);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
if (server->recv_state == RECV_STATE_CONTENT &&
|
|
server->command == REQUEST_COMMAND_PUT)
|
|
ret = handle_block_content (server);
|
|
|
|
break;
|
|
case RECV_STATE_CONTENT:
|
|
ret = handle_block_content (server);
|
|
break;
|
|
}
|
|
|
|
if (ret < 0)
|
|
server->break_loop = TRUE;
|
|
}
|
|
|
|
#define BLOCKTX_TIMEOUT 30
|
|
|
|
static void
|
|
server_thread_loop (BlockTxServer *server)
|
|
{
|
|
fd_set fds;
|
|
struct timeval tv = { BLOCKTX_TIMEOUT, 0 };
|
|
int rc;
|
|
|
|
while (1) {
|
|
FD_ZERO (&fds);
|
|
FD_SET (server->data_fd, &fds);
|
|
tv.tv_sec = BLOCKTX_TIMEOUT;
|
|
tv.tv_usec = 0;
|
|
|
|
rc = select (server->data_fd + 1, &fds, NULL, NULL, &tv);
|
|
if (rc < 0 && errno == EINTR) {
|
|
continue;
|
|
} else if (rc < 0) {
|
|
seaf_warning ("select error: %s.\n", strerror(errno));
|
|
break;
|
|
}
|
|
|
|
if (rc == 0) {
|
|
seaf_warning ("Recv block timeout.\n");
|
|
break;
|
|
}
|
|
|
|
if (FD_ISSET (server->data_fd, &fds)) {
|
|
recv_data_cb (server);
|
|
if (server->break_loop)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void *
|
|
block_tx_server_thread (void *vdata)
|
|
{
|
|
BlockTxServer *server = vdata;
|
|
|
|
server->recv_buf = evbuffer_new ();
|
|
|
|
server_thread_loop (server);
|
|
|
|
if (server->block) {
|
|
seaf_block_manager_close_block (seaf->block_mgr, server->block);
|
|
seaf_block_manager_block_handle_free (seaf->block_mgr, server->block);
|
|
}
|
|
|
|
if (server->parser.enc_init)
|
|
EVP_CIPHER_CTX_free (server->parser.ctx);
|
|
|
|
evbuffer_free (server->recv_buf);
|
|
evutil_closesocket (server->data_fd);
|
|
|
|
return vdata;
|
|
}
|
|
|
|
static void
|
|
block_tx_server_thread_done (void *vdata)
|
|
{
|
|
BlockTxServer *server = vdata;
|
|
|
|
g_free (server);
|
|
}
|
|
|
|
int
|
|
block_tx_server_start (evutil_socket_t data_fd)
|
|
{
|
|
BlockTxServer *server = g_new0 (BlockTxServer, 1);
|
|
int ret = 0;
|
|
|
|
int val = 1;
|
|
ev_socklen_t optlen = sizeof(int);
|
|
setsockopt (data_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, optlen);
|
|
|
|
server->data_fd = data_fd;
|
|
|
|
ret = ccnet_job_manager_schedule_job (seaf->job_mgr,
|
|
block_tx_server_thread,
|
|
block_tx_server_thread_done,
|
|
server);
|
|
if (ret < 0) {
|
|
seaf_warning ("Failed to start block tx server thread.\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|