From d51ad1cd6a8c49588b04903a7d44306e0b147eeb Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Mon, 12 Mar 2018 10:39:13 +0000 Subject: [PATCH] Added partial sample program to accept requests over a socket --- lib/Makefile.am | 2 +- lib/Makefile.in | 19 +- lib/exec.c | 1 - lib/hcl-cmn.h | 3 + lib/hcl.c | 13 +- lib/main.c | 32 +- lib/main2.c | 2190 +++++++++++++++++++++++++++++++++++++++++++++++ lib/utl.c | 6 +- 8 files changed, 2245 insertions(+), 21 deletions(-) create mode 100644 lib/main2.c diff --git a/lib/Makefile.am b/lib/Makefile.am index 9af14d6..2d330fe 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -79,7 +79,7 @@ endif bin_PROGRAMS = hcl -hcl_SOURCES = main.c +hcl_SOURCES = main.c main2.c hcl_CPPFLAGS = $(CPPFLAGS_LIB_COMMON) hcl_LDFLAGS = $(LDFLAGS_LIB_COMMON) hcl_LDADD = $(LIBADD_LIB_COMMON) -lhcl #-ldyncall_s diff --git a/lib/Makefile.in b/lib/Makefile.in index 173e60e..04cb058 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -163,7 +163,7 @@ libhcl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libhcl_la_LDFLAGS) $(LDFLAGS) -o $@ PROGRAMS = $(bin_PROGRAMS) -am_hcl_OBJECTS = hcl-main.$(OBJEXT) +am_hcl_OBJECTS = hcl-main.$(OBJEXT) hcl-main2.$(OBJEXT) hcl_OBJECTS = $(am_hcl_OBJECTS) hcl_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ @@ -434,7 +434,7 @@ libhcl_la_CPPFLAGS = $(CPPFLAGS_LIB_COMMON) libhcl_la_LDFLAGS = $(LDFLAGS_LIB_COMMON) libhcl_la_LIBADD = $(LIBADD_LIB_COMMON) $(am__append_3) libhcl_la_DEPENDENCIES = $(am__append_4) -hcl_SOURCES = main.c +hcl_SOURCES = main.c main2.c hcl_CPPFLAGS = $(CPPFLAGS_LIB_COMMON) hcl_LDFLAGS = $(LDFLAGS_LIB_COMMON) hcl_LDADD = $(LIBADD_LIB_COMMON) -lhcl #-ldyncall_s @@ -587,6 +587,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hcl-main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hcl-main2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcl_la-bigint.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcl_la-comp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcl_la-debug.Plo@am__quote@ @@ -786,6 +787,20 @@ hcl-main.obj: main.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hcl_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hcl-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi` +hcl-main2.o: main2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hcl_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hcl-main2.o -MD -MP -MF $(DEPDIR)/hcl-main2.Tpo -c -o hcl-main2.o `test -f 'main2.c' || echo '$(srcdir)/'`main2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hcl-main2.Tpo $(DEPDIR)/hcl-main2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main2.c' object='hcl-main2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hcl_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hcl-main2.o `test -f 'main2.c' || echo '$(srcdir)/'`main2.c + +hcl-main2.obj: main2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hcl_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hcl-main2.obj -MD -MP -MF $(DEPDIR)/hcl-main2.Tpo -c -o hcl-main2.obj `if test -f 'main2.c'; then $(CYGPATH_W) 'main2.c'; else $(CYGPATH_W) '$(srcdir)/main2.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hcl-main2.Tpo $(DEPDIR)/hcl-main2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main2.c' object='hcl-main2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hcl_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hcl-main2.obj `if test -f 'main2.c'; then $(CYGPATH_W) 'main2.c'; else $(CYGPATH_W) '$(srcdir)/main2.c'; fi` + mostlyclean-libtool: -rm -f *.lo diff --git a/lib/exec.c b/lib/exec.c index abfaf5d..7bcfb50 100644 --- a/lib/exec.c +++ b/lib/exec.c @@ -2246,7 +2246,6 @@ hcl_oop_t hcl_executefromip (hcl_t* hcl, hcl_oow_t initial_ip) int n, log_default_type_mask; HCL_ASSERT (hcl, hcl->code.bc.len < HCL_SMOOI_MAX); /* asserted by the compiler */ - HCL_ASSERT (hcl, initial_ip < hcl->code.bc.len); if (initial_ip >= hcl->code.bc.len) { hcl_seterrnum (hcl, HCL_EINVAL); diff --git a/lib/hcl-cmn.h b/lib/hcl-cmn.h index 9bb8868..40ce88d 100644 --- a/lib/hcl-cmn.h +++ b/lib/hcl-cmn.h @@ -345,6 +345,9 @@ typedef struct hcl_bcs_t hcl_bcs_t; # define HCL_SIZEOF_OOCH_T HCL_SIZEOF_BCH_T #endif +/* the maximum number of bch charaters to represent a single uch character */ +#define HCL_BCSIZE_MAX 6 + /* ========================================================================= * TIME-RELATED TYPES * =========================================================================*/ diff --git a/lib/hcl.c b/lib/hcl.c index fcc6a28..9e8b3e2 100644 --- a/lib/hcl.c +++ b/lib/hcl.c @@ -286,11 +286,22 @@ void hcl_fini (hcl_t* hcl) } } -void hcl_reset (hcl_t* hcl) +void hcl_reset (hcl_t* hcl/*, int flags*/) { hcl_oop_t v; hcl_oow_t i; +#if 0 + if (flags & HCL_RESET_LXC) + { + hcl->c->nungots = 0; + + hcl->c->inarg.line = 1; + hcl->c->inarg.colm = 1; + /* reset on curinp???? hwo to reset the input stream? or crate a separate function? */ + } +#endif + /* delete all literals shown in the literal frame from the system dictionary * excluding special kernel symbols. */ for (i = 0; i < hcl->code.lit.len; i++) diff --git a/lib/main.c b/lib/main.c index 8dc0c54..29076b3 100644 --- a/lib/main.c +++ b/lib/main.c @@ -519,14 +519,14 @@ static int write_all (int fd, const char* ptr, hcl_oow_t len) { hcl_ooi_t wr; - wr = write (1, ptr, len); + wr = write(fd, ptr, len); if (wr <= -1) { #if defined(EAGAIN) && defined(EWOULDBLOCK) && (EAGAIN == EWOULDBLOCK) if (errno == EAGAIN) continue; #else - # if defined(EAGAIN) + #if defined(EAGAIN) if (errno == EAGAIN) continue; #elif defined(EWOULDBLOCK) if (errno == EWOULDBLOCK) continue; @@ -549,12 +549,6 @@ static int write_all (int fd, const char* ptr, hcl_oow_t len) static void log_write (hcl_t* hcl, int mask, const hcl_ooch_t* msg, hcl_oow_t len) { -#if defined(_WIN32) -# error NOT IMPLEMENTED - -#elif defined(macintosh) -# error NOT IMPLEMENTED -#else hcl_bch_t buf[256]; hcl_oow_t ucslen, bcslen, msgidx; int n; @@ -664,8 +658,6 @@ static void log_write (hcl_t* hcl, int mask, const hcl_ooch_t* msg, hcl_oow_t le { if (mask & (HCL_LOG_FATAL | HCL_LOG_ERROR | HCL_LOG_WARN)) write_all (logfd, "\x1B[0m", 4); } - -#endif } @@ -1330,7 +1322,7 @@ static int handle_logopt (hcl_t* hcl, const hcl_bch_t* str) #if defined(HCL_BUILD_DEBUG) static int handle_dbgopt (hcl_t* hcl, const hcl_bch_t* str) { - xtn_t* xtn = (xtn_t*)hcl_getxtn (hcl); + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); const hcl_bch_t* cm, * flt; hcl_oow_t len; unsigned int trait, dbgopt = 0; @@ -1567,7 +1559,7 @@ static void print_synerr (hcl_t* hcl) #define MIN_MEMSIZE 512000ul -int main (int argc, char* argv[]) +static int main_tty (int argc, char* argv[]) { hcl_t* hcl; xtn_t* xtn; @@ -1895,3 +1887,19 @@ oops: hcl_close (hcl); return -1; } + +int main_server (int argc, char* argv[]); + +int main (int argc, char* argv[]) +{ + const char* slash; + const char* prog; + + prog = argv[0]; + slash = strrchr(prog, '/'); + if (slash) prog = slash + 1; + + if (strcmp(prog, "hcld") == 0) return main_server (argc, argv); + + return main_tty (argc, argv); +} diff --git a/lib/main2.c b/lib/main2.c new file mode 100644 index 0000000..432d6e9 --- /dev/null +++ b/lib/main2.c @@ -0,0 +1,2190 @@ + + +/* + * $Id$ + * + 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 "hcl-prv.h" +#include "hcl-opt.h" + +#include +#include +#include +#include +#include +#include + + +#if defined(_WIN32) +# include +# include +# if defined(HCL_HAVE_CFG_H) && defined(HCL_ENABLE_LIBLTDL) +# include +# define USE_LTDL +# endif +#elif defined(__OS2__) +# define INCL_DOSMODULEMGR +# define INCL_DOSPROCESS +# define INCL_DOSERRORS +# include +#elif defined(__MSDOS__) +# include +# include +#elif defined(macintosh) +# include +#else + +# if defined(HCL_ENABLE_LIBLTDL) +# include +# define USE_LTDL +# define sys_dl_error() lt_dlerror() +# define sys_dl_open(x) lt_dlopen(x) +# define sys_dl_openext(x) lt_dlopenext(x) +# define sys_dl_close(x) lt_dlclose(x) +# define sys_dl_getsym(x,n) lt_dlsym(x,n) +# elif defined(HAVE_DLFCN_H) +# include +# define USE_DLFCN +# define sys_dl_error() dlerror() +# define sys_dl_open(x) dlopen(x,RTLD_NOW) +# define sys_dl_openext(x) dlopen(x,RTLD_NOW) +# define sys_dl_close(x) dlclose(x) +# define sys_dl_getsym(x,n) dlsym(x,n) +# else +# error UNSUPPORTED DYNAMIC LINKER +# endif + +# if defined(HAVE_TIME_H) +# include +# endif +# if defined(HAVE_SYS_TIME_H) +# include +# endif +# if defined(HAVE_SIGNAL_H) +# include +# endif +# if defined(HAVE_SYS_MMAN_H) +# include +# endif + +# include +# include +# include + +# include +# include +# include +# include +# include +# include +#endif + +#if !defined(HCL_DEFAULT_PFMODPREFIX) +# if defined(_WIN32) +# define HCL_DEFAULT_PFMODPREFIX "hcl-" +# elif defined(__OS2__) +# define HCL_DEFAULT_PFMODPREFIX "hcl" +# elif defined(__DOS__) +# define HCL_DEFAULT_PFMODPREFIX "hcl" +# else +# define HCL_DEFAULT_PFMODPREFIX "libhcl-" +# endif +#endif + +#if !defined(HCL_DEFAULT_PFMODPOSTFIX) +# if defined(_WIN32) +# define HCL_DEFAULT_PFMODPOSTFIX "" +# elif defined(__OS2__) +# define HCL_DEFAULT_PFMODPOSTFIX "" +# elif defined(__DOS__) +# define HCL_DEFAULT_PFMODPOSTFIX "" +# else +# if defined(USE_DLFCN) +# define HCL_DEFAULT_PFMODPOSTFIX ".so" +# else +# define HCL_DEFAULT_PFMODPOSTFIX "" +# endif +# endif +#endif + +typedef union sck_addr_t sck_addr_t; +union sck_addr_t +{ + struct sockaddr_in in4; + struct sockaddr_in6 in6; +}; + +typedef struct bb_t bb_t; +struct bb_t +{ + char buf[1024]; + hcl_oow_t pos; + hcl_oow_t len; + int fd; + hcl_bch_t* fn; +}; + +typedef struct proto_t proto_t; +typedef struct client_t client_t; +typedef struct server_t server_t; + +typedef struct xtn_t xtn_t; +struct xtn_t +{ + proto_t* proto; + int vm_running; + + int logfd; + int logmask; + int logfd_istty; +}; + +enum proto_token_type_t +{ + PROTO_TOKEN_EOF, + PROTO_TOKEN_NL, + + PROTO_TOKEN_BEGIN, + PROTO_TOKEN_END, + PROTO_TOKEN_SCRIPT, + PROTO_TOKEN_EXIT, + + PROTO_TOKEN_OK, + PROTO_TOKEN_ERROR, + PROTO_TOKEN_LENGTH, + PROTO_TOKEN_ENCODING, + + PROTO_TOKEN_IDENT +}; + +typedef enum proto_token_type_t proto_token_type_t; + +typedef struct proto_token_t proto_token_t; +struct proto_token_t +{ + proto_token_type_t type; + hcl_ooch_t* ptr; + hcl_oow_t len; + hcl_oow_t capa; + hcl_ioloc_t loc; +}; + +enum proto_req_state_t +{ + PROTO_REQ_IN_TOP_LEVEL, + PROTO_REQ_IN_BLOCK_LEVEL +}; + +#define PROTO_REPLY_BUF_SIZE 1300 + +enum proto_reply_type_t +{ + PROTO_REPLY_SIMPLE = 0, + PROTO_REPLY_CHUNKED +}; +typedef enum proto_reply_type_t proto_reply_type_t; + +struct proto_t +{ + client_t* client; + + hcl_t* hcl; + hcl_iolxc_t* lxc; + proto_token_t tok; + + struct + { + int state; + } req; + + struct + { + proto_reply_type_t type; + hcl_oow_t nchunks; + hcl_bch_t buf[PROTO_REPLY_BUF_SIZE]; + hcl_oow_t len; + + int inject_nl; /* inject nl before the chunk length */ + } reply; +}; + +enum client_state_t +{ + CLIENT_STATE_DEAD = 0, + CLIENT_STATE_ALIVE = 1 +}; +typedef enum client_state_t client_state_t; + +struct client_t +{ + pthread_t thr; + int sck; + /* TODO: peer address */ + + time_t time_created; + client_state_t state; + + proto_t* proto; + + server_t* server; + client_t* prev_client; + client_t* next_client; +}; + +struct server_t +{ + int stopreq; + + struct + { + hcl_oow_t memsize; /* hcl heap memory size */ + unsigned int dbgopt; + int large_pages; + const char* logopt; + } cfg; + + struct + { + client_t* head; + client_t* tail; + } client_list[2]; + + pthread_mutex_t client_mutex; + pthread_mutex_t log_mutex; +}; + + +int proto_feed_reply (proto_t* proto, const hcl_ooch_t* ptr, hcl_oow_t len, int escape); + +/* ========================================================================= */ + +#define MB_1 (256UL*1024*1024) + +static void* sys_alloc (hcl_mmgr_t* mmgr, hcl_oow_t size) +{ + return malloc(size); +} + +static void* sys_realloc (hcl_mmgr_t* mmgr, void* ptr, hcl_oow_t size) +{ + return realloc(ptr, size); +} + +static void sys_free (hcl_mmgr_t* mmgr, void* ptr) +{ + free (ptr); +} + +static hcl_mmgr_t sys_mmgr = +{ + sys_alloc, + sys_realloc, + sys_free, + HCL_NULL +}; + +/* ========================================================================= */ + + +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) +# define IS_PATH_SEP(c) ((c) == '/' || (c) == '\\') +#else +# define IS_PATH_SEP(c) ((c) == '/') +#endif + + +static const hcl_bch_t* get_base_name (const hcl_bch_t* path) +{ + const hcl_bch_t* p, * last = HCL_NULL; + + for (p = path; *p != '\0'; p++) + { + if (IS_PATH_SEP(*p)) last = p; + } + + return (last == HCL_NULL)? path: (last + 1); +} + +static HCL_INLINE int open_input (hcl_t* hcl, hcl_ioinarg_t* arg) +{ + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + bb_t* bb = HCL_NULL; + +/* TOOD: support predefined include directory as well */ + if (arg->includer) + { + /* includee */ + hcl_oow_t ucslen, bcslen, parlen; + const hcl_bch_t* fn, * fb; + + #if defined(HCL_OOCH_IS_UCH) + if (hcl_convootobcstr (hcl, arg->name, &ucslen, HCL_NULL, &bcslen) <= -1) goto oops; + #else + bcslen = hcl_countbcstr (arg->name); + #endif + + fn = ((bb_t*)arg->includer->handle)->fn; + + fb = get_base_name (fn); + parlen = fb - fn; + + bb = (bb_t*)hcl_callocmem (hcl, HCL_SIZEOF(*bb) + (HCL_SIZEOF(hcl_bch_t) * (parlen + bcslen + 1))); + if (!bb) goto oops; + + bb->fn = (hcl_bch_t*)(bb + 1); + hcl_copybchars (bb->fn, fn, parlen); + #if defined(HCL_OOCH_IS_UCH) + hcl_convootobcstr (hcl, arg->name, &ucslen, &bb->fn[parlen], &bcslen); + #else + hcl_copybcstr (&bb->fn[parlen], bcslen + 1, arg->name); + #endif + + bb->fd = open(bb->fn, O_RDONLY, 0); + } + else + { + /* main stream */ + hcl_oow_t pathlen = 0; + bb = (bb_t*)hcl_callocmem (hcl, HCL_SIZEOF(*bb) + (HCL_SIZEOF(hcl_bch_t) * (pathlen + 1))); + if (!bb) goto oops; + + /*bb->fn = (hcl_bch_t*)(bb + 1); + hcl_copybcstr (bb->fn, pathlen + 1, "");*/ + + bb->fd = xtn->proto->client->sck; + } + if (bb->fd <= -1) + { + hcl_seterrnum (hcl, HCL_EIOERR); + goto oops; + } + + arg->handle = bb; + return 0; + +oops: + if (bb) + { + if (bb->fd >= 0 && bb->fd != xtn->proto->client->sck) close (bb->fd); + hcl_freemem (hcl, bb); + } + return -1; +} + +static HCL_INLINE int close_input (hcl_t* hcl, hcl_ioinarg_t* arg) +{ + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + bb_t* bb; + + bb = (bb_t*)arg->handle; + HCL_ASSERT (hcl, bb != HCL_NULL && bb->fd >= 0); + + if (bb->fd != xtn->proto->client->sck) close (bb->fd); + hcl_freemem (hcl, bb); + + arg->handle = HCL_NULL; + return 0; +} + + +static HCL_INLINE int read_input (hcl_t* hcl, hcl_ioinarg_t* arg) +{ + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + bb_t* bb; + hcl_oow_t bcslen, ucslen, remlen; + int x; + + bb = (bb_t*)arg->handle; + HCL_ASSERT (hcl, bb != HCL_NULL && bb->fd >= 0); + + if (bb->fd == xtn->proto->client->sck) + { + ssize_t x; + +/* TOOD: timeout, etc */ + x = recv (bb->fd, &bb->buf[bb->len], HCL_COUNTOF(bb->buf) - bb->len, 0); + if (x <= -1) + { + hcl_seterrnum (hcl, HCL_EIOERR); + return -1; + } + + bb->len += x; + } + else + { + ssize_t x; + x = read(bb->fd, &bb->buf[bb->len], HCL_COUNTOF(bb->buf) - bb->len); + if (x <= -1) + { + hcl_seterrnum (hcl, HCL_EIOERR); + return -1; + } + + bb->len += x; + } + +#if defined(HCL_OOCH_IS_UCH) + bcslen = bb->len; + ucslen = HCL_COUNTOF(arg->buf); + x = hcl_convbtooochars (hcl, bb->buf, &bcslen, arg->buf, &ucslen); + if (x <= -1 && ucslen <= 0) return -1; + /* if ucslen is greater than 0, i see that some characters have been + * converted properly */ +#else + bcslen = (bb->len < HCL_COUNTOF(arg->buf))? bb->len: HCL_COUNTOF(arg->buf); + ucslen = bcslen; + hcl_copybchars (arg->buf, bb->buf, bcslen); +#endif + + remlen = bb->len - bcslen; + if (remlen > 0) memmove (bb->buf, &bb->buf[bcslen], remlen); + bb->len = remlen; + + arg->xlen = ucslen; + return 0; +} + + +static int read_handler (hcl_t* hcl, hcl_iocmd_t cmd, void* arg) +{ + switch (cmd) + { + case HCL_IO_OPEN: + return open_input (hcl, (hcl_ioinarg_t*)arg); + + case HCL_IO_CLOSE: + return close_input (hcl, (hcl_ioinarg_t*)arg); + + case HCL_IO_READ: + return read_input (hcl, (hcl_ioinarg_t*)arg); + + default: + hcl_seterrnum (hcl, HCL_EINTERN); + return -1; + } +} + +static int print_handler (hcl_t* hcl, hcl_iocmd_t cmd, void* arg) +{ + switch (cmd) + { + case HCL_IO_OPEN: + return 0; + + case HCL_IO_CLOSE: + return 0; + + case HCL_IO_WRITE: + { + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + hcl_iooutarg_t* outarg = (hcl_iooutarg_t*)arg; + + if (proto_feed_reply(xtn->proto, outarg->ptr, outarg->len, 0) <= -1) + { + /* TODO: change error code and message. propagage the errormessage from proto */ + hcl_seterrbfmt (hcl, HCL_ESYSERR, "failed to write message via proto"); + return -1; + } + outarg->xlen = outarg->len; + return 0; + } + + default: + hcl_seterrnum (hcl, HCL_EINTERN); + return -1; + } +} + +/* ========================================================================= */ + +static void* alloc_heap (hcl_t* hcl, hcl_oow_t size) +{ +#if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) && defined(MAP_ANONYMOUS) + /* It's called via hcl_makeheap() when HCL creates a GC heap. + * The heap is large in size. I can use a different memory allocation + * function instead of an ordinary malloc. + * upon failure, it doesn't require to set error information as hcl_makeheap() + * set the error number to HCL_EOOMEM. */ + +#if !defined(MAP_HUGETLB) && (defined(__amd64__) || defined(__x86_64__)) +# define MAP_HUGETLB 0x40000 +#endif + + hcl_oow_t* ptr; + int flags; + hcl_oow_t actual_size; + + flags = MAP_PRIVATE | MAP_ANONYMOUS; + + #if defined(MAP_HUGETLB) + flags |= MAP_HUGETLB; + #endif + + #if defined(MAP_UNINITIALIZED) + flags |= MAP_UNINITIALIZED; + #endif + + actual_size = HCL_SIZEOF(hcl_oow_t) + size; + actual_size = HCL_ALIGN_POW2(actual_size, 2 * 1024 * 1024); + ptr = (hcl_oow_t*)mmap(NULL, actual_size, PROT_READ | PROT_WRITE, flags, -1, 0); + if (ptr == MAP_FAILED) + { + #if defined(MAP_HUGETLB) + flags &= ~MAP_HUGETLB; + ptr = (hcl_oow_t*)mmap(NULL, actual_size, PROT_READ | PROT_WRITE, flags, -1, 0); + if (ptr == MAP_FAILED) return HCL_NULL; + #else + return HCL_NULL; + #endif + } + *ptr = actual_size; + + return (void*)(ptr + 1); + +#else + return HCL_MMGR_ALLOC(hcl->mmgr, size); +#endif +} + +static void free_heap (hcl_t* hcl, void* ptr) +{ +#if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) + hcl_oow_t* actual_ptr; + actual_ptr = (hcl_oow_t*)ptr - 1; + munmap (actual_ptr, *actual_ptr); +#else + return HCL_MMGR_FREE(hcl->mmgr, ptr); +#endif +} + +static int write_all (int fd, const char* ptr, hcl_oow_t len) +{ + while (len > 0) + { + hcl_ooi_t wr; + + wr = write (fd, ptr, len); + + if (wr <= -1) + { + #if defined(EAGAIN) && defined(EWOULDBLOCK) && (EAGAIN == EWOULDBLOCK) + if (errno == EAGAIN) continue; + #else + # if defined(EAGAIN) + if (errno == EAGAIN) continue; + #elif defined(EWOULDBLOCK) + if (errno == EWOULDBLOCK) continue; + #endif + #endif + + #if defined(EINTR) + /* TODO: would this interfere with non-blocking nature of this VM? */ + if (errno == EINTR) continue; + #endif + return -1; + } + + ptr += wr; + len -= wr; + } + + return 0; +} + +static HCL_INLINE void __log_write (hcl_t* hcl, int mask, const hcl_ooch_t* msg, hcl_oow_t len) +{ + hcl_bch_t buf[256]; + hcl_oow_t ucslen, bcslen, msgidx; + int n; + + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + int logfd; + + if (mask & HCL_LOG_STDERR) + { + /* the messages that go to STDERR don't get masked out */ + logfd = 2; + } + else + { + if (!(xtn->logmask & mask & ~HCL_LOG_ALL_LEVELS)) return; /* check log types */ + if (!(xtn->logmask & mask & ~HCL_LOG_ALL_TYPES)) return; /* check log levels */ + + if (mask & HCL_LOG_STDOUT) logfd = 1; + else + { + logfd = xtn->logfd; + if (logfd <= -1) return; + } + } + +/* TODO: beautify the log message. + * do classification based on mask. */ + if (!(mask & (HCL_LOG_STDOUT | HCL_LOG_STDERR))) + { + time_t now; + char ts[32]; + size_t tslen; + struct tm tm, *tmp; + + now = time(NULL); + + tmp = localtime_r (&now, &tm); + #if defined(HAVE_STRFTIME_SMALL_Z) + tslen = strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S %z ", tmp); + #else + tslen = strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S %Z ", tmp); + #endif + if (tslen == 0) + { + strcpy (ts, "0000-00-00 00:00:00 +0000"); + tslen = 25; + } + +/* TODO: less write system calls by having a buffer */ + write_all (logfd, ts, tslen); + + tslen = snprintf (ts, sizeof(ts), "[%d] ", xtn->proto->client->sck); + write_all (logfd, ts, tslen); + } + + if (xtn->logfd_istty) + { + if (mask & HCL_LOG_FATAL) write_all (logfd, "\x1B[1;31m", 7); + else if (mask & HCL_LOG_ERROR) write_all (logfd, "\x1B[1;32m", 7); + else if (mask & HCL_LOG_WARN) write_all (logfd, "\x1B[1;33m", 7); + } + +#if defined(HCL_OOCH_IS_UCH) + msgidx = 0; + while (len > 0) + { + ucslen = len; + bcslen = HCL_COUNTOF(buf); + + n = hcl_convootobchars (hcl, &msg[msgidx], &ucslen, buf, &bcslen); + if (n == 0 || n == -2) + { + /* n = 0: + * converted all successfully + * n == -2: + * buffer not sufficient. not all got converted yet. + * write what have been converted this round. */ + + HCL_ASSERT (hcl, ucslen > 0); /* if this fails, the buffer size must be increased */ + + /* attempt to write all converted characters */ + if (write_all (logfd, buf, bcslen) <= -1) break; + + if (n == 0) break; + else + { + msgidx += ucslen; + len -= ucslen; + } + } + else if (n <= -1) + { + /* conversion error */ + break; + } + } +#else + write_all (logfd, msg, len); +#endif + + if (xtn->logfd_istty) + { + if (mask & (HCL_LOG_FATAL | HCL_LOG_ERROR | HCL_LOG_WARN)) write_all (logfd, "\x1B[0m", 4); + } +} + +static void log_write (hcl_t* hcl, int mask, const hcl_ooch_t* msg, hcl_oow_t len) +{ + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + pthread_mutex_lock (&xtn->proto->client->server->log_mutex); + __log_write (hcl, mask, msg, len); + pthread_mutex_unlock (&xtn->proto->client->server->log_mutex); +} + +static void syserrstrb (hcl_t* hcl, int syserr, hcl_bch_t* buf, hcl_oow_t len) +{ +#if defined(HAVE_STRERROR_R) + strerror_r (syserr, buf, len); +#else + /* this is not thread safe */ + hcl_copybcstr (buf, len, strerror(syserr)); +#endif +} + +/* ========================================================================= */ + +static void* dl_open (hcl_t* hcl, const hcl_ooch_t* name, int flags) +{ +#if defined(USE_LTDL) || defined(USE_DLFCN) + hcl_bch_t stabuf[128], * bufptr; + hcl_oow_t ucslen, bcslen, bufcapa; + void* handle; + + #if defined(HCL_OOCH_IS_UCH) + if (hcl_convootobcstr(hcl, name, &ucslen, HCL_NULL, &bufcapa) <= -1) return HCL_NULL; + /* +1 for terminating null. but it's not needed because HCL_COUNTOF(HCL_DEFAULT_PFMODPREFIX) + * and HCL_COUNTOF(HCL_DEFAULT_PFMODPOSTIFX) include the terminating nulls. Never mind about + * the extra 2 characters. */ + #else + bufcapa = hcl_countbcstr(name); + #endif + bufcapa += HCL_COUNTOF(HCL_DEFAULT_PFMODPREFIX) + HCL_COUNTOF(HCL_DEFAULT_PFMODPOSTFIX) + 1; + + if (bufcapa <= HCL_COUNTOF(stabuf)) bufptr = stabuf; + else + { + bufptr = (hcl_bch_t*)hcl_allocmem(hcl, bufcapa * HCL_SIZEOF(*bufptr)); + if (!bufptr) return HCL_NULL; + } + + if (flags & HCL_VMPRIM_OPENDL_PFMOD) + { + hcl_oow_t len, i, xlen; + + /* opening a primitive function module - mostly libhcl-xxxx */ + len = hcl_copybcstr(bufptr, bufcapa, HCL_DEFAULT_PFMODPREFIX); + + bcslen = bufcapa - len; + #if defined(HCL_OOCH_IS_UCH) + hcl_convootobcstr(hcl, name, &ucslen, &bufptr[len], &bcslen); + #else + bcslen = hcl_copybcstr(&bufptr[len], bcslen, name); + #endif + + /* length including the prefix and the name. but excluding the postfix */ + xlen = len + bcslen; + + for (i = len; i < xlen; i++) + { + /* convert a period(.) to a dash(-) */ + if (bufptr[i] == '.') bufptr[i] = '-'; + } + + retry: + hcl_copybcstr (&bufptr[xlen], bufcapa - xlen, HCL_DEFAULT_PFMODPOSTFIX); + + /* both prefix and postfix attached. for instance, libhcl-xxx */ + handle = sys_dl_openext(bufptr); + if (!handle) + { + HCL_DEBUG3 (hcl, "Failed to open(ext) DL %hs[%js] - %hs\n", bufptr, name, sys_dl_error()); + + /* try without prefix and postfix */ + bufptr[xlen] = '\0'; + handle = sys_dl_openext(&bufptr[len]); + if (!handle) + { + hcl_bch_t* dash; + const hcl_bch_t* dl_errstr; + dl_errstr = sys_dl_error(); + HCL_DEBUG3 (hcl, "Failed to open(ext) DL %hs[%js] - %hs\n", &bufptr[len], name, dl_errstr); + hcl_seterrbfmt (hcl, HCL_ESYSERR, "unable to open(ext) DL %js - %hs", name, dl_errstr); + + dash = hcl_rfindbchar(bufptr, hcl_countbcstr(bufptr), '-'); + if (dash) + { + /* remove a segment at the back. + * [NOTE] a dash contained in the original name before + * period-to-dash transformation may cause extraneous/wrong + * loading reattempts. */ + xlen = dash - bufptr; + goto retry; + } + } + else + { + HCL_DEBUG3 (hcl, "Opened(ext) DL %hs[%js] handle %p\n", &bufptr[len], name, handle); + } + } + else + { + HCL_DEBUG3 (hcl, "Opened(ext) DL %hs[%js] handle %p\n", bufptr, name, handle); + } + } + else + { + /* opening a raw shared object without a prefix and/or a postfix */ + #if defined(HCL_OOCH_IS_UCH) + bcslen = bufcapa; + hcl_convootobcstr(hcl, name, &ucslen, bufptr, &bcslen); + #else + bcslen = hcl_copybcstr(bufptr, bufcapa, name); + #endif + + if (hcl_findbchar(bufptr, bcslen, '.')) + { + handle = sys_dl_open(bufptr); + if (!handle) + { + const hcl_bch_t* dl_errstr; + dl_errstr = sys_dl_error(); + HCL_DEBUG2 (hcl, "Failed to open DL %hs - %hs\n", bufptr, dl_errstr); + hcl_seterrbfmt (hcl, HCL_ESYSERR, "unable to open DL %js - %hs", name, dl_errstr); + } + else HCL_DEBUG2 (hcl, "Opened DL %hs handle %p\n", bufptr, handle); + } + else + { + handle = sys_dl_openext(bufptr); + if (!handle) + { + const hcl_bch_t* dl_errstr; + dl_errstr = sys_dl_error(); + HCL_DEBUG2 (hcl, "Failed to open(ext) DL %hs - %s\n", bufptr, dl_errstr); + hcl_seterrbfmt (hcl, HCL_ESYSERR, "unable to open(ext) DL %js - %hs", name, dl_errstr); + } + else HCL_DEBUG2 (hcl, "Opened(ext) DL %hs handle %p\n", bufptr, handle); + } + } + + if (bufptr != stabuf) hcl_freemem (hcl, bufptr); + return handle; + +#else + +/* TODO: support various platforms */ + /* TODO: implemenent this */ + HCL_DEBUG1 (hcl, "Dynamic loading not implemented - cannot open %js\n", name); + hcl_seterrnum (hcl, HCL_ENOIMPL, "dynamic loading not implemented - cannot open %js", name); + return HCL_NULL; +#endif +} + +static void dl_close (hcl_t* hcl, void* handle) +{ +#if defined(USE_LTDL) || defined(USE_DLFCN) + HCL_DEBUG1 (hcl, "Closed DL handle %p\n", handle); + sys_dl_close (handle); + +#else + /* TODO: implemenent this */ + HCL_DEBUG1 (hcl, "Dynamic loading not implemented - cannot close handle %p\n", handle); +#endif +} + +static void* dl_getsym (hcl_t* hcl, void* handle, const hcl_ooch_t* name) +{ +#if defined(USE_LTDL) || defined(USE_DLFCN) + hcl_bch_t stabuf[64], * bufptr; + hcl_oow_t bufcapa, ucslen, bcslen, i; + const hcl_bch_t* symname; + void* sym; + + #if defined(HCL_OOCH_IS_UCH) + if (hcl_convootobcstr(hcl, name, &ucslen, HCL_NULL, &bcslen) <= -1) return HCL_NULL; + #else + bcslen = hcl_countbcstr (name); + #endif + + if (bcslen >= HCL_COUNTOF(stabuf) - 2) + { + bufcapa = bcslen + 3; + bufptr = (hcl_bch_t*)hcl_allocmem(hcl, bufcapa * HCL_SIZEOF(*bufptr)); + if (!bufptr) return HCL_NULL; + } + else + { + bufcapa = HCL_COUNTOF(stabuf); + bufptr = stabuf; + } + + bcslen = bufcapa - 1; + #if defined(HCL_OOCH_IS_UCH) + hcl_convootobcstr (hcl, name, &ucslen, &bufptr[1], &bcslen); + #else + bcslen = hcl_copybcstr(&bufptr[1], bcslen, name); + #endif + + /* convert a period(.) to an underscore(_) */ + for (i = 1; i <= bcslen; i++) if (bufptr[i] == '.') bufptr[i] = '_'; + + symname = &bufptr[1]; /* try the name as it is */ + + sym = sys_dl_getsym(handle, symname); + if (!sym) + { + bufptr[0] = '_'; + symname = &bufptr[0]; /* try _name */ + sym = sys_dl_getsym(handle, symname); + if (!sym) + { + bufptr[bcslen + 1] = '_'; + bufptr[bcslen + 2] = '\0'; + + symname = &bufptr[1]; /* try name_ */ + sym = sys_dl_getsym(handle, symname); + + if (!sym) + { + symname = &bufptr[0]; /* try _name_ */ + sym = sys_dl_getsym(handle, symname); + if (!sym) + { + const hcl_bch_t* dl_errstr; + dl_errstr = sys_dl_error(); + HCL_DEBUG3 (hcl, "Failed to get module symbol %js from handle %p - %hs\n", name, handle, dl_errstr); + hcl_seterrbfmt (hcl, HCL_ENOENT, "unable to get module symbol %hs - %hs", symname, dl_errstr); + + } + } + } + } + + if (sym) HCL_DEBUG3 (hcl, "Loaded module symbol %js from handle %p - %hs\n", name, handle, symname); + if (bufptr != stabuf) hcl_freemem (hcl, bufptr); + return sym; + +#else + /* TODO: IMPLEMENT THIS */ + HCL_DEBUG2 (hcl, "Dynamic loading not implemented - Cannot load module symbol %js from handle %p\n", name, handle); + hcl_seterrbfmt (hcl, HCL_ENOIMPL, "dynamic loading not implemented - Cannot load module symbol %js from handle %p", name, handle); + return HCL_NULL; +#endif +} + +/* ========================================================================= */ + +static void vm_gettime (hcl_t* hcl, hcl_ntime_t* now) +{ +#if defined(_WIN32) + /* TODO: */ +#elif defined(__OS2__) + ULONG out; + +/* TODO: handle overflow?? */ +/* TODO: use DosTmrQueryTime() and DosTmrQueryFreq()? */ + DosQuerySysInfo (QSV_MS_COUNT, QSV_MS_COUNT, &out, HCL_SIZEOF(out)); /* milliseconds */ + /* it must return NO_ERROR */ + HCL_INITNTIME (now, HCL_MSEC_TO_SEC(out), HCL_MSEC_TO_NSEC(out)); +#elif defined(__DOS__) && (defined(_INTELC32_) || defined(__WATCOMC__)) + clock_t c; + +/* TODO: handle overflow?? */ + c = clock (); + now->sec = c / CLOCKS_PER_SEC; + #if (CLOCKS_PER_SEC == 100) + now->nsec = HCL_MSEC_TO_NSEC((c % CLOCKS_PER_SEC) * 10); + #elif (CLOCKS_PER_SEC == 1000) + now->nsec = HCL_MSEC_TO_NSEC(c % CLOCKS_PER_SEC); + #elif (CLOCKS_PER_SEC == 1000000L) + now->nsec = HCL_USEC_TO_NSEC(c % CLOCKS_PER_SEC); + #elif (CLOCKS_PER_SEC == 1000000000L) + now->nsec = (c % CLOCKS_PER_SEC); + #else + # error UNSUPPORTED CLOCKS_PER_SEC + #endif +#elif defined(macintosh) + UnsignedWide tick; + hcl_uint64_t tick64; + Microseconds (&tick); + tick64 = *(hcl_uint64_t*)&tick; + HCL_INITNTIME (now, HCL_USEC_TO_SEC(tick64), HCL_USEC_TO_NSEC(tick64)); +#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + struct timespec ts; + clock_gettime (CLOCK_MONOTONIC, &ts); + HCL_INITNTIME(now, ts.tv_sec, ts.tv_nsec); +#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME) + struct timespec ts; + clock_gettime (CLOCK_REALTIME, &ts); + HCL_INITNTIME(now, ts.tv_sec, ts.tv_nsec); +#else + struct timeval tv; + gettimeofday (&tv, HCL_NULL); + HCL_INITNTIME(now, tv.tv_sec, HCL_USEC_TO_NSEC(tv.tv_usec)); +#endif +} + +static void vm_sleep (hcl_t* hcl, const hcl_ntime_t* dur) +{ +#if defined(_WIN32) + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + if (xtn->waitable_timer) + { + LARGE_INTEGER li; + li.QuadPart = -HCL_SECNSEC_TO_NSEC(dur->sec, dur->nsec); + if(SetWaitableTimer(timer, &li, 0, HCL_NULL, HCL_NULL, FALSE) == FALSE) goto normal_sleep; + WaitForSingleObject(timer, INFINITE); + } + else + { + normal_sleep: + /* fallback to normal Sleep() */ + Sleep (HCL_SECNSEC_TO_MSEC(dur->sec,dur->nsec)); + } +#elif defined(__OS2__) + + /* TODO: in gui mode, this is not a desirable method??? + * this must be made event-driven coupled with the main event loop */ + DosSleep (HCL_SECNSEC_TO_MSEC(dur->sec,dur->nsec)); + +#elif defined(macintosh) + + /* TODO: ... */ + +#elif defined(__DOS__) && (defined(_INTELC32_) || defined(__WATCOMC__)) + + clock_t c; + + c = clock (); + c += dur->sec * CLOCKS_PER_SEC; + + #if (CLOCKS_PER_SEC == 100) + c += HCL_NSEC_TO_MSEC(dur->nsec) / 10; + #elif (CLOCKS_PER_SEC == 1000) + c += HCL_NSEC_TO_MSEC(dur->nsec); + #elif (CLOCKS_PER_SEC == 1000000L) + c += HCL_NSEC_TO_USEC(dur->nsec); + #elif (CLOCKS_PER_SEC == 1000000000L) + c += dur->nsec; + #else + # error UNSUPPORTED CLOCKS_PER_SEC + #endif + +/* TODO: handle clock overvlow */ +/* TODO: check if there is abortion request or interrupt */ + while (c > clock()) + { + _halt_cpu(); + } + +#else + #if defined(USE_THREAD) + /* the sleep callback is called only if there is no IO semaphore + * waiting. so i can safely call vm_muxwait() without a muxwait callback + * when USE_THREAD is true */ + vm_muxwait (hcl, dur, HCL_NULL); + #elif defined(HAVE_NANOSLEEP) + struct timespec ts; + ts.tv_sec = dur->sec; + ts.tv_nsec = dur->nsec; + nanosleep (&ts, HCL_NULL); + #elif defined(HAVE_USLEEP) + usleep (HCL_SECNSEC_TO_USEC(dur->sec, dur->nsec)); + #else + # error UNSUPPORT SLEEP + #endif +#endif +} + +/* ========================================================================= */ + +static int vm_startup (hcl_t* hcl) +{ + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + xtn->vm_running = 1; + return 0; +} + +static void vm_cleanup (hcl_t* hcl) +{ + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + xtn->vm_running = 0; +} + +static void vm_checkbc (hcl_t* hcl, hcl_oob_t bcode) +{ + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + + /* TODO: check how to this vm has been running. too long? abort it */ + + /* TODO: check if the client connection is ok? if not, abort it */ + +} + +/* +static void gc_hcl (hcl_t* hcl) +{ +} +*/ + +static void fini_hcl (hcl_t* hcl) +{ + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + if (xtn->logfd >= 0) + { + close (xtn->logfd); + xtn->logfd = -1; + xtn->logfd_istty = 0; + } +} + +/* ========================================================================= */ + +static int handle_logopt (hcl_t* hcl, const hcl_bch_t* str) +{ + xtn_t* xtn = (xtn_t*)hcl_getxtn (hcl); + hcl_bch_t* xstr = (hcl_bch_t*)str; + hcl_bch_t* cm, * flt; + + cm = hcl_findbcharinbcstr (xstr, ','); + if (cm) + { + /* i duplicate this string for open() below as open() doesn't + * accept a length-bounded string */ + xstr = hcl_dupbchars (hcl, str, hcl_countbcstr(str)); + if (!xstr) + { + fprintf (stderr, "ERROR: out of memory in duplicating %s\n", str); + return -1; + } + + cm = hcl_findbcharinbcstr(xstr, ','); + *cm = '\0'; + + do + { + flt = cm + 1; + + cm = hcl_findbcharinbcstr(flt, ','); + if (cm) *cm = '\0'; + + if (hcl_compbcstr(flt, "app") == 0) xtn->logmask |= HCL_LOG_APP; + else if (hcl_compbcstr(flt, "compiler") == 0) xtn->logmask |= HCL_LOG_COMPILER; + else if (hcl_compbcstr(flt, "vm") == 0) xtn->logmask |= HCL_LOG_VM; + else if (hcl_compbcstr(flt, "mnemonic") == 0) xtn->logmask |= HCL_LOG_MNEMONIC; + else if (hcl_compbcstr(flt, "gc") == 0) xtn->logmask |= HCL_LOG_GC; + else if (hcl_compbcstr(flt, "ic") == 0) xtn->logmask |= HCL_LOG_IC; + else if (hcl_compbcstr(flt, "primitive") == 0) xtn->logmask |= HCL_LOG_PRIMITIVE; + + else if (hcl_compbcstr(flt, "fatal") == 0) xtn->logmask |= HCL_LOG_FATAL; + else if (hcl_compbcstr(flt, "error") == 0) xtn->logmask |= HCL_LOG_ERROR; + else if (hcl_compbcstr(flt, "warn") == 0) xtn->logmask |= HCL_LOG_WARN; + else if (hcl_compbcstr(flt, "info") == 0) xtn->logmask |= HCL_LOG_INFO; + else if (hcl_compbcstr(flt, "debug") == 0) xtn->logmask |= HCL_LOG_DEBUG; + + else if (hcl_compbcstr(flt, "fatal+") == 0) xtn->logmask |= HCL_LOG_FATAL; + else if (hcl_compbcstr(flt, "error+") == 0) xtn->logmask |= HCL_LOG_FATAL | HCL_LOG_ERROR; + else if (hcl_compbcstr(flt, "warn+") == 0) xtn->logmask |= HCL_LOG_FATAL | HCL_LOG_ERROR | HCL_LOG_WARN; + else if (hcl_compbcstr(flt, "info+") == 0) xtn->logmask |= HCL_LOG_FATAL | HCL_LOG_ERROR | HCL_LOG_WARN | HCL_LOG_INFO; + else if (hcl_compbcstr(flt, "debug+") == 0) xtn->logmask |= HCL_LOG_FATAL | HCL_LOG_ERROR | HCL_LOG_WARN | HCL_LOG_INFO | HCL_LOG_DEBUG; + + else + { + fprintf (stderr, "ERROR: unknown log option value - %s\n", flt); + if (str != xstr) hcl_freemem (hcl, xstr); + return -1; + } + } + while (cm); + + + if (!(xtn->logmask & HCL_LOG_ALL_TYPES)) xtn->logmask |= HCL_LOG_ALL_TYPES; /* no types specified. force to all types */ + if (!(xtn->logmask & HCL_LOG_ALL_LEVELS)) xtn->logmask |= HCL_LOG_ALL_LEVELS; /* no levels specified. force to all levels */ + } + else + { + xtn->logmask = HCL_LOG_ALL_LEVELS | HCL_LOG_ALL_TYPES; + } + + xtn->logfd = open (xstr, O_CREAT | O_WRONLY | O_APPEND , 0644); + if (xtn->logfd == -1) + { + fprintf (stderr, "ERROR: cannot open a log file %s\n", xstr); + if (str != xstr) hcl_freemem (hcl, xstr); + return -1; + } + +#if defined(HAVE_ISATTY) + xtn->logfd_istty = isatty(xtn->logfd); +#endif + + if (str != xstr) hcl_freemem (hcl, xstr); + return 0; +} + +#if defined(HCL_BUILD_DEBUG) +static int parse_dbgopt (const char* str, unsigned int* dbgoptp) +{ + const hcl_bch_t* cm, * flt; + hcl_oow_t len; + unsigned int dbgopt = 0; + + cm = str - 1; + do + { + flt = cm + 1; + + cm = strchr(flt, ','); + len = cm? (cm - flt): strlen(flt); + if (strncasecmp(flt, "gc", len) == 0) dbgopt |= HCL_DEBUG_GC; + else if (strncasecmp (flt, "bigint", len) == 0) dbgopt |= HCL_DEBUG_BIGINT; + else + { + fprintf (stderr, "ERROR: unknown debug option value - %.*s\n", (int)len, flt); + return -1; + } + } + while (cm); + + *dbgoptp = dbgopt; + return 0; +} +#endif +/* ========================================================================= */ + +static server_t* g_server = HCL_NULL; + +/* ========================================================================= */ + +typedef void (*signal_handler_t) (int, siginfo_t*, void*); + +static void handle_sigint (int sig, siginfo_t* siginfo, void* ctx) +{ + if (g_server) g_server->stopreq = 1; +} + +static void set_signal (int sig, signal_handler_t handler) +{ + struct sigaction sa; + + memset (&sa, 0, sizeof(sa)); + /*sa.sa_handler = handler;*/ + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = handler; + sigemptyset (&sa.sa_mask); + + sigaction (sig, &sa, NULL); +} + +static void set_signal_to_default (int sig) +{ + struct sigaction sa; + + memset (&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sa.sa_flags = 0; + sigemptyset (&sa.sa_mask); + + sigaction (sig, &sa, NULL); +} + +/* ========================================================================= */ + +proto_t* proto_open (hcl_oow_t xtnsize, client_t* client) +{ + proto_t* proto; + hcl_vmprim_t vmprim; + hcl_cb_t hclcb; + xtn_t* xtn; + unsigned int trait; + + memset (&vmprim, 0, HCL_SIZEOF(vmprim)); + if (client->server->cfg.large_pages) + { + vmprim.alloc_heap = alloc_heap; + vmprim.free_heap = free_heap; + } + vmprim.log_write = log_write; + vmprim.syserrstrb = syserrstrb; + vmprim.dl_open = dl_open; + vmprim.dl_close = dl_close; + vmprim.dl_getsym = dl_getsym; + vmprim.vm_gettime = vm_gettime; + vmprim.vm_sleep = vm_sleep; + + proto = (proto_t*)malloc(sizeof(*proto)); + if (!proto) return NULL; + + memset (proto, 0, sizeof(*proto)); + proto->client = client; + + proto->hcl = hcl_open(&sys_mmgr, HCL_SIZEOF(xtn_t), client->server->cfg.memsize, &vmprim, HCL_NULL); + if (!proto->hcl) goto oops; + + xtn = (xtn_t*)hcl_getxtn(proto->hcl); + xtn->logfd = -1; + xtn->logfd_istty = 0; + xtn->proto = proto; + + hcl_getoption (proto->hcl, HCL_TRAIT, &trait); + trait |= proto->client->server->cfg.dbgopt; + hcl_setoption (proto->hcl, HCL_TRAIT, &trait); + + if (proto->client->server->cfg.logopt && + handle_logopt(proto->hcl, proto->client->server->cfg.logopt) <= -1) goto oops; + + memset (&hclcb, 0, HCL_SIZEOF(hclcb)); + hclcb.fini = fini_hcl; + /*hclcb.gc = gc_hcl;*/ + hclcb.vm_startup = vm_startup; + hclcb.vm_cleanup = vm_cleanup; + hclcb.vm_checkbc = vm_checkbc; + hcl_regcb (proto->hcl, &hclcb); + + if (hcl_ignite(proto->hcl) <= -1) goto oops; + if (hcl_addbuiltinprims(proto->hcl) <= -1) goto oops; + + if (hcl_attachio(proto->hcl, read_handler, print_handler) <= -1) goto oops; + return proto; + +oops: + if (proto) + { + if (proto->hcl) hcl_close (proto->hcl); + free (proto); + } + return NULL; +} + +void proto_close (proto_t* proto) +{ + if (proto->tok.ptr) free (proto->tok.ptr); + hcl_close (proto->hcl); + free (proto); +} + +static int write_reply_chunk (proto_t* proto) +{ + struct msghdr msg; + struct iovec iov[3]; + hcl_bch_t cl[16]; /* ensure that this is large enough for the chunk length string */ + int index = 0, count = 0; + + if (proto->reply.type == PROTO_REPLY_CHUNKED) + { + if (proto->reply.nchunks <= 0) + { + /* this is the first chunk */ + iov[count].iov_base = ".OK\n.ENCODING chunked\n"; + iov[count++].iov_len = 22; + } + + iov[count].iov_base = cl, + iov[count++].iov_len = snprintf(cl, sizeof(cl), "%s%zu:", ((proto->reply.inject_nl && proto->reply.nchunks > 0)? "\n": ""), proto->reply.len); + } + iov[count].iov_base = proto->reply.buf; + iov[count++].iov_len = proto->reply.len; + + while (1) + { + ssize_t nwritten; + + memset (&msg, 0, sizeof(msg)); + msg.msg_iov = (struct iovec*)&iov[index]; + msg.msg_iovlen = count - index; + nwritten = sendmsg(proto->client->sck, &msg, 0); + /*nwritten = writev(proto->client->sck, (const struct iovec*)&iov[index], count - index);*/ + if (nwritten <= -1) + { + fprintf (stderr, "sendmsg failure - %s\n", strerror(errno)); + return -1; + } + + while (index < count && (size_t)nwritten >= iov[index].iov_len) + nwritten -= iov[index++].iov_len; + + if (index == count) break; + + iov[index].iov_base = (void*)((hcl_uint8_t*)iov[index].iov_base + nwritten); + iov[index].iov_len -= nwritten; + } + + if (proto->reply.len <= 0) + { + /* this should be the last chunk */ + proto->reply.nchunks = 0; + } + else + { + proto->reply.nchunks++; + proto->reply.len = 0; + } + + return 0; +} + +void proto_start_reply (proto_t* proto) +{ + proto->reply.type = PROTO_REPLY_CHUNKED; + proto->reply.nchunks = 0; + proto->reply.len = 0; +} + +int proto_feed_reply (proto_t* proto, const hcl_ooch_t* ptr, hcl_oow_t len, int escape) +{ +#if defined(HCL_OOCH_IS_BCH) + /* nothing */ +#else + hcl_oow_t bcslen, ucslen, donelen; + int x; +#endif + +#if defined(HCL_OOCH_IS_BCH) + while (len > 0) + { + if (escape && (*ptr == '\\' || *ptr == '\"')) + { + if (proto->reply.len >= HCL_COUNTOF(proto->reply.buf) && write_reply_chunk(proto) <=-1) return -1; + proto->reply.buf[proto->reply.len++] = '\\'; + } + + if (proto->reply.len >= HCL_COUNTOF(proto->reply.buf) && write_reply_chunk(proto) <=-1) return -1; + proto->reply.buf[proto->reply.len++] = *ptr++; + len--; + } + + return 0; +#else + donelen = 0; + while (donelen < len) + { + if (escape && (*ptr == '\\' || *ptr == '\"')) + { + /* i know that these characters don't need conversion */ + if (proto->reply.len >= HCL_COUNTOF(proto->reply.buf) && write_reply_chunk(proto) <=-1) return -1; + proto->reply.buf[proto->reply.len++] = '\\'; + } + + bcslen = HCL_COUNTOF(proto->reply.buf) - proto->reply.len; + if (bcslen < HCL_BCSIZE_MAX) + { + if (write_reply_chunk(proto) <=-1) return -1; + bcslen = HCL_COUNTOF(proto->reply.buf) - proto->reply.len; + } + ucslen = len - donelen; + + x = hcl_convootobchars(proto->hcl, &ptr[donelen], &ucslen, &proto->reply.buf[proto->reply.len], &bcslen); + if (x <= -1 && ucslen <= 0) return -1; + + donelen += ucslen; + proto->reply.len += bcslen; + } +#endif + return 0; +} + +int proto_end_reply (proto_t* proto, const hcl_ooch_t* failmsg) +{ + HCL_ASSERT (proto->hcl, proto->reply.type == PROTO_REPLY_CHUNKED); + + if (failmsg) + { + if (proto->reply.nchunks <= 0 && proto->reply.len <= 0) + { + static hcl_ooch_t err1[] = { '.','E','R','R','O','R',' ','\"' }; + static hcl_ooch_t err2[] = { '\"','\n' }; + proto->reply.type = PROTO_REPLY_SIMPLE; /* switch to the simple mode forcibly */ + + simple_error: + if (proto_feed_reply(proto, err1, 8, 0) <= -1 || + proto_feed_reply(proto, failmsg, hcl_countoocstr(failmsg), 1) <= -1 || + proto_feed_reply(proto, err2, 2, 0) <= -1) return -1; + + if (write_reply_chunk(proto) <= -1) return -1; + } + else + { + /* some chunks have beed emitted. but at the end, an error has occurred. + * send -1: as the last chunk. the receiver must rub out the reply + * buffer received so far and expect the following .ERROR response */ + static hcl_ooch_t err0[] = { '-','1',':','\n' }; + if (proto->reply.len > 0 && write_reply_chunk(proto) <= -1) return -1; + + proto->reply.type = PROTO_REPLY_SIMPLE; /* switch to the simple mode forcibly */ + proto->reply.nchunks = 0; + proto->reply.len = 0; + + if (proto_feed_reply(proto, err0, 4, 0) <= -1) return -1; + goto simple_error; + } + } + else + { + if (proto->reply.nchunks <= 0 && proto->reply.len <= 0) + { + /* in the chunked mode. but no output has been made so far */ + static hcl_ooch_t ok[] = { '.','O','K',' ','\"','\"','\n' }; + proto->reply.type = PROTO_REPLY_SIMPLE; /* switch to the simple mode forcibly */ + if (proto_feed_reply(proto, ok, 7, 0) <= -1) return -1; + if (write_reply_chunk(proto) <= -1) return -1; + } + else + { + if (proto->reply.len > 0 && write_reply_chunk(proto) <= -1) return -1; + if (write_reply_chunk(proto) <= -1) return -1; /* write 0: */ + } + } + + return 0; +} + +static HCL_INLINE int is_spacechar (hcl_ooci_t c) +{ + /* TODO: handle other space unicode characters */ + switch (c) + { + case ' ': + case '\f': /* formfeed */ + case '\r': /* carriage return */ + case '\t': /* horizon tab */ + case '\v': /* vertical tab */ + return 1; + + default: + return 0; + } +} + +static HCL_INLINE int read_char (proto_t* proto) +{ + proto->lxc = hcl_readchar(proto->hcl); + if (!proto->lxc) return -1; + return 0; +} + +static HCL_INLINE int unread_last_char (proto_t* proto) +{ + return hcl_unreadchar(proto->hcl, proto->lxc); +} + +#define GET_CHAR_TO(proto,c) \ + do { \ + if (read_char(proto) <= -1) return -1; \ + c = (proto)->lxc->c; \ + } while(0) + +#define UNGET_LAST_CHAR(proto) \ + do { \ + if (unread_last_char(proto) <= -1) return -1; \ + } while (0) + +#define CLEAR_TOKEN_NAME(proto) ((proto)->tok.name.len = 0) +#define SET_TOKEN_TYPE(proto,tv) ((proto)->tok.type = (tv)) +#define ADD_TOKEN_CHAR(proto,c) \ + do { if (add_token_char(proto, c) <= -1) return -1; } while (0) + + +static HCL_INLINE int add_token_char (proto_t* proto, hcl_ooch_t c) +{ + if (proto->tok.len >= proto->tok.capa) + { + hcl_ooch_t* tmp; + hcl_oow_t capa; + + capa = HCL_ALIGN_POW2(proto->tok.len + 1, 64); + tmp = (hcl_ooch_t*)realloc(proto->tok.ptr, capa * sizeof(*tmp)); + if (!tmp) + { + fprintf (stderr, "cannot alloc memory when adding a character to the token buffer\n"); + return -1; + } + + proto->tok.ptr = tmp; + proto->tok.capa = capa; + } + + proto->tok.ptr[proto->tok.len++] = c; + return 0; +} + +static HCL_INLINE int is_alphachar (hcl_ooci_t c) +{ +/* TODO: support full unicode */ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static void classify_current_ident_token (proto_t* proto) +{ + static struct cmd_t + { + proto_token_type_t type; + hcl_ooch_t name[32]; + } tab[] = + { + { PROTO_TOKEN_BEGIN, { '.','B','E','G','I','N','\0' } }, + { PROTO_TOKEN_END, { '.','E','N','D','\0' } }, + { PROTO_TOKEN_SCRIPT, { '.','S','C','R','I','P','T','\0' } }, + { PROTO_TOKEN_EXIT, { '.','E','X','I','T','\0' } }, + /* TODO: add more */ + }; + hcl_oow_t i; + + for (i = 0; i < HCL_COUNTOF(tab); i++) + { + if (hcl_compoocharsoocstr(proto->tok.ptr, proto->tok.len, tab[i].name) == 0) + { + SET_TOKEN_TYPE (proto, tab[i].type); + break; + } + } +} + +static int get_token (proto_t* proto) +{ + hcl_ooci_t c; + + GET_CHAR_TO (proto, c); + + /* skip spaces */ + while (is_spacechar(c)) GET_CHAR_TO (proto, c); + + SET_TOKEN_TYPE (proto, PROTO_TOKEN_EOF); /* is it correct? */ + proto->tok.len = 0; + /*proto->tok.loc = hcl->c->lxc.l;*/ /* set token location */ + + switch (c) + { + case HCL_OOCI_EOF: + SET_TOKEN_TYPE (proto, PROTO_TOKEN_EOF); + break; + + case '\n': + SET_TOKEN_TYPE (proto, PROTO_TOKEN_NL); + break; + + case '.': + SET_TOKEN_TYPE (proto, PROTO_TOKEN_IDENT); + + ADD_TOKEN_CHAR(proto, c); + GET_CHAR_TO(proto, c); + if (!is_alphachar(c)) + { + fprintf (stderr, "alphabetic character is expected...\n"); + return -1; + } + + do + { + ADD_TOKEN_CHAR(proto, c); + GET_CHAR_TO(proto, c); + } + while (is_alphachar(c)); + + UNGET_LAST_CHAR (proto); + + classify_current_ident_token (proto); + break; + + default: + fprintf (stderr, "unrecognized character found - code [%x]\n", c); + return -1; + } + return 0; +} + +int proto_handle_request (proto_t* proto) +{ + if (get_token(proto) <= -1) return -1; + + switch (proto->tok.type) + { + case PROTO_TOKEN_EOF: + if (proto->req.state != PROTO_REQ_IN_TOP_LEVEL) + { + fprintf (stderr, "Unexpected EOF without .END\n"); + return -1; + } + + /* TODO: send some response */ + return 0; + + case PROTO_TOKEN_EXIT: + /* TODO: erorr - not valid inside BEGIN ... END */ + if (proto->req.state != PROTO_REQ_IN_TOP_LEVEL) + { + fprintf (stderr, ".EXIT allowed in the top level only\n"); + return -1; + } + + /* TODO: send response */ + return 0; + + case PROTO_TOKEN_BEGIN: + if (proto->req.state != PROTO_REQ_IN_TOP_LEVEL) + { + fprintf (stderr, ".BEGIN allowed in the top level only\n"); + return -1; + } + + if (get_token(proto) <= -1) return -1; + if (proto->tok.type != PROTO_TOKEN_NL) + { + fprintf (stderr, "no new line after BEGIN\n"); + return -1; + } + + proto->req.state = PROTO_REQ_IN_BLOCK_LEVEL; + hcl_reset (proto->hcl); + break; + + case PROTO_TOKEN_END: + { + hcl_oop_t obj; + + if (proto->req.state != PROTO_REQ_IN_BLOCK_LEVEL) + { + fprintf (stderr, ".END allowed in the block level only\n"); + return -1; + } + + if (get_token(proto) <= -1) return -1; + if (proto->tok.type != PROTO_TOKEN_NL) + { + fprintf (stderr, "no new line after END\n"); + return -1; + } + + + proto_start_reply (proto); + obj = (hcl_getbclen(proto->hcl) > 0)? hcl_execute(proto->hcl): proto->hcl->_nil; + if (proto_end_reply(proto, (obj? NULL: hcl_geterrmsg(proto->hcl))) <= -1) + { + fprintf (stderr, "cannot end chunked reply\n"); + return -1; + } + + proto->req.state = PROTO_REQ_IN_TOP_LEVEL; + break; + } + + case PROTO_TOKEN_SCRIPT: + { + hcl_oop_t obj; + + if (proto->req.state == PROTO_REQ_IN_TOP_LEVEL) hcl_reset(proto->hcl); + + obj = hcl_read(proto->hcl); + if (!obj) + { + fprintf (stderr, "cannot read script contents\n"); + return -1; + } + + if (get_token(proto) <= -1) return -1; + if (proto->tok.type != PROTO_TOKEN_NL) + { + fprintf (stderr, "no new line after script contents\n"); + return -1; + } + + if (hcl_compile(proto->hcl, obj) <= -1) + { + fprintf (stderr, "cannot compile script contents\n"); + return -1; + } + + if (proto->req.state == PROTO_REQ_IN_TOP_LEVEL) + { + proto_start_reply (proto); + obj = hcl_execute(proto->hcl); + if (proto_end_reply(proto, (obj? NULL: hcl_geterrmsg(proto->hcl))) <= -1) + { + fprintf (stderr, "cannot end chunked reply\n"); + return -1; + } + } + break; + } + + default: + fprintf (stderr, "unknwn token - %d\n", proto->tok.type); + return -1; + } + + return 1; +} + +/* ========================================================================= */ + +static void zap_all_clients (server_t* server) +{ +} + +static client_t* alloc_client (server_t* server, int cli_sck) +{ + client_t* client; + + client = (client_t*)malloc(sizeof(*client)); + if (!client) return NULL; + + memset (client, 0, sizeof(*client)); + client->sck = cli_sck; + client->server = server; + return client; +} + +static void free_client (client_t* client) +{ + if (client->sck >= 0) close (client->sck); +/* TODO: do anything to server? */ + free (client); +} + +static void* start_client (void* ctx); + +server_t* server_open (size_t xtnsize, hcl_oow_t memsize, const char* logopt, unsigned int dbgopt, int large_pages) +{ + server_t* server; + + server = (server_t*)malloc(sizeof(*server) + xtnsize); + if (!server) return NULL; + + memset (server, 0, sizeof(*server) + xtnsize); + server->cfg.memsize = memsize; + server->cfg.logopt = logopt; + server->cfg.dbgopt = dbgopt; + server->cfg.large_pages = large_pages; + pthread_mutex_init (&server->client_mutex, NULL); + pthread_mutex_init (&server->log_mutex, NULL); + return server; +} + +void server_close (server_t* server) +{ + pthread_mutex_destroy (&server->log_mutex); + pthread_mutex_destroy (&server->client_mutex); + free (server); +} + + +int add_client_to_server (server_t* server, client_state_t cstate, client_t* client) +{ + if (client->server != server) + { + fprintf (stderr, "cannot add client %p to server - client not beloing to server\n", client); /* TODO: use a client id when printing instead of the pointer */ + return -1; + } + + + if (server->client_list[cstate].tail) + { + server->client_list[cstate].tail->next_client = client; + client->prev_client = server->client_list[cstate].tail; + server->client_list[cstate].tail = client; + client->next_client = NULL; + } + else + { + server->client_list[cstate].tail = client; + server->client_list[cstate].head = client; + client->prev_client = NULL; + client->next_client = NULL; + } + + client->state = cstate; + return 0; +} + +int zap_client_in_server (server_t* server, client_t* client) +{ + if (client->server != server) + { + fprintf (stderr, "cannot zap client %p from server - client not beloing to server\n", client); /* TODO: use a client id when printing instead of the pointer */ + return -1; + } + + if (client->prev_client) client->prev_client->next_client = client->next_client; + else server->client_list[client->state].head = client->next_client; + if (client->next_client) client->next_client->prev_client = client->prev_client; + else server->client_list[client->state].tail = client->prev_client; + + return 0; +} + +static void* start_client (void* ctx) +{ + client_t* client = (client_t*)ctx; + server_t* server = client->server; + + client->thr = pthread_self(); + client->proto = proto_open(0, client); /* TODO: get this from argumen */ + if (!client->proto) + { + free_client (client); + return NULL; + } + + pthread_mutex_lock (&server->client_mutex); + add_client_to_server (server, CLIENT_STATE_ALIVE, client); + pthread_mutex_unlock (&server->client_mutex); + + while (!server->stopreq) + { + if (proto_handle_request(client->proto) <= 0) break; + } + + proto_close (client->proto); + client->proto = NULL; + + pthread_mutex_lock (&server->client_mutex); + zap_client_in_server (server, client); + add_client_to_server (server, CLIENT_STATE_DEAD, client); + pthread_mutex_unlock (&server->client_mutex); + + //free_client (client); + return NULL; +} + +void purge_all_clients (server_t* server, client_state_t cstate) +{ + client_t* client, * next; + + client = server->client_list[cstate].head; + while (client) + { + next = client->next_client; + + pthread_join (client->thr, NULL); + zap_client_in_server (server, client); + free_client (client); + + client = next; + } +} + +int server_start (server_t* server, const char* addrs) +{ + sck_addr_t srv_addr; + int srv_fd, sck_fam; + int optval; + socklen_t srv_len; + +/* TODO: interprete 'addrs' as a command-separated address list + * 192.168.1.1:20,[::1]:20,127.0.0.1:345 + */ + memset (&srv_addr, 0, sizeof(srv_addr)); + if (inet_pton (AF_INET6, addrs, &srv_addr.in6.sin6_addr) != 1) + { + if (inet_pton (AF_INET, addrs, &srv_addr.in4.sin_addr) != 1) + { + fprintf (stderr, "cannot open convert server address %s - %s\n", addrs, strerror(errno)); + return -1; + } + else + { + srv_addr.in4.sin_family = AF_INET; + srv_addr.in4.sin_port = htons(8888); /* TODO: change it */ + srv_len = sizeof(srv_addr.in4); + sck_fam = AF_INET; + } + } + else + { + srv_addr.in6.sin6_family = AF_INET6; + srv_addr.in6.sin6_port = htons(8888); /* TODO: change it */ + srv_len = sizeof(srv_addr.in6); + sck_fam = AF_INET6; + } + + srv_fd = socket(sck_fam, SOCK_STREAM, 0); + if (srv_fd == -1) + { + fprintf (stderr, "cannot open server socket - %s\n", strerror(errno)); + return -1; + } + + optval = 1; + setsockopt (srv_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)); + + if (bind(srv_fd, (struct sockaddr*)&srv_addr, srv_len) == -1) + { + fprintf (stderr, "cannot bind server socket %d - %s\n", srv_fd, strerror(errno)); + close (srv_fd); + return -1; + } + + if (listen (srv_fd, 128) == -1) + { + fprintf (stderr, "cannot listen on server socket %d - %s\n", srv_fd, strerror(errno)); + close (srv_fd); + return -1; + } + + server->stopreq = 0; + while (!server->stopreq) + { + sck_addr_t cli_addr; + int cli_fd; + socklen_t cli_len; + pthread_t thr; + client_t* client; + + purge_all_clients (server, CLIENT_STATE_DEAD); + + cli_len = sizeof(cli_addr); + cli_fd = accept(srv_fd, (struct sockaddr*)&cli_addr, &cli_len); + if (cli_fd == -1) + { + if (errno != EINTR || !server->stopreq) + { + fprintf (stderr, "cannot accept client on socket %d - %s\n", srv_fd, strerror(errno)); + } + break; + } + + client = alloc_client(server, cli_fd); + if (pthread_create(&thr, NULL, start_client, client) != 0) + { + free_client (client); + } + } + + purge_all_clients (server, CLIENT_STATE_DEAD); // is thread protection needed? + purge_all_clients (server, CLIENT_STATE_ALIVE); // is thread protection needed? + return 0; +} + +void server_stop (server_t* server) +{ + server->stopreq = 1; +} +/* ========================================================================= */ + +static void print_synerr (hcl_t* hcl) +{ + hcl_synerr_t synerr; + xtn_t* xtn; + + xtn = (xtn_t*)hcl_getxtn(hcl); + hcl_getsynerr (hcl, &synerr); + + hcl_logbfmt (hcl,HCL_LOG_STDERR, "ERROR: "); + if (synerr.loc.file) + { + hcl_logbfmt (hcl, HCL_LOG_STDERR, "%js", synerr.loc.file); + } + else + { + /*hcl_logbfmt (hcl, HCL_LOG_STDERR, "%s", xtn->read_path);*/ + hcl_logbfmt (hcl, HCL_LOG_STDERR, "client"); + } + + hcl_logbfmt (hcl, HCL_LOG_STDERR, "[%zu,%zu] %js", + synerr.loc.line, synerr.loc.colm, + (hcl_geterrmsg(hcl) != hcl_geterrstr(hcl)? hcl_geterrmsg(hcl): hcl_geterrstr(hcl)) + ); + + if (synerr.tgt.len > 0) + { + hcl_logbfmt (hcl, HCL_LOG_STDERR, " - %.*js", synerr.tgt.len, synerr.tgt.ptr); + } + + hcl_logbfmt (hcl, HCL_LOG_STDERR, "\n"); +} + +#define MIN_MEMSIZE 512000ul + +int main_server (int argc, char* argv[]) +{ + hcl_bci_t c; + static hcl_bopt_lng_t lopt[] = + { + { ":log", 'l' }, + { ":memsize", 'm' }, + { "large-pages", '\0' }, +#if defined(HCL_BUILD_DEBUG) + { ":debug", '\0' }, /* NOTE: there is no short option for --debug */ +#endif + { HCL_NULL, '\0' } + }; + static hcl_bopt_t opt = + { + "l:m:", + lopt + }; + + server_t* server; + + const char* logopt = HCL_NULL; + hcl_oow_t memsize = MIN_MEMSIZE; + int large_pages = 0; + unsigned int dbgopt = 0; + + setlocale (LC_ALL, ""); + + if (argc < 2) + { + print_usage: + fprintf (stderr, "Usage: %s bind-address:port\n", argv[0]); + return -1; + } + + while ((c = hcl_getbopt (argc, argv, &opt)) != HCL_BCI_EOF) + { + switch (c) + { + case 'l': + logopt = opt.arg; + break; + + case 'm': + memsize = strtoul(opt.arg, HCL_NULL, 0); + if (memsize <= MIN_MEMSIZE) memsize = MIN_MEMSIZE; + break; + + case '\0': + if (hcl_compbcstr(opt.lngopt, "large-pages") == 0) + { + large_pages = 1; + break; + } + #if defined(HCL_BUILD_DEBUG) + else if (hcl_compbcstr(opt.lngopt, "debug") == 0) + { + if (parse_dbgopt (opt.arg, &dbgopt) <= -1) return -1; + break; + } + #endif + + goto print_usage; + + case ':': + if (opt.lngopt) + fprintf (stderr, "bad argument for '%s'\n", opt.lngopt); + else + fprintf (stderr, "bad argument for '%c'\n", opt.opt); + + return -1; + + default: + goto print_usage; + } + } + + if (opt.ind >= argc) goto print_usage; + + server = server_open(0, memsize, logopt, dbgopt, large_pages); + if (!server) + { + fprintf (stderr, "cannot open server\n"); + return -1; + } + + g_server = server; + set_signal (SIGINT, handle_sigint); + server_start (server, argv[opt.ind]); + set_signal_to_default (SIGINT); + g_server = NULL; + + server_close (server); + return 0; +} diff --git a/lib/utl.c b/lib/utl.c index ea6dffd..809c450 100644 --- a/lib/utl.c +++ b/lib/utl.c @@ -26,8 +26,6 @@ #include "hcl-prv.h" -#define HCL_BCLEN_MAX 6 - /* some naming conventions * bchars, uchars -> pointer and length * bcstr, ucstr -> null-terminated string pointer @@ -577,7 +575,7 @@ static HCL_INLINE int ucsn_to_bcsn_with_cmgr ( } else { - hcl_bch_t bcsbuf[HCL_BCLEN_MAX]; + hcl_bch_t bcsbuf[HCL_BCSIZE_MAX]; hcl_oow_t mlen = 0; while (p < end) @@ -659,7 +657,7 @@ static int ucs_to_bcs_with_cmgr ( } else { - hcl_bch_t bcsbuf[HCL_BCLEN_MAX]; + hcl_bch_t bcsbuf[HCL_BCSIZE_MAX]; hcl_oow_t mlen = 0; while (*p != '\0')