880 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			880 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
		}
 | 
						|
    Copyright (c) 2016-2018 Chung, Hyung-Hwan. All rights reserved.
 | 
						|
 | 
						|
    Redistribution and use in source and binary forms, with or without
 | 
						|
    modification, are permitted provided that the following conditions
 | 
						|
    are met:
 | 
						|
    1. Redistributions of source code must retain the above copyright
 | 
						|
       notice, this list of conditions and the following disclaimer.
 | 
						|
    2. Redistributions in binary form must reproduce the above copyright
 | 
						|
       notice, this list of conditions and the following disclaimer in the
 | 
						|
       documentation and/or other materials provided with the distribution.
 | 
						|
 | 
						|
    THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 | 
						|
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 | 
						|
    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | 
						|
    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
						|
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | 
						|
    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
						|
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
						|
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
						|
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | 
						|
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
						|
 */
 | 
						|
 | 
						|
#include <hak-x.h>
 | 
						|
#include <hak-tmr.h>
 | 
						|
#include "hak-prv.h"
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
#include <errno.h>
 | 
						|
 | 
						|
#define HAK_SERVER_TOKEN_NAME_ALIGN 64
 | 
						|
#define HAK_SERVER_WID_MAP_ALIGN 512
 | 
						|
#define HAK_XPROTO_REPLY_BUF_SIZE 1300
 | 
						|
 | 
						|
#if defined(_WIN32)
 | 
						|
#	include <windows.h>
 | 
						|
#	include <tchar.h>
 | 
						|
#elif defined(__OS2__)
 | 
						|
#	define INCL_DOSMODULEMGR
 | 
						|
#	define INCL_DOSPROCESS
 | 
						|
#	define INCL_DOSERRORS
 | 
						|
#	include <os2.h>
 | 
						|
#elif defined(__DOS__)
 | 
						|
#	include <dos.h>
 | 
						|
#	include <time.h>
 | 
						|
#elif defined(macintosh)
 | 
						|
#	include <Timer.h>
 | 
						|
#else
 | 
						|
 | 
						|
#	if defined(HAVE_TIME_H)
 | 
						|
#		include <time.h>
 | 
						|
#	endif
 | 
						|
#	if defined(HAVE_SYS_TIME_H)
 | 
						|
#		include <sys/time.h>
 | 
						|
#	endif
 | 
						|
#	if defined(HAVE_SYS_UIO_H)
 | 
						|
#		include <sys/uio.h>
 | 
						|
#	endif
 | 
						|
 | 
						|
#	include <sys/types.h>
 | 
						|
#	include <sys/socket.h>
 | 
						|
#	include <pthread.h>
 | 
						|
#	include <poll.h>
 | 
						|
#	include <unistd.h>
 | 
						|
#endif
 | 
						|
 | 
						|
struct client_hak_xtn_t
 | 
						|
{
 | 
						|
	hak_client_t* client;
 | 
						|
};
 | 
						|
typedef struct client_hak_xtn_t client_hak_xtn_t;
 | 
						|
 | 
						|
enum state_flag_t
 | 
						|
{
 | 
						|
	STATE_LOCAL_IN_CLOSED  = (1 << 0),
 | 
						|
	STATE_LOCAL_OUT_CLOSED  = (1 << 1),
 | 
						|
	STATE_REMOTE_IN_CLOSED = (1 << 2),
 | 
						|
 | 
						|
	HAK_CLIENT_ALL_CLOSED = (STATE_LOCAL_IN_CLOSED | STATE_LOCAL_OUT_CLOSED | STATE_REMOTE_IN_CLOSED)
 | 
						|
};
 | 
						|
typedef enum state_flag_t state_flag_t;
 | 
						|
 | 
						|
struct hak_client_t
 | 
						|
{
 | 
						|
	hak_oow_t   _instsize;
 | 
						|
	hak_mmgr_t* _mmgr;
 | 
						|
	hak_cmgr_t* _cmgr;
 | 
						|
 | 
						|
	hak_client_prim_t prim;
 | 
						|
	hak_t* dummy_hak;
 | 
						|
 | 
						|
	hak_errnum_t errnum;
 | 
						|
	struct
 | 
						|
	{
 | 
						|
	#if defined(HAK_OOCH_IS_BCH)
 | 
						|
		hak_uch_t  xerrmsg[HAK_ERRMSG_CAPA];
 | 
						|
	#else
 | 
						|
		hak_bch_t  xerrmsg[HAK_ERRMSG_CAPA * 2];
 | 
						|
	#endif
 | 
						|
		hak_ooch_t buf[HAK_ERRMSG_CAPA];
 | 
						|
		hak_oow_t len;
 | 
						|
	} errmsg;
 | 
						|
	int stopreq;
 | 
						|
 | 
						|
	struct
 | 
						|
	{
 | 
						|
		hak_bitmask_t trait;
 | 
						|
		hak_bitmask_t logmask;
 | 
						|
	} cfg;
 | 
						|
 | 
						|
	int mux_pipe[2]; /* pipe to break the blocking multiplexer in the main server loop */
 | 
						|
	int state;
 | 
						|
 | 
						|
	struct
 | 
						|
	{
 | 
						|
		int sck;
 | 
						|
		hak_xproto_t* proto;
 | 
						|
	} remote;
 | 
						|
 | 
						|
	struct
 | 
						|
	{
 | 
						|
		int in;
 | 
						|
		int out;
 | 
						|
		int err;
 | 
						|
 | 
						|
		struct
 | 
						|
		{
 | 
						|
			hak_uint8_t* ptr;
 | 
						|
			hak_oow_t capa;
 | 
						|
			hak_oow_t pos;
 | 
						|
			hak_oow_t len;
 | 
						|
		} pw2r; /* pending write to the remote side */
 | 
						|
	} local;
 | 
						|
 | 
						|
 | 
						|
	struct
 | 
						|
	{
 | 
						|
		hak_bch_t buf[4096];
 | 
						|
		hak_oow_t pos;
 | 
						|
		hak_oow_t len;
 | 
						|
	} script;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================= */
 | 
						|
 | 
						|
static void client_log_write_for_dummy (hak_t* hak, hak_bitmask_t mask, const hak_ooch_t* msg, hak_oow_t len)
 | 
						|
{
 | 
						|
	client_hak_xtn_t* xtn = (client_hak_xtn_t*)hak_getxtn(hak);
 | 
						|
	hak_client_t* client;
 | 
						|
 | 
						|
	client = xtn->client;
 | 
						|
	client->prim.log_write (client, mask, msg, len);
 | 
						|
}
 | 
						|
 | 
						|
hak_client_t* hak_client_open (hak_mmgr_t* mmgr, hak_oow_t xtnsize, hak_client_prim_t* prim, hak_errinf_t* errinf)
 | 
						|
{
 | 
						|
	hak_client_t* client = HAK_NULL;
 | 
						|
	hak_t* hak = HAK_NULL;
 | 
						|
	client_hak_xtn_t* xtn;
 | 
						|
	int pfd[2];
 | 
						|
 | 
						|
	client = (hak_client_t*)HAK_MMGR_ALLOC(mmgr, HAK_SIZEOF(*client) + xtnsize);
 | 
						|
	if (HAK_UNLIKELY(!client))
 | 
						|
	{
 | 
						|
		if (errinf)
 | 
						|
		{
 | 
						|
			HAK_MEMSET(errinf, 0, HAK_SIZEOF(*errinf));
 | 
						|
			errinf->num = HAK_ESYSMEM;
 | 
						|
			hak_copy_oocstr(errinf->msg, HAK_COUNTOF(errinf->msg), hak_errnum_to_errstr(errinf->num));
 | 
						|
		}
 | 
						|
		goto oops;
 | 
						|
	}
 | 
						|
 | 
						|
	hak = hak_openstdwithmmgr(mmgr, HAK_SIZEOF(*xtn), errinf);
 | 
						|
	if (HAK_UNLIKELY(!hak)) goto oops;
 | 
						|
 | 
						|
	if (hak_sys_open_pipes(pfd, 1) <= -1)
 | 
						|
	{
 | 
						|
		if (errinf)
 | 
						|
		{
 | 
						|
			hak_seterrbfmtwithsyserr(hak, 0, errno, HAK_NULL, 0);
 | 
						|
			hak_geterrinf(hak, errinf);
 | 
						|
		}
 | 
						|
		goto oops;
 | 
						|
	}
 | 
						|
 | 
						|
	/* replace the vmprim.log_write function */
 | 
						|
	hak->vmprim.log_write = client_log_write_for_dummy;
 | 
						|
 | 
						|
	xtn = (client_hak_xtn_t*)hak_getxtn(hak);
 | 
						|
	xtn->client = client;
 | 
						|
 | 
						|
	HAK_MEMSET(client, 0, HAK_SIZEOF(*client) + xtnsize);
 | 
						|
	client->_instsize = HAK_SIZEOF(*client);
 | 
						|
	client->_mmgr = mmgr;
 | 
						|
	client->_cmgr = hak_get_utf8_cmgr();
 | 
						|
	client->prim = *prim;
 | 
						|
	client->dummy_hak = hak;
 | 
						|
	client->mux_pipe[0] = pfd[0];
 | 
						|
	client->mux_pipe[1] = pfd[1];
 | 
						|
	client->remote.sck = -1;
 | 
						|
	client->local.in = -1;
 | 
						|
	client->local.out = -1;
 | 
						|
	client->local.err = -1;
 | 
						|
 | 
						|
	client->cfg.logmask = ~(hak_bitmask_t)0;
 | 
						|
 | 
						|
	/* the dummy hak is used for this client to perform primitive operations
 | 
						|
	 * such as getting system time or logging. so the heap size doesn't
 | 
						|
	 * need to be changed from the tiny value set above. */
 | 
						|
	hak_setoption (client->dummy_hak, HAK_LOG_MASK, &client->cfg.logmask);
 | 
						|
	hak_setcmgr (client->dummy_hak, client->_cmgr);
 | 
						|
 | 
						|
	return client;
 | 
						|
 | 
						|
oops:
 | 
						|
	/* NOTE: pipe should be closed if jump to here is made after pipe() above */
 | 
						|
	if (hak) hak_close(hak);
 | 
						|
	if (client) HAK_MMGR_FREE(mmgr, client);
 | 
						|
	return HAK_NULL;
 | 
						|
}
 | 
						|
 | 
						|
static int is_stdio_fd (int fd)
 | 
						|
{
 | 
						|
	return fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO;
 | 
						|
}
 | 
						|
 | 
						|
void hak_client_close (hak_client_t* client)
 | 
						|
{
 | 
						|
	if (client->remote.proto) hak_xproto_close (client->remote.proto);
 | 
						|
	if (client->remote.sck >= 0) close (client->remote.sck);
 | 
						|
	if (client->local.in >= 0 && is_stdio_fd(client->local.in)) close (client->local.in);
 | 
						|
	if (client->local.out >= 0 && is_stdio_fd(client->local.out)) close (client->local.out);
 | 
						|
	if (client->local.err >= 0 && is_stdio_fd(client->local.err)) close (client->local.err);
 | 
						|
 | 
						|
	hak_sys_close_pipes(client->mux_pipe);
 | 
						|
	hak_close (client->dummy_hak);
 | 
						|
	HAK_MMGR_FREE(client->_mmgr, client);
 | 
						|
}
 | 
						|
 | 
						|
int hak_client_setoption (hak_client_t* client, hak_client_option_t id, const void* value)
 | 
						|
{
 | 
						|
	switch (id)
 | 
						|
	{
 | 
						|
		case HAK_CLIENT_TRAIT:
 | 
						|
			client->cfg.trait = *(const hak_bitmask_t*)value;
 | 
						|
			return 0;
 | 
						|
 | 
						|
		case HAK_CLIENT_LOG_MASK:
 | 
						|
			client->cfg.logmask = *(const hak_bitmask_t*)value;
 | 
						|
			if (client->dummy_hak)
 | 
						|
			{
 | 
						|
				/* setting this affects the dummy hak immediately.
 | 
						|
				 * existing hak instances inside worker threads won't get
 | 
						|
				 * affected. new hak instances to be created later
 | 
						|
				 * is supposed to use the new value */
 | 
						|
				hak_setoption (client->dummy_hak, HAK_LOG_MASK, value);
 | 
						|
			}
 | 
						|
			return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	hak_client_seterrnum (client, HAK_EINVAL);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
int hak_client_getoption (hak_client_t* client, hak_client_option_t id, void* value)
 | 
						|
{
 | 
						|
	switch (id)
 | 
						|
	{
 | 
						|
		case HAK_CLIENT_TRAIT:
 | 
						|
			*(hak_bitmask_t*)value = client->cfg.trait;
 | 
						|
			return 0;
 | 
						|
 | 
						|
		case HAK_CLIENT_LOG_MASK:
 | 
						|
			*(hak_bitmask_t*)value = client->cfg.logmask;
 | 
						|
			return 0;
 | 
						|
	};
 | 
						|
 | 
						|
	hak_client_seterrnum (client, HAK_EINVAL);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void* hak_client_getxtn (hak_client_t* client)
 | 
						|
{
 | 
						|
	return (void*)((hak_uint8_t*)client + client->_instsize);
 | 
						|
}
 | 
						|
 | 
						|
hak_mmgr_t* hak_client_getmmgr (hak_client_t* client)
 | 
						|
{
 | 
						|
	return client->_mmgr;
 | 
						|
}
 | 
						|
 | 
						|
hak_cmgr_t* hak_client_getcmgr (hak_client_t* client)
 | 
						|
{
 | 
						|
	return client->_cmgr;
 | 
						|
}
 | 
						|
 | 
						|
void hak_client_setcmgr (hak_client_t* client, hak_cmgr_t* cmgr)
 | 
						|
{
 | 
						|
	client->_cmgr = cmgr;
 | 
						|
}
 | 
						|
 | 
						|
hak_errnum_t hak_client_geterrnum (hak_client_t* client)
 | 
						|
{
 | 
						|
	return client->errnum;
 | 
						|
}
 | 
						|
 | 
						|
const hak_ooch_t* hak_client_geterrstr (hak_client_t* client)
 | 
						|
{
 | 
						|
	return hak_errnum_to_errstr(client->errnum);
 | 
						|
}
 | 
						|
 | 
						|
const hak_ooch_t* hak_client_geterrmsg (hak_client_t* client)
 | 
						|
{
 | 
						|
	if (client->errmsg.len <= 0) return hak_errnum_to_errstr(client->errnum);
 | 
						|
	return client->errmsg.buf;
 | 
						|
}
 | 
						|
 | 
						|
const hak_bch_t* hak_client_geterrbmsg (hak_client_t* client)
 | 
						|
{
 | 
						|
#if defined(HAK_OOCH_IS_BCH)
 | 
						|
	return (client->errmsg.len <= 0)? hak_errnum_to_errstr(client->errnum): client->errmsg.buf;
 | 
						|
#else
 | 
						|
	const hak_ooch_t* msg;
 | 
						|
	hak_oow_t wcslen, mbslen;
 | 
						|
 | 
						|
	msg = (client->errmsg.len <= 0)? hak_errnum_to_errstr(client->errnum): client->errmsg.buf;
 | 
						|
 | 
						|
	mbslen = HAK_COUNTOF(client->errmsg.xerrmsg);
 | 
						|
	hak_conv_ucstr_to_bcstr_with_cmgr(msg, &wcslen, client->errmsg.xerrmsg, &mbslen, client->_cmgr);
 | 
						|
 | 
						|
	return client->errmsg.xerrmsg;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
const hak_uch_t* hak_client_geterrumsg (hak_client_t* client)
 | 
						|
{
 | 
						|
#if defined(HAK_OOCH_IS_BCH)
 | 
						|
	const hak_ooch_t* msg;
 | 
						|
	hak_oow_t wcslen, mbslen;
 | 
						|
 | 
						|
	msg = (client->errmsg.len <= 0)? hak_errnum_to_errstr(client->errnum): client->errmsg.buf;
 | 
						|
 | 
						|
	wcslen = HAK_COUNTOF(client->errmsg.xerrmsg);
 | 
						|
	hak_conv_bcstr_to_ucstr_with_cmgr(msg, &mbslen, client->errmsg.xerrmsg, &wcslen, client->_cmgr, 1);
 | 
						|
 | 
						|
	return client->errmsg.xerrmsg;
 | 
						|
#else
 | 
						|
	return (client->errmsg.len == '\0')? hak_errnum_to_errstr(client->errnum): client->errmsg.buf;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void hak_client_seterrnum (hak_client_t* client, hak_errnum_t errnum)
 | 
						|
{
 | 
						|
	/*if (client->shuterr) return; */
 | 
						|
	client->errnum = errnum;
 | 
						|
	client->errmsg.len = 0;
 | 
						|
}
 | 
						|
 | 
						|
void hak_client_seterrbfmt (hak_client_t* client, hak_errnum_t errnum, const hak_bch_t* fmt, ...)
 | 
						|
{
 | 
						|
	va_list ap;
 | 
						|
 | 
						|
	va_start (ap, fmt);
 | 
						|
	hak_seterrbfmtv (client->dummy_hak, errnum, fmt, ap);
 | 
						|
	va_end (ap);
 | 
						|
 | 
						|
	HAK_ASSERT(client->dummy_hak, HAK_COUNTOF(client->errmsg.buf) == HAK_COUNTOF(client->dummy_hak->errmsg.buf));
 | 
						|
	client->errnum = errnum;
 | 
						|
	hak_copy_oochars (client->errmsg.buf, client->dummy_hak->errmsg.buf, HAK_COUNTOF(client->errmsg.buf));
 | 
						|
	client->errmsg.len = client->dummy_hak->errmsg.len;
 | 
						|
}
 | 
						|
 | 
						|
void hak_client_seterrufmt (hak_client_t* client, hak_errnum_t errnum, const hak_uch_t* fmt, ...)
 | 
						|
{
 | 
						|
	va_list ap;
 | 
						|
 | 
						|
	va_start (ap, fmt);
 | 
						|
	hak_seterrufmtv (client->dummy_hak, errnum, fmt, ap);
 | 
						|
	va_end (ap);
 | 
						|
 | 
						|
	HAK_ASSERT(client->dummy_hak, HAK_COUNTOF(client->errmsg.buf) == HAK_COUNTOF(client->dummy_hak->errmsg.buf));
 | 
						|
	client->errnum = errnum;
 | 
						|
	hak_copy_oochars (client->errmsg.buf, client->dummy_hak->errmsg.buf, HAK_COUNTOF(client->errmsg.buf));
 | 
						|
	client->errmsg.len = client->dummy_hak->errmsg.len;
 | 
						|
}
 | 
						|
 | 
						|
/* ========================================================================= */
 | 
						|
 | 
						|
void hak_client_logbfmt (hak_client_t* client, hak_bitmask_t mask, const hak_bch_t* fmt, ...)
 | 
						|
{
 | 
						|
	va_list ap;
 | 
						|
	va_start (ap, fmt);
 | 
						|
	hak_logbfmtv (client->dummy_hak, mask, fmt, ap);
 | 
						|
	va_end (ap);
 | 
						|
}
 | 
						|
 | 
						|
void hak_client_logufmt (hak_client_t* client, hak_bitmask_t mask, const hak_uch_t* fmt, ...)
 | 
						|
{
 | 
						|
	va_list ap;
 | 
						|
	va_start (ap, fmt);
 | 
						|
	hak_logufmtv (client->dummy_hak, mask, fmt, ap);
 | 
						|
	va_end (ap);
 | 
						|
}
 | 
						|
 | 
						|
/* ========================================================================= */
 | 
						|
 | 
						|
void* hak_client_allocmem (hak_client_t* client, hak_oow_t size)
 | 
						|
{
 | 
						|
	void* ptr;
 | 
						|
 | 
						|
	ptr = HAK_MMGR_ALLOC(client->_mmgr, size);
 | 
						|
	if (!ptr) hak_client_seterrnum (client, HAK_ESYSMEM);
 | 
						|
	return ptr;
 | 
						|
}
 | 
						|
 | 
						|
void* hak_client_callocmem (hak_client_t* client, hak_oow_t size)
 | 
						|
{
 | 
						|
	void* ptr;
 | 
						|
 | 
						|
	ptr = HAK_MMGR_ALLOC(client->_mmgr, size);
 | 
						|
	if (!ptr) hak_client_seterrnum (client, HAK_ESYSMEM);
 | 
						|
	else HAK_MEMSET(ptr, 0, size);
 | 
						|
	return ptr;
 | 
						|
}
 | 
						|
 | 
						|
void* hak_client_reallocmem (hak_client_t* client, void* ptr, hak_oow_t size)
 | 
						|
{
 | 
						|
	ptr = HAK_MMGR_REALLOC(client->_mmgr, ptr, size);
 | 
						|
	if (!ptr) hak_client_seterrnum (client, HAK_ESYSMEM);
 | 
						|
	return ptr;
 | 
						|
}
 | 
						|
 | 
						|
void hak_client_freemem (hak_client_t* client, void* ptr)
 | 
						|
{
 | 
						|
	HAK_MMGR_FREE(client->_mmgr, ptr);
 | 
						|
}
 | 
						|
 | 
						|
/* ========================================================================= */
 | 
						|
 | 
						|
struct proto_xtn_t
 | 
						|
{
 | 
						|
	hak_client_t* client;
 | 
						|
};
 | 
						|
typedef struct proto_xtn_t proto_xtn_t;
 | 
						|
 | 
						|
static int proto_on_packet (hak_xproto_t* proto, hak_xpkt_type_t type, const void* data, hak_oow_t len)
 | 
						|
{
 | 
						|
	proto_xtn_t* proto_xtn;
 | 
						|
	hak_client_t* client;
 | 
						|
	proto_xtn = hak_xproto_getxtn(proto);
 | 
						|
	client = proto_xtn->client;
 | 
						|
	return client->prim.on_packet(client, type, data, len);
 | 
						|
}
 | 
						|
 | 
						|
static int client_connect_to_server (hak_client_t* client, const char* ipaddr)
 | 
						|
{
 | 
						|
	hak_sckaddr_t sckaddr;
 | 
						|
	hak_scklen_t scklen;
 | 
						|
	int sckfam;
 | 
						|
	int sck = -1;
 | 
						|
	hak_xproto_t* proto = HAK_NULL;
 | 
						|
 | 
						|
	proto_xtn_t* proto_xtn;
 | 
						|
	hak_xproto_cb_t proto_cb;
 | 
						|
 | 
						|
	sckfam = hak_bchars_to_sckaddr(ipaddr, strlen(ipaddr), &sckaddr, &scklen);
 | 
						|
	if (sckfam <= -1)
 | 
						|
	{
 | 
						|
		hak_client_seterrbfmt (client, HAK_EINVAL, "cannot convert ip address - %hs", ipaddr);
 | 
						|
		goto oops;
 | 
						|
	}
 | 
						|
 | 
						|
	sck = socket(sckfam, SOCK_STREAM, 0);
 | 
						|
	if (sck <= -1)
 | 
						|
	{
 | 
						|
		hak_client_seterrbfmt (client, HAK_ESYSERR, "cannot create socket - %hs", strerror(errno));
 | 
						|
		goto oops;
 | 
						|
	}
 | 
						|
 | 
						|
	hak_sys_set_cloexec(sck, 1);
 | 
						|
 | 
						|
#if 0
 | 
						|
	if (sckfam == AF_INET)
 | 
						|
	{
 | 
						|
		struct sockaddr_in anyaddr;
 | 
						|
		int opt = 1;
 | 
						|
		setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
 | 
						|
		HAK_MEMSET(&anyaddr, 0, HAK_SIZEOF(anyaddr));
 | 
						|
		anyaddr.sin_family = sckfam;
 | 
						|
		if (bind(sck, (struct sockaddr *)&anyaddr, scklen) <= -1)
 | 
						|
		{
 | 
						|
			hak_client_seterrbfmt (client, HAK_ESYSERR,
 | 
						|
				"cannot bind socket %d - %hs", sck, strerror(errno));
 | 
						|
			goto oops;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (sckfam == AF_INET6)
 | 
						|
	{
 | 
						|
		struct sockaddr_in6 anyaddr;
 | 
						|
		int opt = 1;
 | 
						|
		setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
 | 
						|
		HAK_MEMSET(&anyaddr, 0, HAK_SIZEOF(anyaddr));
 | 
						|
		anyaddr.sin6_family = sckfam;
 | 
						|
		if (bind(sck, (struct sockaddr *)&anyaddr, scklen) <= -1)
 | 
						|
		{
 | 
						|
			hak_client_seterrbfmt (client, HAK_ESYSERR,
 | 
						|
				"cannot bind socket %d - %hs", sck, strerror(errno));
 | 
						|
			goto oops;
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
/* TODO: async connect? */
 | 
						|
/* TODO: connect timeout */
 | 
						|
	if (connect(sck, (struct sockaddr*)&sckaddr, scklen) <= -1)
 | 
						|
	{
 | 
						|
		hak_client_seterrbfmt (client, HAK_ESYSERR,
 | 
						|
			"cannot connect socket %d to %hs - %hs", sck, ipaddr, strerror(errno));
 | 
						|
		goto oops;
 | 
						|
	}
 | 
						|
 | 
						|
	hak_sys_set_nonblock(sck, 1); /* make it nonblocking after connection has been established */
 | 
						|
 | 
						|
	HAK_MEMSET(&proto, 0, HAK_SIZEOF(proto_cb));
 | 
						|
	proto_cb.on_packet = proto_on_packet;
 | 
						|
 | 
						|
	proto = hak_xproto_open(hak_client_getmmgr(client), &proto_cb, HAK_SIZEOF(*proto_xtn));
 | 
						|
	if (HAK_UNLIKELY(!proto))
 | 
						|
	{
 | 
						|
		hak_client_seterrbfmt (client, HAK_ESYSERR, "cannot open protocol to %s", ipaddr);
 | 
						|
		goto oops;
 | 
						|
	}
 | 
						|
	proto_xtn = hak_xproto_getxtn(proto);
 | 
						|
	proto_xtn->client = client;
 | 
						|
 | 
						|
	client->remote.sck = sck;
 | 
						|
	client->remote.proto = proto;
 | 
						|
	return 0;
 | 
						|
 | 
						|
oops:
 | 
						|
	if (proto) hak_xproto_close (proto);
 | 
						|
	if (sck >= 0) close (sck);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void client_close (hak_client_t* client)
 | 
						|
{
 | 
						|
	if (client->remote.proto)
 | 
						|
	{
 | 
						|
		hak_xproto_close (client->remote.proto);
 | 
						|
		client->remote.proto = HAK_NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (client->remote.sck >= 0)
 | 
						|
	{
 | 
						|
		close (client->remote.sck);
 | 
						|
		client->remote.sck = -1;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int client_add_to_local_pw2r (hak_client_t* client, const hak_uint8_t* ptr, hak_oow_t len)
 | 
						|
{
 | 
						|
	if (client->local.pw2r.len >= client->local.pw2r.capa)
 | 
						|
	{
 | 
						|
		hak_uint8_t* tmp;
 | 
						|
		hak_oow_t newcapa;
 | 
						|
 | 
						|
		newcapa = HAK_ALIGN_POW2(client->local.pw2r.capa + len, 128);
 | 
						|
		tmp = hak_client_reallocmem(client, client->local.pw2r.ptr, newcapa * HAK_SIZEOF(*client->local.pw2r.ptr));
 | 
						|
		if (HAK_UNLIKELY(!tmp)) return -1;
 | 
						|
 | 
						|
		client->local.pw2r.capa = newcapa;
 | 
						|
		client->local.pw2r.ptr = tmp;
 | 
						|
	}
 | 
						|
 | 
						|
	HAK_MEMCPY(&client->local.pw2r.ptr[client->local.pw2r.len], ptr, len);
 | 
						|
	client->local.pw2r.len += len;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int client_send_to_remote (hak_client_t* client, hak_xpkt_type_t pktype, const hak_uint8_t* ptr, hak_oow_t len)
 | 
						|
{
 | 
						|
	hak_xpkt_hdr_t hdr;
 | 
						|
	struct iovec iov[2];
 | 
						|
	hak_uint16_t seglen;
 | 
						|
	int n, i;
 | 
						|
 | 
						|
	do
 | 
						|
	{
 | 
						|
		seglen = (len > HAK_XPKT_MAX_PLD_LEN)? HAK_XPKT_MAX_PLD_LEN: len;
 | 
						|
 | 
						|
		hdr.id = 1; /* TODO: */
 | 
						|
		hdr.type = pktype | (((seglen >> 8) & 0x0F) << 4);
 | 
						|
		hdr.len = seglen & 0xFF;
 | 
						|
 | 
						|
		i = 0;
 | 
						|
		iov[i].iov_base = &hdr;
 | 
						|
		iov[i++].iov_len = HAK_SIZEOF(hdr);
 | 
						|
		if (seglen > 0)
 | 
						|
		{
 | 
						|
			iov[i].iov_base = ptr;
 | 
						|
			iov[i++].iov_len = seglen;
 | 
						|
		}
 | 
						|
 | 
						|
		n = hak_sys_send_iov(client->remote.sck, iov, i);
 | 
						|
		if (n <= -1) return -1;
 | 
						|
 | 
						|
		if (n < i || iov[n - 1].iov_len > 0)
 | 
						|
		{
 | 
						|
			/* the write isn't completed. */
 | 
						|
			for (i = n; i < 2 ; i++)
 | 
						|
			{
 | 
						|
				if (iov[i].iov_len > 0)
 | 
						|
				{
 | 
						|
					/* pending write... */
 | 
						|
					if (client_add_to_local_pw2r(client, iov[i].iov_base, iov[i].iov_len) <= -1) return -1;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		ptr += seglen;
 | 
						|
		len -= seglen;
 | 
						|
	}
 | 
						|
	while (len > 0);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* ========================================================================= */
 | 
						|
 | 
						|
static void on_control_event (hak_client_t* client, struct pollfd* pfd)
 | 
						|
{
 | 
						|
	char tmp[128];
 | 
						|
hak_client_logbfmt(client, HAK_LOG_STDERR, "ON CONTROL EVENT \n");
 | 
						|
	while (read(client->mux_pipe[0], tmp, HAK_SIZEOF(tmp)) > 0) /* nothing */;
 | 
						|
/* TODO: handle different command? */
 | 
						|
}
 | 
						|
 | 
						|
static void on_remote_event (hak_client_t* client, struct pollfd* pfd, int shut_wr_after_req)
 | 
						|
{
 | 
						|
//hak_client_logbfmt(client, HAK_LOG_STDERR, "ON REMOTE EVENT \n");
 | 
						|
 | 
						|
	if (pfd->revents & POLLOUT)
 | 
						|
	{
 | 
						|
		ssize_t n;
 | 
						|
		hak_oow_t len;
 | 
						|
 | 
						|
		len = client->local.pw2r.len - client->local.pw2r.pos;
 | 
						|
		n = hak_sys_send(client->remote.sck, &client->local.pw2r.ptr[client->local.pw2r.pos], &len);
 | 
						|
		client->local.pw2r.pos += len;
 | 
						|
		if (client->local.pw2r.pos >= client->local.pw2r.len)
 | 
						|
		{
 | 
						|
			/* empty the buffer */
 | 
						|
			client->local.pw2r.pos = 0;
 | 
						|
			client->local.pw2r.len = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		if (n <= -1)
 | 
						|
		{
 | 
						|
			/* TODO: logging */
 | 
						|
hak_client_logbfmt(client, HAK_LOG_STDERR, "send error - %hs\n", strerror(errno));
 | 
						|
			goto reqstop;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (pfd->revents & POLLIN)
 | 
						|
	{
 | 
						|
		hak_oow_t bcap;
 | 
						|
		hak_uint8_t* bptr;
 | 
						|
		ssize_t x;
 | 
						|
 | 
						|
		bptr = hak_xproto_getbuf(client->remote.proto, &bcap);
 | 
						|
		x = recv(client->remote.sck, bptr, bcap, 0);
 | 
						|
		if (x <= -1)
 | 
						|
		{
 | 
						|
			if (errno == EINTR) goto carry_on; /* didn't read read */
 | 
						|
hak_client_logbfmt(client, HAK_LOG_STDERR, "recv error from remote - %hs", strerror(errno));
 | 
						|
			/*hak_seterrwithsyserr(hak, 0, errno); */
 | 
						|
			/* TODO: error info set... */
 | 
						|
			goto reqstop;
 | 
						|
		}
 | 
						|
		if (x == 0) hak_xproto_seteof(client->remote.proto, 1);
 | 
						|
		hak_xproto_advbuf (client->remote.proto, x);
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
carry_on:
 | 
						|
	/* handle the data received from the remote side */
 | 
						|
	while (hak_xproto_ready(client->remote.proto))
 | 
						|
	{
 | 
						|
		int n;
 | 
						|
 | 
						|
		if ((n = hak_xproto_process(client->remote.proto)) <= -1)
 | 
						|
		{
 | 
						|
			/* TODO: proper error message */
 | 
						|
			printf ("PROTOCOL PROCESSING ERROR...\n");
 | 
						|
			goto reqstop;
 | 
						|
		}
 | 
						|
		if (n == 0)
 | 
						|
		{
 | 
						|
			/* TODO: chceck if there is remaining data in the buffer...?? */
 | 
						|
			printf ("CALLBACK REQUESTED TO EXIT...\n");
 | 
						|
			goto reqstop;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (hak_xproto_geteof(client->remote.proto))
 | 
						|
	{
 | 
						|
		client->state |= STATE_REMOTE_IN_CLOSED;
 | 
						|
		goto reqstop;
 | 
						|
	}
 | 
						|
	return;
 | 
						|
 | 
						|
reqstop:
 | 
						|
	client->stopreq = 1;
 | 
						|
	return;
 | 
						|
}
 | 
						|
 | 
						|
static void on_local_in_event (hak_client_t* client, struct pollfd* pfd)
 | 
						|
{
 | 
						|
	ssize_t n;
 | 
						|
	hak_uint8_t buf[128];
 | 
						|
 | 
						|
//hak_client_logbfmt(client, HAK_LOG_STDERR, "local in on %d\n", pfd->fd);
 | 
						|
	n = read(pfd->fd, buf, HAK_SIZEOF(buf));
 | 
						|
	if (n <= -1)
 | 
						|
	{
 | 
						|
		//if (hak_sys_is_errno_wb(errno)) ...
 | 
						|
hak_client_logbfmt(client, HAK_LOG_STDERR, "local in read error - %hs\n", strerror(errno));
 | 
						|
		client->stopreq = 1;
 | 
						|
	}
 | 
						|
	else if (n == 0)
 | 
						|
	{
 | 
						|
/*hak_client_logbfmt(client, HAK_LOG_STDERR, "local in eof\n");*/
 | 
						|
/* TODO ARRANGE TO FINISH.. AFTER EXUCTION OF REMAINING STUFF... */
 | 
						|
		//client->stopreq = 1;
 | 
						|
		client->state |= STATE_LOCAL_IN_CLOSED;
 | 
						|
		n = client_send_to_remote(client, HAK_XPKT_EXECUTE, HAK_NULL, 0);
 | 
						|
		if (n <= -1)
 | 
						|
		{
 | 
						|
hak_client_logbfmt(client, HAK_LOG_STDERR, "local to remote  (execute)- %hs\n", strerror(errno));
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
/*hak_client_logbfmt(client, HAK_LOG_STDERR, "local read - %ld\n", (long)n);*/
 | 
						|
		n = client_send_to_remote(client, HAK_XPKT_CODE, buf, n);
 | 
						|
		if (n <= -1)
 | 
						|
		{
 | 
						|
hak_client_logbfmt(client, HAK_LOG_STDERR, "local to remote (code)- %hs\n", strerror(errno));
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int client_setup_local(hak_client_t* client)
 | 
						|
{
 | 
						|
	client->local.in = STDIN_FILENO;
 | 
						|
	client->local.out = STDOUT_FILENO;
 | 
						|
	client->local.err = STDERR_FILENO;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int hak_client_start (hak_client_t* client, const char* ipaddr, int shut_wr_after_req)
 | 
						|
{
 | 
						|
	/*  TODO: cin, cout, cerr could be actual files or something other than the console.
 | 
						|
	          the actual loop won't begin until all these file descriptors are ready */
 | 
						|
 | 
						|
	client->stopreq = 0;
 | 
						|
	if (client_setup_local(client) <= -1) return -1;
 | 
						|
hak_client_logbfmt(client, HAK_LOG_STDERR, "staritg XXXXXXXXXXX loop... ...\n");
 | 
						|
	if (client_connect_to_server(client, ipaddr) <= -1) return -1; /* TODO: support time out or abort while connecting... */
 | 
						|
 | 
						|
hak_client_logbfmt(client, HAK_LOG_STDERR, "staritg client loop... ...\n");
 | 
						|
	while (!client->stopreq)
 | 
						|
	{
 | 
						|
		int nfds, i;
 | 
						|
		struct pollfd pfd[10];
 | 
						|
 | 
						|
		if ((client->state & HAK_CLIENT_ALL_CLOSED) == HAK_CLIENT_ALL_CLOSED)
 | 
						|
		{
 | 
						|
			/* no explicit stop request. but all file descriptors reached EOF */
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		HAK_MEMSET(pfd, 0, HAK_SIZEOF(pfd));
 | 
						|
		nfds = 0;
 | 
						|
 | 
						|
		/* always monitor the control channel */
 | 
						|
		pfd[nfds].fd = client->mux_pipe[0];
 | 
						|
		pfd[nfds].events = POLLIN;
 | 
						|
		pfd[nfds++].revents = 0;
 | 
						|
 | 
						|
		pfd[nfds].fd = client->remote.sck;
 | 
						|
		/* TODO: if there is data received from the server, not flushed to the client side
 | 
						|
		 *       don't monitor input */
 | 
						|
		pfd[nfds].events = POLLIN;
 | 
						|
		if (client->local.pw2r.len > client->local.pw2r.pos) pfd[nfds].events |= POLLOUT;
 | 
						|
		pfd[nfds++].revents = 0;
 | 
						|
 | 
						|
		/* TODO: client->local.in and client->local.out can be equal.
 | 
						|
		 *       handle this? */
 | 
						|
		if (client->local.in >= 0)
 | 
						|
		{
 | 
						|
			if (client->local.pw2r.pos >= client->local.pw2r.len)
 | 
						|
			{
 | 
						|
//hak_client_logbfmt(client, HAK_LOG_STDERR, "ADDING LOCAL IN TO MULTIPLEX...\n");
 | 
						|
				pfd[nfds].fd = client->local.in;
 | 
						|
				pfd[nfds].events = POLLIN;
 | 
						|
				pfd[nfds++].revents = 0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		i = poll(pfd, nfds, 1000);
 | 
						|
//hak_client_logbfmt(client, HAK_LOG_STDERR, "poll returned %d\n", i);
 | 
						|
		if (i <= -1)
 | 
						|
		{
 | 
						|
			hak_client_seterrbfmt (client, HAK_ESYSERR, "poll error - %hs", strerror(errno));
 | 
						|
			goto oops;
 | 
						|
		}
 | 
						|
 | 
						|
		if (i == 0)
 | 
						|
		{
 | 
						|
			/* TODO: proper timeout handling */
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		for (i = 0; i < nfds; i++)
 | 
						|
		{
 | 
						|
			if (!pfd[i].revents) continue;
 | 
						|
 | 
						|
//hak_client_logbfmt(client, HAK_LOG_STDERR, "EVENT ON %d mux[%d], remote[%d], local[%d]\n", pfd[i].fd, client->mux_pipe[0], client->remote.sck, client->local.in);
 | 
						|
			if (pfd[i].fd == client->mux_pipe[0])
 | 
						|
			{
 | 
						|
				on_control_event (client, &pfd[i]);
 | 
						|
			}
 | 
						|
			else if (pfd[i].fd == client->remote.sck)
 | 
						|
			{
 | 
						|
				/* event from the server */
 | 
						|
				on_remote_event (client, &pfd[i], shut_wr_after_req);
 | 
						|
			}
 | 
						|
			else if (pfd[i].fd == client->local.in)
 | 
						|
			{
 | 
						|
				/*if (pfd[i].revents & POLLIN)*/
 | 
						|
					on_local_in_event (client, &pfd[i]);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
done:
 | 
						|
/* TODO: we can check if the buffer has all been consumed. if not, there is trailing garbage.. */
 | 
						|
	/*{
 | 
						|
		struct linger linger;
 | 
						|
		linger.l_onoff = 1;
 | 
						|
		linger.l_linger = 0;
 | 
						|
		setsockopt (client->remote.sck, SOL_SOCKET, SO_LINGER, (char *) &linger, sizeof(linger));
 | 
						|
	}*/
 | 
						|
 | 
						|
	client_close (client);
 | 
						|
	return 0;
 | 
						|
 | 
						|
oops:
 | 
						|
	client_close (client);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
void hak_client_stop (hak_client_t* client)
 | 
						|
{
 | 
						|
	client->stopreq = 1;
 | 
						|
	write (client->mux_pipe[1], "Q", 1); /* don't care about failure */
 | 
						|
}
 |