From 7e782809f9bc52440910d4e8e303d9dae090f1c6 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Sun, 21 Apr 2024 22:15:04 +0900 Subject: [PATCH] undergoing code refactoring of xproto, server, client code --- bin/main-c.c | 197 ++-- lib/Makefile.am | 9 +- lib/Makefile.in | 76 +- lib/hcl-x.c | 265 +----- lib/hcl-x.h | 22 +- lib/hcl-xutl.h | 20 +- lib/x-client.c | 336 +++++++ lib/x-proto.c | 195 ++++ lib/x-server.c | 2285 +++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 2979 insertions(+), 426 deletions(-) create mode 100644 lib/x-client.c create mode 100644 lib/x-proto.c create mode 100644 lib/x-server.c diff --git a/bin/main-c.c b/bin/main-c.c index cd9dff0..8b841ba 100644 --- a/bin/main-c.c +++ b/bin/main-c.c @@ -69,9 +69,6 @@ struct client_xtn_t hcl_bch_t buf[4096]; hcl_oow_t len; } logbuf; - - int reply_count; - hcl_oow_t data_length; }; /* ========================================================================= */ @@ -491,133 +488,19 @@ static int send_iov (int sck, struct iovec* iov, int count) /* ========================================================================= */ -enum hcl_xproto_rcv_state_t +struct proto_xtn_t { - HCL_XPROTO_RCV_HDR, - HCL_XPROTO_RCV_PLD, + int x; }; -typedef enum hcl_xproto_rcv_state_t hcl_xproto_rcv_state_t; +typedef struct proto_xtn_t proto_xtn_t; - -struct hcl_xproto_t +static int handle_packet (hcl_xproto_t* proto, hcl_xpkt_type_t type, const void* data, hcl_oow_t len) { - hcl_t* hcl; - - struct + if (type == HCL_XPKT_STDOUT) { - hcl_xproto_rcv_state_t state; - hcl_oow_t len_needed; - unsigned int eof: 1; - - hcl_oow_t len; - hcl_uint8_t buf[4096]; - - hcl_xpkt_hdr_t hdr; - } rcv; -}; -typedef struct hcl_xproto_t hcl_xproto_t; - -static int receive_raw_bytes (hcl_xproto_t* proto, int sck, hcl_ntime_t* idle_tmout) -{ - hcl_t* hcl = proto->hcl; - struct pollfd pfd; - int tmout, actual_tmout; - ssize_t x; - int n; - - if (HCL_UNLIKELY(proto->rcv.eof)) - { - hcl_seterrbfmt (hcl, HCL_EGENERIC, "connection closed"); - return -1; + /*if (len > 0) fwrite (data, 1, len, stdout); */ + if (len > 0) fprintf (stdout, "%.*s", (int)len, data); } - - tmout = idle_tmout? HCL_SECNSEC_TO_MSEC(idle_tmout->sec, idle_tmout->nsec): -1; - actual_tmout = (tmout <= 0)? 10000: tmout; - - pfd.fd = sck; - pfd.events = POLLIN | POLLERR; - pfd.revents = 0; - n = poll(&pfd, 1, actual_tmout); - if (n <= -1) - { - if (errno == EINTR) return 0; - hcl_seterrwithsyserr (hcl, 0, errno); - return -1; - } - else if (n == 0) - { - /* timed out - no activity on the pfd */ - if (tmout > 0) - { - /* timeout explicity set. no activity for that duration. considered idle */ - hcl_seterrbfmt (hcl, HCL_EGENERIC, "no activity on the socket %d", sck); - return -1; - } - - return 0; /* didn't read yet */ - } - - if (pfd.revents & POLLERR) - { - hcl_seterrbfmt (hcl, HCL_EGENERIC, "error condition detected on socket %d", sck); - return -1; - } - - x = recv(sck, &proto->rcv.buf[proto->rcv.len], HCL_COUNTOF(proto->rcv.buf) - proto->rcv.len, 0); - if (x <= -1) - { - if (errno == EINTR) return 0; /* didn't read read */ - - hcl_seterrwithsyserr (hcl, 0, errno); - return -1; - } - - if (x == 0) proto->rcv.eof = 1; - - proto->rcv.len += x; -//printf ("RECEIVED %d rcv.len %d rcv.len_needed %d [%.*s]\n", (int)x, (int)proto->rcv.len, (int)proto->rcv.len_needed, (int)proto->rcv.len, proto->rcv.buf); - return 1; /* read some data */ -} - -static int handle_received_data (hcl_xproto_t* proto) -{ -//printf ("HANDLE RECIVED rcv.len %d rcv.len_needed %d [%.*s]\n", (int)proto->rcv.len, (int)proto->rcv.len_needed, (int)proto->rcv.len, proto->rcv.buf); - switch (proto->rcv.state) - { - case HCL_XPROTO_RCV_HDR: - if (proto->rcv.len < HCL_SIZEOF(proto->rcv.hdr)) return 0; /* need more data */ - - memcpy (&proto->rcv.hdr, proto->rcv.buf, HCL_SIZEOF(proto->rcv.hdr)); - //proto->rcv.hdr.len = hcl_ntoh16(proto->rcv.hdr.len); /* keep this in the host byte order */ - - /* consume the header */ - memmove (proto->rcv.buf, &proto->rcv.buf[HCL_SIZEOF(proto->rcv.hdr)], proto->rcv.len - HCL_SIZEOF(proto->rcv.hdr)); - proto->rcv.len -= HCL_SIZEOF(proto->rcv.hdr); - - /* switch to the payload mode */ - proto->rcv.state = HCL_XPROTO_RCV_PLD; - proto->rcv.len_needed = proto->rcv.hdr.len; - return 0; - - case HCL_XPROTO_RCV_PLD: - if (proto->rcv.len < proto->rcv.hdr.len) return 0; /* need more payload data */ - - if (proto->rcv.hdr.type == HCL_XPKT_STDOUT) - { - if (proto->rcv.hdr.len > 0) - fprintf (stdout, "%.*s", (int)proto->rcv.hdr.len, proto->rcv.buf); - } - - if (proto->rcv.hdr.len > 0) - { - memmove (proto->rcv.buf, &proto->rcv.buf[proto->rcv.hdr.len], proto->rcv.len - proto->rcv.hdr.len); - proto->rcv.len -= proto->rcv.hdr.len; - } - proto->rcv.state = HCL_XPROTO_RCV_HDR; - proto->rcv.len_needed = HCL_SIZEOF(proto->rcv.hdr); - break; - } - return 1; } @@ -634,11 +517,11 @@ static int handle_request (hcl_client_t* client, const char* ipaddr, const char* ssize_t n; const char* scptr; const char* sccur; - - hcl_xproto_t proto_buf; - hcl_xproto_t* proto = &proto_buf; + hcl_xproto_t* proto = HCL_NULL; client_xtn_t* client_xtn; + proto_xtn_t* proto_xtn; + hcl_xproto_cb_t proto_cb; client_xtn = hcl_client_getxtn(client); @@ -684,14 +567,17 @@ static int handle_request (hcl_client_t* client, const char* ipaddr, const char* goto oops; } - /* TODO: create hcl_xproto_open... */ - memset (proto, 0, HCL_SIZEOF(*proto)); - proto->hcl = hcl_openstdwithmmgr(hcl_client_getmmgr(client), 0, HCL_NULL); // TODO: - proto->rcv.state = HCL_XPROTO_RCV_HDR; - proto->rcv.len_needed = HCL_SIZEOF(proto->rcv.hdr); - proto->rcv.eof = 0; -// TODO: destroy xproto and data upon termination. + memset (&proto, 0, HCL_SIZEOF(proto_cb)); + proto_cb.on_packet = handle_packet; + proto = hcl_xproto_open(hcl_client_getmmgr(client), &proto_cb, HCL_SIZEOF(*proto_xtn)); + if (HCL_UNLIKELY(!proto)) + { + fprintf (stderr, "cannot open protocol to %s\n", ipaddr); + goto oops; + } + proto_xtn = hcl_xproto_getxtn(proto); + //proto_xtn->client = client; scptr = sccur = script; while (1) @@ -774,27 +660,44 @@ static int handle_request (hcl_client_t* client, const char* ipaddr, const char* if (pfd.revents & POLLIN) { -//printf ("receiving...\n"); - if (receive_raw_bytes(proto, sck, HCL_NULL) <= -1) break; + hcl_oow_t bcap; + hcl_uint8_t* bptr; + + bptr = hcl_xproto_getbuf(proto, &bcap);; + x = recv(sck, bptr, bcap, 0); + if (x <= -1) + { + if (errno == EINTR) goto carry_on; /* didn't read read */ + /*hcl_seterrwithsyserr (hcl, 0, errno); */ + /* TODO: error info set... */ + return -1; + } + if (x == 0) hcl_xproto_seteof(proto, 1); + hcl_xproto_advbuf (proto, x); } - while (/*proto->rcv.len > 0 &&*/ proto->rcv.len >= proto->rcv.len_needed) + + carry_on: + while (hcl_xproto_ready(proto)) { - if (handle_received_data(proto) <= -1) + if ((n = hcl_xproto_process(proto)) <= -1) { - goto oops; + /* TODO: proper error message */ + return -1; + } + if (n == 0) + { + /* TODO: chceck if there is remaining data in the buffer...?? */ + printf ("NO MORE DATA. EXITING...\n"); + goto done; } } - } - client_xtn->data_length = 0; - client_xtn->reply_count = 0; + if (hcl_xproto_geteof(proto)) break; + } +done: /* TODO: we can check if the buffer has all been consumed. if not, there is trailing garbage.. */ - - /*shutdown (sck, (shut_wr_after_req? SHUT_RD: SHUT_RDWR));*/ - if (!shut_wr_after_req) shutdown (sck, SHUT_RDWR); - /*{ struct linger linger; linger.l_onoff = 1; @@ -802,10 +705,12 @@ static int handle_request (hcl_client_t* client, const char* ipaddr, const char* setsockopt (sck, SOL_SOCKET, SO_LINGER, (char *) &linger, sizeof(linger)); }*/ + hcl_xproto_close (proto); close (sck); return 0; oops: + if (proto) hcl_xproto_close (proto); if (sck >= 0) close (sck); return -1; } diff --git a/lib/Makefile.am b/lib/Makefile.am index f36e3a1..fc5f75c 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -106,14 +106,15 @@ endif if ENABLE_HCLX pkglib_LTLIBRARIES += libhclx.la -pkginclude_HEADERS += hcl-c.h hcl-s.h hcl-x.h hcl-tmr.h hcl-xutl.h hcl-json.h +pkginclude_HEADERS += hcl-x.h hcl-tmr.h hcl-xutl.h hcl-json.h libhclx_la_SOURCES = \ tmr.c hcl-tmr.h \ xutl.c xutl-sa.h hcl-xutl.h \ json.c hcl-json.h \ - hcl-s.c hcl-s.h \ - hcl-x.c hcl-x.h \ - hcl-c.c hcl-c.h + hcl-x.h \ + x-client.c \ + x-proto.c \ + x-server.c libhclx_la_CPPFLAGS = $(CPPFLAGS_LIB_COMMON) $(CPPFLAGS_PFMOD) libhclx_la_LDFLAGS = $(LDFLAGS_LIB_COMMON) libhclx_la_LIBADD = libhcl.la $(LIBADD_LIB_COMMON) diff --git a/lib/Makefile.in b/lib/Makefile.in index e51bce1..fabf966 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -97,7 +97,7 @@ host_triplet = @host@ @ENABLE_STATIC_MODULE_TRUE@ ../mod/libhcl-str.la \ @ENABLE_STATIC_MODULE_TRUE@ ../mod/libhcl-sys.la @ENABLE_HCLX_TRUE@am__append_7 = libhclx.la -@ENABLE_HCLX_TRUE@am__append_8 = hcl-c.h hcl-s.h hcl-x.h hcl-tmr.h hcl-xutl.h hcl-json.h +@ENABLE_HCLX_TRUE@am__append_8 = hcl-x.h hcl-tmr.h hcl-xutl.h hcl-json.h subdir = lib ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_sign.m4 \ @@ -171,12 +171,12 @@ libhcl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ @ENABLE_HCLX_TRUE@libhclx_la_DEPENDENCIES = libhcl.la \ @ENABLE_HCLX_TRUE@ $(am__DEPENDENCIES_5) am__libhclx_la_SOURCES_DIST = tmr.c hcl-tmr.h xutl.c xutl-sa.h \ - hcl-xutl.h json.c hcl-json.h hcl-s.c hcl-s.h hcl-x.c hcl-x.h \ - hcl-c.c hcl-c.h + hcl-xutl.h json.c hcl-json.h hcl-x.h x-client.c x-proto.c \ + x-server.c @ENABLE_HCLX_TRUE@am_libhclx_la_OBJECTS = libhclx_la-tmr.lo \ @ENABLE_HCLX_TRUE@ libhclx_la-xutl.lo libhclx_la-json.lo \ -@ENABLE_HCLX_TRUE@ libhclx_la-hcl-s.lo libhclx_la-hcl-x.lo \ -@ENABLE_HCLX_TRUE@ libhclx_la-hcl-c.lo +@ENABLE_HCLX_TRUE@ libhclx_la-x-client.lo libhclx_la-x-proto.lo \ +@ENABLE_HCLX_TRUE@ libhclx_la-x-server.lo libhclx_la_OBJECTS = $(am_libhclx_la_OBJECTS) libhclx_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ @@ -211,10 +211,11 @@ am__depfiles_remade = ./$(DEPDIR)/libhcl_la-bigint.Plo \ ./$(DEPDIR)/libhcl_la-std.Plo ./$(DEPDIR)/libhcl_la-sym.Plo \ ./$(DEPDIR)/libhcl_la-utf16.Plo ./$(DEPDIR)/libhcl_la-utf8.Plo \ ./$(DEPDIR)/libhcl_la-utl.Plo ./$(DEPDIR)/libhcl_la-xchg.Plo \ - ./$(DEPDIR)/libhcl_la-xma.Plo ./$(DEPDIR)/libhclx_la-hcl-c.Plo \ - ./$(DEPDIR)/libhclx_la-hcl-s.Plo \ - ./$(DEPDIR)/libhclx_la-hcl-x.Plo \ - ./$(DEPDIR)/libhclx_la-json.Plo ./$(DEPDIR)/libhclx_la-tmr.Plo \ + ./$(DEPDIR)/libhcl_la-xma.Plo ./$(DEPDIR)/libhclx_la-json.Plo \ + ./$(DEPDIR)/libhclx_la-tmr.Plo \ + ./$(DEPDIR)/libhclx_la-x-client.Plo \ + ./$(DEPDIR)/libhclx_la-x-proto.Plo \ + ./$(DEPDIR)/libhclx_la-x-server.Plo \ ./$(DEPDIR)/libhclx_la-xutl.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ @@ -244,7 +245,7 @@ am__can_run_installinfo = \ esac am__pkginclude_HEADERS_DIST = hcl.h hcl-chr.h hcl-cmn.h hcl-fmt.h \ hcl-opt.h hcl-pac1.h hcl-rbt.h hcl-upac.h hcl-utl.h hcl-xma.h \ - hcl-c.h hcl-s.h hcl-x.h hcl-tmr.h hcl-xutl.h hcl-json.h + hcl-x.h hcl-tmr.h hcl-xutl.h hcl-json.h HEADERS = $(pkginclude_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ hcl-cfg.h.in @@ -479,9 +480,10 @@ libhcl_la_LIBADD = $(LIBADD_LIB_COMMON) $(am__append_6) @ENABLE_HCLX_TRUE@ tmr.c hcl-tmr.h \ @ENABLE_HCLX_TRUE@ xutl.c xutl-sa.h hcl-xutl.h \ @ENABLE_HCLX_TRUE@ json.c hcl-json.h \ -@ENABLE_HCLX_TRUE@ hcl-s.c hcl-s.h \ -@ENABLE_HCLX_TRUE@ hcl-x.c hcl-x.h \ -@ENABLE_HCLX_TRUE@ hcl-c.c hcl-c.h +@ENABLE_HCLX_TRUE@ hcl-x.h \ +@ENABLE_HCLX_TRUE@ x-client.c \ +@ENABLE_HCLX_TRUE@ x-proto.c \ +@ENABLE_HCLX_TRUE@ x-server.c @ENABLE_HCLX_TRUE@libhclx_la_CPPFLAGS = $(CPPFLAGS_LIB_COMMON) $(CPPFLAGS_PFMOD) @ENABLE_HCLX_TRUE@libhclx_la_LDFLAGS = $(LDFLAGS_LIB_COMMON) @@ -611,11 +613,11 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcl_la-utl.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcl_la-xchg.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcl_la-xma.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhclx_la-hcl-c.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhclx_la-hcl-s.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhclx_la-hcl-x.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhclx_la-json.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhclx_la-tmr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhclx_la-x-client.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhclx_la-x-proto.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhclx_la-x-server.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhclx_la-xutl.Plo@am__quote@ # am--include-marker $(am__depfiles_remade): @@ -865,26 +867,26 @@ libhclx_la-json.lo: json.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhclx_la-json.lo `test -f 'json.c' || echo '$(srcdir)/'`json.c -libhclx_la-hcl-s.lo: hcl-s.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhclx_la-hcl-s.lo -MD -MP -MF $(DEPDIR)/libhclx_la-hcl-s.Tpo -c -o libhclx_la-hcl-s.lo `test -f 'hcl-s.c' || echo '$(srcdir)/'`hcl-s.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhclx_la-hcl-s.Tpo $(DEPDIR)/libhclx_la-hcl-s.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hcl-s.c' object='libhclx_la-hcl-s.lo' libtool=yes @AMDEPBACKSLASH@ +libhclx_la-x-client.lo: x-client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhclx_la-x-client.lo -MD -MP -MF $(DEPDIR)/libhclx_la-x-client.Tpo -c -o libhclx_la-x-client.lo `test -f 'x-client.c' || echo '$(srcdir)/'`x-client.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhclx_la-x-client.Tpo $(DEPDIR)/libhclx_la-x-client.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='x-client.c' object='libhclx_la-x-client.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhclx_la-hcl-s.lo `test -f 'hcl-s.c' || echo '$(srcdir)/'`hcl-s.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhclx_la-x-client.lo `test -f 'x-client.c' || echo '$(srcdir)/'`x-client.c -libhclx_la-hcl-x.lo: hcl-x.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhclx_la-hcl-x.lo -MD -MP -MF $(DEPDIR)/libhclx_la-hcl-x.Tpo -c -o libhclx_la-hcl-x.lo `test -f 'hcl-x.c' || echo '$(srcdir)/'`hcl-x.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhclx_la-hcl-x.Tpo $(DEPDIR)/libhclx_la-hcl-x.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hcl-x.c' object='libhclx_la-hcl-x.lo' libtool=yes @AMDEPBACKSLASH@ +libhclx_la-x-proto.lo: x-proto.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhclx_la-x-proto.lo -MD -MP -MF $(DEPDIR)/libhclx_la-x-proto.Tpo -c -o libhclx_la-x-proto.lo `test -f 'x-proto.c' || echo '$(srcdir)/'`x-proto.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhclx_la-x-proto.Tpo $(DEPDIR)/libhclx_la-x-proto.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='x-proto.c' object='libhclx_la-x-proto.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhclx_la-hcl-x.lo `test -f 'hcl-x.c' || echo '$(srcdir)/'`hcl-x.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhclx_la-x-proto.lo `test -f 'x-proto.c' || echo '$(srcdir)/'`x-proto.c -libhclx_la-hcl-c.lo: hcl-c.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhclx_la-hcl-c.lo -MD -MP -MF $(DEPDIR)/libhclx_la-hcl-c.Tpo -c -o libhclx_la-hcl-c.lo `test -f 'hcl-c.c' || echo '$(srcdir)/'`hcl-c.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhclx_la-hcl-c.Tpo $(DEPDIR)/libhclx_la-hcl-c.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hcl-c.c' object='libhclx_la-hcl-c.lo' libtool=yes @AMDEPBACKSLASH@ +libhclx_la-x-server.lo: x-server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhclx_la-x-server.lo -MD -MP -MF $(DEPDIR)/libhclx_la-x-server.Tpo -c -o libhclx_la-x-server.lo `test -f 'x-server.c' || echo '$(srcdir)/'`x-server.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhclx_la-x-server.Tpo $(DEPDIR)/libhclx_la-x-server.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='x-server.c' object='libhclx_la-x-server.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhclx_la-hcl-c.lo `test -f 'hcl-c.c' || echo '$(srcdir)/'`hcl-c.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhclx_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhclx_la-x-server.lo `test -f 'x-server.c' || echo '$(srcdir)/'`x-server.c mostlyclean-libtool: -rm -f *.lo @@ -1068,11 +1070,11 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/libhcl_la-utl.Plo -rm -f ./$(DEPDIR)/libhcl_la-xchg.Plo -rm -f ./$(DEPDIR)/libhcl_la-xma.Plo - -rm -f ./$(DEPDIR)/libhclx_la-hcl-c.Plo - -rm -f ./$(DEPDIR)/libhclx_la-hcl-s.Plo - -rm -f ./$(DEPDIR)/libhclx_la-hcl-x.Plo -rm -f ./$(DEPDIR)/libhclx_la-json.Plo -rm -f ./$(DEPDIR)/libhclx_la-tmr.Plo + -rm -f ./$(DEPDIR)/libhclx_la-x-client.Plo + -rm -f ./$(DEPDIR)/libhclx_la-x-proto.Plo + -rm -f ./$(DEPDIR)/libhclx_la-x-server.Plo -rm -f ./$(DEPDIR)/libhclx_la-xutl.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ @@ -1148,11 +1150,11 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/libhcl_la-utl.Plo -rm -f ./$(DEPDIR)/libhcl_la-xchg.Plo -rm -f ./$(DEPDIR)/libhcl_la-xma.Plo - -rm -f ./$(DEPDIR)/libhclx_la-hcl-c.Plo - -rm -f ./$(DEPDIR)/libhclx_la-hcl-s.Plo - -rm -f ./$(DEPDIR)/libhclx_la-hcl-x.Plo -rm -f ./$(DEPDIR)/libhclx_la-json.Plo -rm -f ./$(DEPDIR)/libhclx_la-tmr.Plo + -rm -f ./$(DEPDIR)/libhclx_la-x-client.Plo + -rm -f ./$(DEPDIR)/libhclx_la-x-proto.Plo + -rm -f ./$(DEPDIR)/libhclx_la-x-server.Plo -rm -f ./$(DEPDIR)/libhclx_la-xutl.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/lib/hcl-x.c b/lib/hcl-x.c index a3c35bd..f646a94 100644 --- a/lib/hcl-x.c +++ b/lib/hcl-x.c @@ -110,45 +110,6 @@ typedef struct server_hcl_xtn_t server_hcl_xtn_t; /* ---------------------------------- */ -enum hcl_xproto_rcv_state_t -{ - HCL_XPROTO_RCV_HDR, - HCL_XPROTO_RCV_PLD -}; -typedef enum hcl_xproto_rcv_state_t hcl_xproto_rcv_state_t; - -struct hcl_xproto_t -{ - hcl_oow_t _instsize; - hcl_mmgr_t* _mmgr; - - hcl_tmr_index_t exec_runtime_event_index; - struct - { - hcl_xproto_rcv_state_t state; - hcl_oow_t len_needed; - unsigned int eof: 1; - - hcl_oow_t len; - hcl_uint8_t buf[HCL_XPKT_MAX_PLD_LEN]; - - /* normalize header of hcl_xpkt_hdr_t with combined bits into separate placeholders */ - struct - { - hcl_uint8_t id; - hcl_uint8_t type; - hcl_uint16_t len; /* this is wider than the len field of hcl_xpkt_hdr_t */ - } hdr; - } rcv; - - struct - { - - } snd; -}; - -/* ---------------------------------- */ - enum hcl_server_worker_state_t { HCL_SERVER_WORKER_STATE_DEAD = 0, @@ -181,6 +142,7 @@ struct hcl_server_worker_t hcl_ntime_t alloc_time; hcl_server_worker_state_t state; hcl_server_worker_opstate_t opstate; + hcl_tmr_index_t exec_runtime_event_index; hcl_xproto_t* proto; hcl_t* hcl; @@ -694,6 +656,16 @@ static void fini_hcl (hcl_t* hcl) */ /* ========================================================================= */ + +static hcl_server_worker_t* proto_to_worker (hcl_xproto_t* proto) +{ + proto_xtn_t* prtxtn; + prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); + return prtxtn->worker; +} + +/* ========================================================================= */ + #define SERVER_LOGMASK_INFO (HCL_LOG_INFO | HCL_LOG_APP) #define SERVER_LOGMASK_ERROR (HCL_LOG_ERROR | HCL_LOG_APP) @@ -728,15 +700,9 @@ static void exec_runtime_handler (hcl_tmr_t* tmr, const hcl_ntime_t* now, hcl_tm { /* [NOTE] this handler is executed in the main server thread * when it calls hcl_tmr_fire(). */ - - hcl_xproto_t* proto; hcl_server_worker_t* worker; - proto_xtn_t* prtxtn; - - proto = (hcl_xproto_t*)evt->ctx; - prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); - worker = prtxtn->worker; + worker = proto_to_worker((hcl_xproto_t*)evt->ctx); /* TODO: can we use worker->hcl for logging before abort?? */ HCL_LOG1 (worker->server->dummy_hcl, SERVER_LOGMASK_INFO, "Aborting script execution for max_actor_runtime exceeded [%zu]\n", worker->wid); hcl_abort (worker->hcl); @@ -746,19 +712,16 @@ static void exec_runtime_updater (hcl_tmr_t* tmr, hcl_tmr_index_t old_index, hcl { /* [NOTE] this handler is executed in the main server thread * when it calls hcl_tmr_fire() */ - hcl_xproto_t* proto; hcl_server_worker_t* worker; - proto_xtn_t* prtxtn; proto = (hcl_xproto_t*)evt->ctx; - prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); - worker = prtxtn->worker; - HCL_ASSERT (worker->hcl, proto->exec_runtime_event_index == old_index); + worker = proto_to_worker(proto); + HCL_ASSERT (worker->hcl, worker->exec_runtime_event_index == old_index); /* the event is being removed by hcl_tmr_fire() or by hcl_tmr_delete() * if new_index is HCL_TMR_INVALID_INDEX. it's being updated if not. */ - proto->exec_runtime_event_index = new_index; + worker->exec_runtime_event_index = new_index; } static int insert_exec_timer (hcl_xproto_t* proto, const hcl_ntime_t* tmout) @@ -767,15 +730,13 @@ static int insert_exec_timer (hcl_xproto_t* proto, const hcl_ntime_t* tmout) hcl_tmr_event_t event; hcl_tmr_index_t index; - proto_xtn_t* prtxtn; hcl_server_worker_t* worker; hcl_server_t* server; - prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); - worker = prtxtn->worker; + worker = proto_to_worker(proto); server = worker->server; - HCL_ASSERT (worker->hcl, proto->exec_runtime_event_index == HCL_TMR_INVALID_INDEX); + HCL_ASSERT (worker->hcl, worker->exec_runtime_event_index == HCL_TMR_INVALID_INDEX); HCL_MEMSET (&event, 0, HCL_SIZEOF(event)); event.ctx = proto; @@ -786,7 +747,7 @@ static int insert_exec_timer (hcl_xproto_t* proto, const hcl_ntime_t* tmout) pthread_mutex_lock (&server->tmr_mutex); index = hcl_tmr_insert(server->tmr, &event); - proto->exec_runtime_event_index = index; + worker->exec_runtime_event_index = index; if (index != HCL_TMR_INVALID_INDEX) { /* inform the server of timer event change */ @@ -800,25 +761,23 @@ static int insert_exec_timer (hcl_xproto_t* proto, const hcl_ntime_t* tmout) static void delete_exec_timer (hcl_xproto_t* proto) { /* [NOTE] this is executed in the worker thread. if the event has been fired - * in the server thread, proto->exec_runtime_event_index should be + * in the server thread, worker->exec_runtime_event_index should be * HCL_TMR_INVALID_INDEX as set by exec_runtime_handler */ - proto_xtn_t* prtxtn; hcl_server_worker_t* worker; hcl_server_t* server; - prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); - worker = prtxtn->worker; + worker = proto_to_worker(proto); server = worker->server; pthread_mutex_lock (&server->tmr_mutex); - if (proto->exec_runtime_event_index != HCL_TMR_INVALID_INDEX) + if (worker->exec_runtime_event_index != HCL_TMR_INVALID_INDEX) { /* the event has not been fired yet. let's delete it * if it has been fired, the index it shall be HCL_TMR_INVALID_INDEX already */ - hcl_tmr_delete (server->tmr, proto->exec_runtime_event_index); - HCL_ASSERT (worker->hcl, proto->exec_runtime_event_index == HCL_TMR_INVALID_INDEX); - /*proto->exec_runtime_event_index = HCL_TMR_INVALID_INDEX; */ + hcl_tmr_delete (server->tmr, worker->exec_runtime_event_index); + HCL_ASSERT (worker->hcl, worker->exec_runtime_event_index == HCL_TMR_INVALID_INDEX); + /*worker->exec_runtime_event_index = HCL_TMR_INVALID_INDEX; */ } pthread_mutex_unlock (&server->tmr_mutex); } @@ -827,12 +786,10 @@ static int execute_script (hcl_xproto_t* proto, const hcl_bch_t* trigger) { hcl_oop_t obj; const hcl_ooch_t* failmsg = HCL_NULL; - proto_xtn_t* prtxtn; hcl_server_worker_t* worker; hcl_server_t* server; - prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); - worker = prtxtn->worker; + worker = proto_to_worker(proto); server = worker->server; #if 0 @@ -905,24 +862,18 @@ static void reformat_synerr (hcl_t* hcl) static void send_proto_hcl_error (hcl_xproto_t* proto) { - proto_xtn_t* prtxtn; hcl_server_worker_t* worker; - - prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); - worker = prtxtn->worker; - + worker = proto_to_worker(proto); if (HCL_ERRNUM(worker->hcl) == HCL_ESYNERR) reformat_synerr (worker->hcl); send_error_message (proto, hcl_geterrmsg(worker->hcl)); } static void show_server_workers (hcl_xproto_t* proto) { - proto_xtn_t* prtxtn; hcl_server_worker_t* worker, * w; hcl_server_t* server; - prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); - worker = prtxtn->worker; + worker = proto_to_worker(proto); server = worker->server; pthread_mutex_lock (&server->worker_mutex); @@ -936,13 +887,11 @@ static void show_server_workers (hcl_xproto_t* proto) static int kill_server_worker (hcl_xproto_t* proto, hcl_oow_t wid) { - proto_xtn_t* prtxtn; hcl_server_worker_t* worker; hcl_server_t* server; int xret = 0; - prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); - worker = prtxtn->worker; + worker = proto_to_worker(proto); server = worker->server; pthread_mutex_lock (&server->worker_mutex); @@ -979,14 +928,12 @@ static int kill_server_worker (hcl_xproto_t* proto, hcl_oow_t wid) return xret; } -static int handle_packet (hcl_xproto_t* proto, hcl_xpkt_type_t type, void* data, hcl_oow_t len) +static int handle_packet (hcl_xproto_t* proto, hcl_xpkt_type_t type, const void* data, hcl_oow_t len) { - proto_xtn_t* prtxtn; hcl_server_worker_t* worker; hcl_t* hcl; - prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); - worker = prtxtn->worker; + worker = proto_to_worker(proto); hcl = worker->hcl; printf ("HANDLE PACKET TYPE => %d\n", type); @@ -1082,15 +1029,13 @@ static int send_iov (int sck, struct iovec* iov, int count) static int send_stdout_bytes (hcl_xproto_t* proto, const hcl_bch_t* data, hcl_oow_t len) { - proto_xtn_t* prtxtn; hcl_server_worker_t* worker; hcl_xpkt_hdr_t hdr; struct iovec iov[2]; const hcl_bch_t* ptr, * cur, * end; hcl_uint16_t seglen; - prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); - worker = prtxtn->worker; + worker = proto_to_worker(proto); ptr = cur = data; end = data + len; @@ -1126,15 +1071,13 @@ printf ("SENDING BYTES [%.*s]\n", (int)len, data); #if defined(HCL_OOCH_IS_UCH) static int send_stdout_chars (hcl_xproto_t* proto, const hcl_ooch_t* data, hcl_oow_t len) { - proto_xtn_t* prtxtn; hcl_server_worker_t* worker; const hcl_ooch_t* ptr, * end; hcl_bch_t tmp[256]; hcl_oow_t tln, pln; int n; - prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); - worker = prtxtn->worker; + worker = proto_to_worker(proto); ptr = data; end = data + len; @@ -1155,139 +1098,6 @@ static int send_stdout_chars (hcl_xproto_t* proto, const hcl_ooch_t* data, hcl_o } #endif -/* ========================================================================= */ - -hcl_xproto_t* hcl_xproto_open (hcl_mmgr_t* mmgr, hcl_oow_t xtnsize) -{ - hcl_xproto_t* proto; - - proto = (hcl_xproto_t*)HCL_MMGR_ALLOC(mmgr, HCL_SIZEOF(*proto) + xtnsize); - if (HCL_UNLIKELY(!proto)) return HCL_NULL; - - HCL_MEMSET (proto, 0, HCL_SIZEOF(*proto)); - proto->_instsize = HCL_SIZEOF(*proto); - proto->_mmgr = mmgr; - proto->exec_runtime_event_index = HCL_TMR_INVALID_INDEX; - proto->rcv.state = HCL_XPROTO_RCV_HDR; - proto->rcv.len_needed = HCL_XPKT_HDR_LEN; - proto->rcv.eof = 0; - - return proto; -} - -void hcl_xproto_close (hcl_xproto_t* proto) -{ - HCL_MMGR_FREE (proto->_mmgr, proto); -} - -void* hcl_xproto_getxtn (hcl_xproto_t* proto) -{ - return (proto + 1); -} - -int hcl_xprpto_feed (hcl_xproto_t* proto, const void* data, hcl_oow_t len) -{ -} - -hcl_uint8_t* hcl_xproto_getbuf (hcl_xproto_t* proto, hcl_oow_t* capa) -{ - *capa = HCL_COUNTOF(proto->rcv.buf) - proto->rcv.len; - return &proto->rcv.buf[proto->rcv.len]; -} - -void hcl_xproto_seteof (hcl_xproto_t* proto, int v) -{ - proto->rcv.eof = v; -} - -void hcl_xproto_advbuf (hcl_xproto_t* proto, hcl_oow_t inc) -{ - proto->rcv.len += inc; -} - -int hcl_xproto_ready (hcl_xproto_t* proto) -{ - /* has it received suffient data for processing? */ - return proto->rcv.len >= proto->rcv.len_needed; -} - -int hcl_xproto_process (hcl_xproto_t* proto) -{ - int n; - hcl_xpkt_hdr_t* hdr; - - switch (proto->rcv.state) - { - case HCL_XPROTO_RCV_HDR: - if (proto->rcv.len < HCL_XPKT_HDR_LEN) goto carry_on; /* need more data */ - - hdr = (hcl_xpkt_hdr_t*)proto->rcv.buf; - proto->rcv.hdr.id = hdr->id; - proto->rcv.hdr.type = hdr->type & 0x0F; - proto->rcv.hdr.len = (hcl_uint16_t)hdr->len | ((hcl_uint16_t)(hdr->type >> 4) << 8); - - /* consume the header */ - HCL_MEMMOVE (proto->rcv.buf, &proto->rcv.buf[HCL_XPKT_HDR_LEN], proto->rcv.len - HCL_XPKT_HDR_LEN); - proto->rcv.len -= HCL_XPKT_HDR_LEN; - - /* switch to the payload mode */ - if (proto->rcv.hdr.len > 0) - { - proto->rcv.state = HCL_XPROTO_RCV_PLD; - proto->rcv.len_needed = proto->rcv.hdr.len; - } - else - { - /* take shortcut */ -/* TODO: convert handle_packet as call back */ - n = handle_packet(proto, proto->rcv.hdr.type, proto->rcv.buf, proto->rcv.hdr.len); - if (n <= -1) goto fail_with_errmsg; - if (n == 0) return 0; - } - - break; - - case HCL_XPROTO_RCV_PLD: - if (proto->rcv.len < proto->rcv.hdr.len) goto carry_on; /* need more payload data */ - -/* TODO: convert handle_packet as call back */ - n = handle_packet(proto, proto->rcv.hdr.type, proto->rcv.buf, proto->rcv.hdr.len); - -/* TODO: minimize the use of HCL_MEMOVE... use the buffer */ - /* switch to the header mode */ - if (proto->rcv.hdr.len > 0) - { - HCL_MEMMOVE (proto->rcv.buf, &proto->rcv.buf[proto->rcv.hdr.len], proto->rcv.len - proto->rcv.hdr.len); - proto->rcv.len -= proto->rcv.hdr.len; - } - proto->rcv.state = HCL_XPROTO_RCV_HDR; - proto->rcv.len_needed = HCL_XPKT_HDR_LEN; - - if (n <= -1) goto fail_with_errmsg; - if (n == 0) return 0; - - break; - - default: -/* - hcl_seterrbfmt (hcl, HCL_EINTERN, "invalid request state %d", (int)proto->rcv.state); -*/ -/* TODO: call back */ - goto fail_with_errmsg; - } - -carry_on: - return 1; - -fail_with_errmsg: -// TODO: proper error handling - //send_proto_hcl_error (proto); - //HCL_LOG1 (hcl, SERVER_LOGMASK_ERROR, "Unable to compile .SCRIPT contents - %js\n", hcl_geterrmsg(worker->hcl)); - return -1; - -} - - /* ========================================================================= */ hcl_server_t* hcl_server_open (hcl_mmgr_t* mmgr, hcl_oow_t xtnsize, hcl_server_prim_t* prim, hcl_errnum_t* errnum) @@ -1520,6 +1330,7 @@ static hcl_server_worker_t* alloc_worker (hcl_server_t* server, int cli_sck, con worker->sck = cli_sck; worker->peeraddr = *peeraddr; worker->server = server; + worker->exec_runtime_event_index = HCL_TMR_INVALID_INDEX; server->dummy_hcl->vmprim.vm_gettime (server->dummy_hcl, &worker->alloc_time); /* TODO: the callback may return monotonic time. find a way to guarantee it is realtime??? */ @@ -1680,7 +1491,6 @@ static int worker_step (hcl_server_worker_t* worker) if (x <= -1) { if (errno == EINTR) goto carry_on; /* didn't read read */ - hcl_seterrwithsyserr (hcl, 0, errno); return -1; } @@ -1779,8 +1589,12 @@ static int init_worker_proto (hcl_server_worker_t* worker) { hcl_xproto_t* proto; proto_xtn_t* xtn; + hcl_xproto_cb_t cb; - proto = hcl_xproto_open(hcl_server_getmmgr(worker->server), HCL_SIZEOF(*xtn)); + HCL_MEMSET (&cb, 0, HCL_SIZEOF(cb)); + cb.on_packet = handle_packet; + + proto = hcl_xproto_open(hcl_server_getmmgr(worker->server), &cb, HCL_SIZEOF(*xtn)); if (HCL_UNLIKELY(!proto)) return -1; xtn = hcl_xproto_getxtn(proto); @@ -2754,6 +2568,3 @@ void hcl_client_freemem (hcl_client_t* client, void* ptr) { HCL_MMGR_FREE (client->_mmgr, ptr); } - - - diff --git a/lib/hcl-x.h b/lib/hcl-x.h index 7f80c2e..a1e58dc 100644 --- a/lib/hcl-x.h +++ b/lib/hcl-x.h @@ -70,6 +70,19 @@ typedef struct hcl_xpkt_hdr_t hcl_xpkt_hdr_t; typedef struct hcl_xproto_t hcl_xproto_t; +typedef int (*hcl_xproto_cb_on_packet) ( + hcl_xproto_t* proto, + hcl_xpkt_type_t type, + const void* data, + hcl_oow_t len +); + +struct hcl_xproto_cb_t +{ + hcl_xproto_cb_on_packet on_packet; +}; +typedef struct hcl_xproto_cb_t hcl_xproto_cb_t; + /* ---------------------------------------------------------------------- */ typedef struct hcl_server_proto_t hcl_server_proto_t; @@ -400,8 +413,9 @@ HCL_EXPORT void hcl_client_freemem ( /* ---------------------------------------------------------------------- */ HCL_EXPORT hcl_xproto_t* hcl_xproto_open ( - hcl_mmgr_t* mmgr, - hcl_oow_t xtnsize + hcl_mmgr_t* mmgr, + hcl_xproto_cb_t* cb, + hcl_oow_t xtnsize ); HCL_EXPORT void hcl_xproto_close ( @@ -417,6 +431,10 @@ hcl_uint8_t* hcl_xproto_getbuf ( hcl_oow_t* capa ); +int hcl_xproto_geteof ( + hcl_xproto_t* proto +); + void hcl_xproto_seteof ( hcl_xproto_t* proto, int v diff --git a/lib/hcl-xutl.h b/lib/hcl-xutl.h index 97c3bd0..2a802ee 100644 --- a/lib/hcl-xutl.h +++ b/lib/hcl-xutl.h @@ -110,17 +110,17 @@ HCL_EXPORT int hcl_bcharstosckaddr ( ); #if defined(HCL_HAVE_INLINE) - static HCL_INLINE int hcl_uchars_to_sckaddr (const hcl_uch_t* str, hcl_oow_t len, hcl_sckaddr_t* sckaddr, hcl_scklen_t* scklen) - { - return hcl_ucharstosckaddr(HCL_NULL, str, len, sckaddr, scklen); - } - static HCL_INLINE int hcl_bchars_to_sckaddr (const hcl_bch_t* str, hcl_oow_t len, hcl_sckaddr_t* sckaddr, hcl_scklen_t* scklen) - { - return hcl_bcharstosckaddr(HCL_NULL, str, len, sckaddr, scklen); - } +static HCL_INLINE int hcl_uchars_to_sckaddr (const hcl_uch_t* str, hcl_oow_t len, hcl_sckaddr_t* sckaddr, hcl_scklen_t* scklen) +{ + return hcl_ucharstosckaddr(HCL_NULL, str, len, sckaddr, scklen); +} +static HCL_INLINE int hcl_bchars_to_sckaddr (const hcl_bch_t* str, hcl_oow_t len, hcl_sckaddr_t* sckaddr, hcl_scklen_t* scklen) +{ + return hcl_bcharstosckaddr(HCL_NULL, str, len, sckaddr, scklen); +} #else - #define hcl_uchars_to_sckaddr(str,len,sckaddr,scklen) hcl_ucharstosckaddr(HCL_NULL,str,len,sckaddr,scklen) - #define hcl_bchars_to_sckaddr(str,len,sckaddr,scklen) hcl_bcharstosckaddr(HCL_NULL,str,len,sckaddr,scklen) +#define hcl_uchars_to_sckaddr(str,len,sckaddr,scklen) hcl_ucharstosckaddr(HCL_NULL,str,len,sckaddr,scklen) +#define hcl_bchars_to_sckaddr(str,len,sckaddr,scklen) hcl_bcharstosckaddr(HCL_NULL,str,len,sckaddr,scklen) #endif #if defined(HCL_OOCH_IS_UCH) diff --git a/lib/x-client.c b/lib/x-client.c new file mode 100644 index 0000000..915ecbc --- /dev/null +++ b/lib/x-client.c @@ -0,0 +1,336 @@ +/* + 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-x.h" +#include "hcl-prv.h" +#include "hcl-tmr.h" +#include "hcl-xutl.h" + +#include +#include +#include + +#define HCL_SERVER_TOKEN_NAME_ALIGN 64 +#define HCL_SERVER_WID_MAP_ALIGN 512 +#define HCL_XPROTO_REPLY_BUF_SIZE 1300 + +#if defined(_WIN32) +# include +# include +#elif defined(__OS2__) +# define INCL_DOSMODULEMGR +# define INCL_DOSPROCESS +# define INCL_DOSERRORS +# include +#elif defined(__DOS__) +# include +# include +# include +#elif defined(macintosh) +# include +#else + +# 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 +# if defined(HAVE_SYS_UIO_H) +# include +# endif +# if defined(HAVE_SYS_EPOLL_H) +# include +# endif + +# include +# include +# include +# include +# include +# include +# include +#endif + +struct client_hcl_xtn_t +{ + hcl_client_t* client; +}; +typedef struct client_hcl_xtn_t client_hcl_xtn_t; + +struct hcl_client_t +{ + hcl_oow_t _instsize; + hcl_mmgr_t* _mmgr; + hcl_cmgr_t* _cmgr; + + hcl_client_prim_t prim; + hcl_t* dummy_hcl; + + hcl_errnum_t errnum; + struct + { + hcl_ooch_t buf[HCL_ERRMSG_CAPA]; + hcl_oow_t len; + } errmsg; + + struct + { + hcl_bitmask_t trait; + hcl_bitmask_t logmask; + } cfg; +}; + + +/* ========================================================================= */ + +static void client_log_write_for_dummy (hcl_t* hcl, hcl_bitmask_t mask, const hcl_ooch_t* msg, hcl_oow_t len) +{ + client_hcl_xtn_t* xtn = (client_hcl_xtn_t*)hcl_getxtn(hcl); + hcl_client_t* client; + + client = xtn->client; + client->prim.log_write (client, mask, msg, len); +} + +hcl_client_t* hcl_client_open (hcl_mmgr_t* mmgr, hcl_oow_t xtnsize, hcl_client_prim_t* prim, hcl_errnum_t* errnum) +{ + hcl_client_t* client; + hcl_t* hcl; + client_hcl_xtn_t* xtn; + + client = (hcl_client_t*)HCL_MMGR_ALLOC(mmgr, HCL_SIZEOF(*client) + xtnsize); + if (!client) + { + if (errnum) *errnum = HCL_ESYSMEM; + return HCL_NULL; + } + + hcl = hcl_openstdwithmmgr(mmgr, HCL_SIZEOF(*xtn), errnum); + if (!hcl) + { + HCL_MMGR_FREE (mmgr, client); + return HCL_NULL; + } + + /* replace the vmprim.log_write function */ + hcl->vmprim.log_write = client_log_write_for_dummy; + + xtn = (client_hcl_xtn_t*)hcl_getxtn(hcl); + xtn->client = client; + + HCL_MEMSET (client, 0, HCL_SIZEOF(*client) + xtnsize); + client->_instsize = HCL_SIZEOF(*client); + client->_mmgr = mmgr; + client->_cmgr = hcl_get_utf8_cmgr(); + client->prim = *prim; + client->dummy_hcl = hcl; + + client->cfg.logmask = ~(hcl_bitmask_t)0; + + /* the dummy hcl 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. */ + hcl_setoption (client->dummy_hcl, HCL_LOG_MASK, &client->cfg.logmask); + hcl_setcmgr (client->dummy_hcl, client->_cmgr); + + return client; +} + +void hcl_client_close (hcl_client_t* client) +{ + hcl_close (client->dummy_hcl); + HCL_MMGR_FREE (client->_mmgr, client); +} + +int hcl_client_setoption (hcl_client_t* client, hcl_client_option_t id, const void* value) +{ + switch (id) + { + case HCL_CLIENT_TRAIT: + client->cfg.trait = *(const hcl_bitmask_t*)value; + return 0; + + case HCL_CLIENT_LOG_MASK: + client->cfg.logmask = *(const hcl_bitmask_t*)value; + if (client->dummy_hcl) + { + /* setting this affects the dummy hcl immediately. + * existing hcl instances inside worker threads won't get + * affected. new hcl instances to be created later + * is supposed to use the new value */ + hcl_setoption (client->dummy_hcl, HCL_LOG_MASK, value); + } + return 0; + } + + hcl_client_seterrnum (client, HCL_EINVAL); + return -1; +} + +int hcl_client_getoption (hcl_client_t* client, hcl_client_option_t id, void* value) +{ + switch (id) + { + case HCL_CLIENT_TRAIT: + *(hcl_bitmask_t*)value = client->cfg.trait; + return 0; + + case HCL_CLIENT_LOG_MASK: + *(hcl_bitmask_t*)value = client->cfg.logmask; + return 0; + }; + + hcl_client_seterrnum (client, HCL_EINVAL); + return -1; +} + + +void* hcl_client_getxtn (hcl_client_t* client) +{ + return (void*)((hcl_uint8_t*)client + client->_instsize); +} + +hcl_mmgr_t* hcl_client_getmmgr (hcl_client_t* client) +{ + return client->_mmgr; +} + +hcl_cmgr_t* hcl_client_getcmgr (hcl_client_t* client) +{ + return client->_cmgr; +} + +void hcl_client_setcmgr (hcl_client_t* client, hcl_cmgr_t* cmgr) +{ + client->_cmgr = cmgr; +} + +hcl_errnum_t hcl_client_geterrnum (hcl_client_t* client) +{ + return client->errnum; +} + +const hcl_ooch_t* hcl_client_geterrstr (hcl_client_t* client) +{ + return hcl_errnum_to_errstr(client->errnum); +} + +const hcl_ooch_t* hcl_client_geterrmsg (hcl_client_t* client) +{ + if (client->errmsg.len <= 0) return hcl_errnum_to_errstr(client->errnum); + return client->errmsg.buf; +} + +void hcl_client_seterrnum (hcl_client_t* client, hcl_errnum_t errnum) +{ + /*if (client->shuterr) return; */ + client->errnum = errnum; + client->errmsg.len = 0; +} + +void hcl_client_seterrbfmt (hcl_client_t* client, hcl_errnum_t errnum, const hcl_bch_t* fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + hcl_seterrbfmtv (client->dummy_hcl, errnum, fmt, ap); + va_end (ap); + + HCL_ASSERT (client->dummy_hcl, HCL_COUNTOF(client->errmsg.buf) == HCL_COUNTOF(client->dummy_hcl->errmsg.buf)); + client->errnum = errnum; + hcl_copy_oochars (client->errmsg.buf, client->dummy_hcl->errmsg.buf, HCL_COUNTOF(client->errmsg.buf)); + client->errmsg.len = client->dummy_hcl->errmsg.len; +} + +void hcl_client_seterrufmt (hcl_client_t* client, hcl_errnum_t errnum, const hcl_uch_t* fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + hcl_seterrufmtv (client->dummy_hcl, errnum, fmt, ap); + va_end (ap); + + HCL_ASSERT (client->dummy_hcl, HCL_COUNTOF(client->errmsg.buf) == HCL_COUNTOF(client->dummy_hcl->errmsg.buf)); + client->errnum = errnum; + hcl_copy_oochars (client->errmsg.buf, client->dummy_hcl->errmsg.buf, HCL_COUNTOF(client->errmsg.buf)); + client->errmsg.len = client->dummy_hcl->errmsg.len; +} + +/* ========================================================================= */ + +void hcl_client_logbfmt (hcl_client_t* client, hcl_bitmask_t mask, const hcl_bch_t* fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + hcl_logbfmtv (client->dummy_hcl, mask, fmt, ap); + va_end (ap); +} + +void hcl_client_logufmt (hcl_client_t* client, hcl_bitmask_t mask, const hcl_uch_t* fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + hcl_logufmtv (client->dummy_hcl, mask, fmt, ap); + va_end (ap); +} + +/* ========================================================================= */ + +void* hcl_client_allocmem (hcl_client_t* client, hcl_oow_t size) +{ + void* ptr; + + ptr = HCL_MMGR_ALLOC(client->_mmgr, size); + if (!ptr) hcl_client_seterrnum (client, HCL_ESYSMEM); + return ptr; +} + +void* hcl_client_callocmem (hcl_client_t* client, hcl_oow_t size) +{ + void* ptr; + + ptr = HCL_MMGR_ALLOC(client->_mmgr, size); + if (!ptr) hcl_client_seterrnum (client, HCL_ESYSMEM); + else HCL_MEMSET (ptr, 0, size); + return ptr; +} + +void* hcl_client_reallocmem (hcl_client_t* client, void* ptr, hcl_oow_t size) +{ + ptr = HCL_MMGR_REALLOC(client->_mmgr, ptr, size); + if (!ptr) hcl_client_seterrnum (client, HCL_ESYSMEM); + return ptr; +} + +void hcl_client_freemem (hcl_client_t* client, void* ptr) +{ + HCL_MMGR_FREE (client->_mmgr, ptr); +} diff --git a/lib/x-proto.c b/lib/x-proto.c new file mode 100644 index 0000000..04796cd --- /dev/null +++ b/lib/x-proto.c @@ -0,0 +1,195 @@ +/* + 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 +#include "hcl-prv.h" + +enum hcl_xproto_rcv_state_t +{ + HCL_XPROTO_RCV_HDR, + HCL_XPROTO_RCV_PLD +}; +typedef enum hcl_xproto_rcv_state_t hcl_xproto_rcv_state_t; + +struct hcl_xproto_t +{ + hcl_oow_t _instsize; + hcl_mmgr_t* _mmgr; + hcl_xproto_cb_t _cb; + + struct + { + hcl_xproto_rcv_state_t state; + hcl_oow_t len_needed; + unsigned int eof: 1; + + hcl_oow_t len; + hcl_uint8_t buf[HCL_XPKT_MAX_PLD_LEN]; + + /* normalize header of hcl_xpkt_hdr_t with combined bits into separate placeholders */ + struct + { + hcl_uint8_t id; + hcl_uint8_t type; + hcl_uint16_t len; /* this is wider than the len field of hcl_xpkt_hdr_t */ + } hdr; + } rcv; + + struct + { + + } snd; +}; + +hcl_xproto_t* hcl_xproto_open (hcl_mmgr_t* mmgr, hcl_xproto_cb_t* cb, hcl_oow_t xtnsize) +{ + hcl_xproto_t* proto; + + proto = (hcl_xproto_t*)HCL_MMGR_ALLOC(mmgr, HCL_SIZEOF(*proto) + xtnsize); + if (HCL_UNLIKELY(!proto)) return HCL_NULL; + + HCL_MEMSET (proto, 0, HCL_SIZEOF(*proto)); + proto->_instsize = HCL_SIZEOF(*proto); + proto->_mmgr = mmgr; + proto->_cb = *cb; + proto->rcv.state = HCL_XPROTO_RCV_HDR; + proto->rcv.len_needed = HCL_XPKT_HDR_LEN; + proto->rcv.eof = 0; + + return proto; +} + +void hcl_xproto_close (hcl_xproto_t* proto) +{ + HCL_MMGR_FREE (proto->_mmgr, proto); +} + +void* hcl_xproto_getxtn (hcl_xproto_t* proto) +{ + return (proto + 1); +} + +hcl_uint8_t* hcl_xproto_getbuf (hcl_xproto_t* proto, hcl_oow_t* capa) +{ + *capa = HCL_COUNTOF(proto->rcv.buf) - proto->rcv.len; + return &proto->rcv.buf[proto->rcv.len]; +} + +int hcl_xproto_geteof (hcl_xproto_t* proto) +{ + return proto->rcv.eof; +} + +void hcl_xproto_seteof (hcl_xproto_t* proto, int v) +{ + proto->rcv.eof = v; +} + +void hcl_xproto_advbuf (hcl_xproto_t* proto, hcl_oow_t inc) +{ + proto->rcv.len += inc; +} + +int hcl_xproto_ready (hcl_xproto_t* proto) +{ + /* has it received suffient data for processing? */ + return proto->rcv.len >= proto->rcv.len_needed; +} + +int hcl_xproto_process (hcl_xproto_t* proto) +{ + int n; + hcl_xpkt_hdr_t* hdr; + + switch (proto->rcv.state) + { + case HCL_XPROTO_RCV_HDR: + if (proto->rcv.len < HCL_XPKT_HDR_LEN) goto carry_on; /* need more data */ + + hdr = (hcl_xpkt_hdr_t*)proto->rcv.buf; + proto->rcv.hdr.id = hdr->id; + proto->rcv.hdr.type = hdr->type & 0x0F; + proto->rcv.hdr.len = (hcl_uint16_t)hdr->len | ((hcl_uint16_t)(hdr->type >> 4) << 8); + + /* consume the header */ + HCL_MEMMOVE (proto->rcv.buf, &proto->rcv.buf[HCL_XPKT_HDR_LEN], proto->rcv.len - HCL_XPKT_HDR_LEN); + proto->rcv.len -= HCL_XPKT_HDR_LEN; + + /* switch to the payload mode */ + if (proto->rcv.hdr.len > 0) + { + proto->rcv.state = HCL_XPROTO_RCV_PLD; + proto->rcv.len_needed = proto->rcv.hdr.len; + } + else + { + /* take shortcut */ +/* TODO: convert handle_packet as call back */ + n = proto->_cb.on_packet(proto, proto->rcv.hdr.type, proto->rcv.buf, proto->rcv.hdr.len); + if (n <= -1) goto fail_with_errmsg; + if (n == 0) return 0; + } + + break; + + case HCL_XPROTO_RCV_PLD: + if (proto->rcv.len < proto->rcv.hdr.len) goto carry_on; /* need more payload data */ + +/* TODO: convert handle_packet as call back */ + n = proto->_cb.on_packet(proto, proto->rcv.hdr.type, proto->rcv.buf, proto->rcv.hdr.len); + +/* TODO: minimize the use of HCL_MEMOVE... use the buffer */ + /* switch to the header mode */ + if (proto->rcv.hdr.len > 0) + { + HCL_MEMMOVE (proto->rcv.buf, &proto->rcv.buf[proto->rcv.hdr.len], proto->rcv.len - proto->rcv.hdr.len); + proto->rcv.len -= proto->rcv.hdr.len; + } + proto->rcv.state = HCL_XPROTO_RCV_HDR; + proto->rcv.len_needed = HCL_XPKT_HDR_LEN; + + if (n <= -1) goto fail_with_errmsg; + if (n == 0) return 0; + + break; + + default: +/* + hcl_seterrbfmt (hcl, HCL_EINTERN, "invalid request state %d", (int)proto->rcv.state); +*/ +/* TODO: call back */ + goto fail_with_errmsg; + } + +carry_on: + return 1; + +fail_with_errmsg: +// TODO: proper error handling + //send_proto_hcl_error (proto); + //HCL_LOG1 (hcl, SERVER_LOGMASK_ERROR, "Unable to compile .SCRIPT contents - %js\n", hcl_geterrmsg(worker->hcl)); + return -1; + +} + diff --git a/lib/x-server.c b/lib/x-server.c new file mode 100644 index 0000000..9592e32 --- /dev/null +++ b/lib/x-server.c @@ -0,0 +1,2285 @@ +/* + 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-x.h" +#include "hcl-prv.h" +#include "hcl-tmr.h" +#include "hcl-xutl.h" + +#include +#include +#include + +#define HCL_SERVER_TOKEN_NAME_ALIGN 64 +#define HCL_SERVER_WID_MAP_ALIGN 512 +#define HCL_XPROTO_REPLY_BUF_SIZE 1300 + +#if defined(_WIN32) +# include +# include +#elif defined(__OS2__) +# define INCL_DOSMODULEMGR +# define INCL_DOSPROCESS +# define INCL_DOSERRORS +# include +#elif defined(__DOS__) +# include +# include +# include +#elif defined(macintosh) +# include +#else + +# 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 +# if defined(HAVE_SYS_UIO_H) +# include +# endif +# if defined(HAVE_SYS_EPOLL_H) +# include +# endif + +# include +# include +# include +# include +# include +# include +# include +#endif + +struct bb_t +{ + char buf[1024]; + hcl_oow_t pos; + hcl_oow_t len; + int fd; + hcl_bch_t* fn; +}; +typedef struct bb_t bb_t; + +struct proto_xtn_t +{ + hcl_server_worker_t* worker; +}; +typedef struct proto_xtn_t proto_xtn_t; + +struct worker_hcl_xtn_t +{ + hcl_server_worker_t* worker; + int vm_running; +}; +typedef struct worker_hcl_xtn_t worker_hcl_xtn_t; + +struct server_hcl_xtn_t +{ + hcl_server_t* server; +}; +typedef struct server_hcl_xtn_t server_hcl_xtn_t; + +/* ---------------------------------- */ + +enum hcl_server_worker_state_t +{ + HCL_SERVER_WORKER_STATE_DEAD = 0, + HCL_SERVER_WORKER_STATE_ALIVE = 1, + HCL_SERVER_WORKER_STATE_ZOMBIE = 2 /* the worker is not chained in the server's client list */ +}; +typedef enum hcl_server_worker_state_t hcl_server_worker_state_t; + +enum hcl_server_worker_opstate_t +{ + HCL_SERVER_WORKER_OPSTATE_IDLE = 0, + HCL_SERVER_WORKER_OPSTATE_ERROR = 1, + HCL_SERVER_WORKER_OPSTATE_WAIT = 2, + HCL_SERVER_WORKER_OPSTATE_READ = 3, + HCL_SERVER_WORKER_OPSTATE_COMPILE = 4, + HCL_SERVER_WORKER_OPSTATE_EXECUTE = 5 +}; +typedef enum hcl_server_worker_opstate_t hcl_server_worker_opstate_t; + +struct hcl_server_worker_t +{ + pthread_t thr; + hcl_oow_t wid; + + int sck; + hcl_sckaddr_t peeraddr; + + int claimed; + + hcl_ntime_t alloc_time; + hcl_server_worker_state_t state; + hcl_server_worker_opstate_t opstate; + hcl_tmr_index_t exec_runtime_event_index; + hcl_xproto_t* proto; + hcl_t* hcl; + + hcl_server_t* server; + hcl_server_worker_t* prev_worker; + hcl_server_worker_t* next_worker; +}; + +struct hcl_server_wid_map_data_t +{ + int used; + union + { + hcl_server_worker_t* worker; + hcl_oow_t next; + } u; +}; +typedef struct hcl_server_wid_map_data_t hcl_server_wid_map_data_t; + +typedef struct hcl_server_listener_t hcl_server_listener_t; +struct hcl_server_listener_t +{ + int sck; + hcl_sckaddr_t sckaddr; + hcl_server_listener_t* next_listener; +}; + +struct hcl_server_t +{ + hcl_oow_t _instsize; + hcl_mmgr_t* _mmgr; + hcl_cmgr_t* _cmgr; + hcl_server_prim_t prim; + + /* [NOTE] + * this dummy_hcl is used when the main thread requires logging mostly. + * as there is no explicit locking when calling HCL_LOG() functions, + * the code must ensure that the logging functions are called in the + * context of the main server thraed only. error message setting is + * also performed in the main thread context for the same reason. + * + * however, you may have noticed mixed use of HCL_ASSERT with dummy_hcl + * in both the server thread context and the client thread contexts. + * it should be ok as assertion is only for debugging and it's operation + * is thread safe. */ + hcl_t* dummy_hcl; + + hcl_tmr_t* tmr; + + hcl_errnum_t errnum; + struct + { + hcl_ooch_t buf[HCL_ERRMSG_CAPA]; + hcl_oow_t len; + } errmsg; + int stopreq; + + struct + { + hcl_bitmask_t trait; + hcl_bitmask_t logmask; + hcl_oow_t worker_stack_size; + hcl_oow_t worker_max_count; + hcl_ntime_t worker_idle_timeout; + hcl_oow_t actor_heap_size; + hcl_ntime_t actor_max_runtime; + hcl_ooch_t script_include_path[HCL_PATH_MAX + 1]; + void* module_inctx; + } cfg; + + struct + { + int ep_fd; + struct epoll_event ev_buf[128]; + hcl_server_listener_t* head; + hcl_oow_t count; + } listener; + + struct + { + hcl_server_worker_t* head; + hcl_server_worker_t* tail; + hcl_oow_t count; + } worker_list[2]; /* DEAD and ALIVE oly. ZOMBIEs are not chained here */ + + struct + { + hcl_server_wid_map_data_t* ptr; + hcl_oow_t capa; + hcl_oow_t free_first; + hcl_oow_t free_last; + } wid_map; /* worker's id map */ + + int mux_pipe[2]; /* pipe to break the blocking multiplexer in the main server loop */ + + pthread_mutex_t worker_mutex; + pthread_mutex_t tmr_mutex; + pthread_mutex_t log_mutex; +}; + +/* ========================================================================= */ +static int send_stdout_bytes (hcl_xproto_t* proto, const hcl_bch_t* data, hcl_oow_t len); + +#if defined(HCL_OOCH_IS_UCH) +static int send_stdout_chars (hcl_xproto_t* proto, const hcl_ooch_t* data, hcl_oow_t len); +#else +#define send_stdout_chars(proto,data,len) send_stdout_bytes(proto,data,len) +#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 (HCL_IS_PATH_SEP(*p)) last = p; + } + + return (last == HCL_NULL)? path: (last + 1); +} + +static HCL_INLINE int open_read_stream (hcl_t* hcl, hcl_io_cciarg_t* arg) +{ + worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); + bb_t* bb = HCL_NULL; + hcl_server_t* server; + + server = xtn->worker->server; + + if (arg->includer) + { + /* includee */ + /* TOOD: Do i need to skip prepending the include path if the included path is an absolute path? + * it may be good for security if i don't skip it. we can lock the included files in a given directory */ + 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_count_bcstr(arg->name); + #endif + + fn = ((bb_t*)arg->includer->handle)->fn; + if (fn[0] == '\0' && server->cfg.script_include_path[0] != '\0') + { + #if defined(HCL_OOCH_IS_UCH) + if (hcl_convootobcstr(hcl, server->cfg.script_include_path, &ucslen, HCL_NULL, &parlen) <= -1) goto oops; + #else + parlen = hcl_count_bcstr(server->cfg.script_include_path); + #endif + } + else + { + fb = get_base_name(fn); + parlen = fb - fn; + } + + bb = (bb_t*)hcl_callocmem(hcl, HCL_SIZEOF(*bb) + (HCL_SIZEOF(hcl_bch_t) * (parlen + bcslen + 2))); + if (!bb) goto oops; + + bb->fn = (hcl_bch_t*)(bb + 1); + if (fn[0] == '\0' && server->cfg.script_include_path[0] != '\0') + { + #if defined(HCL_OOCH_IS_UCH) + hcl_convootobcstr (hcl, server->cfg.script_include_path, &ucslen, bb->fn, &parlen); + #else + hcl_copy_bchars (bb->fn, server->cfg.script_include_path, parlen); + #endif + if (!HCL_IS_PATH_SEP(bb->fn[parlen])) bb->fn[parlen++] = HCL_DFL_PATH_SEP; /* +2 was used in hcl_callocmem() for this (+1 for this, +1 for '\0' */ + } + else + { + hcl_copy_bchars (bb->fn, fn, parlen); + } + + #if defined(HCL_OOCH_IS_UCH) + hcl_convootobcstr (hcl, arg->name, &ucslen, &bb->fn[parlen], &bcslen); + #else + hcl_copy_bcstr (&bb->fn[parlen], bcslen + 1, arg->name); + #endif + bb->fd = open(bb->fn, O_RDONLY, 0); + + if (bb->fd <= -1) + { + hcl_seterrnum (hcl, HCL_EIOERR); + goto oops; + } + } + 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; + + /* copy ane empty string as a main stream's name */ + bb->fn = (hcl_bch_t*)(bb + 1); + hcl_copy_bcstr (bb->fn, pathlen + 1, ""); + + bb->fd = xtn->worker->sck; + } + + HCL_ASSERT (hcl, bb->fd >= 0); + + arg->handle = bb; + return 0; + +oops: + if (bb) + { + if (bb->fd >= 0 && bb->fd != xtn->worker->sck) close (bb->fd); + hcl_freemem (hcl, bb); + } + return -1; +} + +static HCL_INLINE int close_read_stream (hcl_t* hcl, hcl_io_cciarg_t* arg) +{ + worker_hcl_xtn_t* xtn = (worker_hcl_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->worker->sck) close (bb->fd); + hcl_freemem (hcl, bb); + + arg->handle = HCL_NULL; + return 0; +} + +static HCL_INLINE int read_read_stream (hcl_t* hcl, hcl_io_cciarg_t* arg) +{ + worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); + bb_t* bb; + hcl_oow_t bcslen, ucslen, remlen; + hcl_server_worker_t* worker; + ssize_t x; + int y; + + bb = (bb_t*)arg->handle; + HCL_ASSERT (hcl, bb != HCL_NULL && bb->fd >= 0); + + worker = xtn->worker; + +start_over: + if (arg->includer) + { + /* includee */ + if (HCL_UNLIKELY(worker->server->stopreq)) + { + hcl_seterrbfmt (hcl, HCL_EGENERIC, "stop requested"); + return -1; + } + + x = read(bb->fd, &bb->buf[bb->len], HCL_COUNTOF(bb->buf) - bb->len); + if (x <= -1) + { + if (errno == EINTR) goto start_over; + hcl_seterrwithsyserr (hcl, 0, errno); + return -1; + } + + bb->len += x; + } + else + { + /* main stream */ + hcl_server_t* server; + + HCL_ASSERT (hcl, bb->fd == worker->sck); + server = worker->server; + + while (1) + { + int n; + struct pollfd pfd; + int tmout, actual_tmout; + + if (HCL_UNLIKELY(server->stopreq)) + { + hcl_seterrbfmt (hcl, HCL_EGENERIC, "stop requested"); + return -1; + } + + tmout = HCL_SECNSEC_TO_MSEC(server->cfg.worker_idle_timeout.sec, server->cfg.worker_idle_timeout.nsec); + actual_tmout = (tmout <= 0)? 10000: tmout; + + pfd.fd = bb->fd; + pfd.events = POLLIN | POLLERR; + n = poll(&pfd, 1, actual_tmout); + if (n <= -1) + { + if (errno == EINTR) goto start_over; + hcl_seterrwithsyserr (hcl, 0, errno); + return -1; + } + else if (n >= 1) break; + + /* timed out - no activity on the pfd */ + if (tmout > 0) + { + hcl_seterrbfmt (hcl, HCL_EGENERIC, "no activity on the worker socket %d", bb->fd); + return -1; + } + } + + x = recv(bb->fd, &bb->buf[bb->len], HCL_COUNTOF(bb->buf) - bb->len, 0); + if (x <= -1) + { + if (errno == EINTR) goto start_over; + hcl_seterrwithsyserr (hcl, 0, errno); + return -1; + } + + bb->len += x; + } + +#if defined(HCL_OOCH_IS_UCH) + bcslen = bb->len; + ucslen = HCL_COUNTOF(arg->buf.c); + y = hcl_convbtooochars(hcl, bb->buf, &bcslen, arg->buf.c, &ucslen); + if (y <= -1 && ucslen <= 0) + { + if (y == -3 && x != 0) goto start_over; /* incomplete sequence and not EOF yet */ + 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.b))? bb->len: HCL_COUNTOF(arg->buf.b); + ucslen = bcslen; + hcl_copy_bchars (arg->buf.b, bb->buf, bcslen); +#endif + + remlen = bb->len - bcslen; + if (remlen > 0) HCL_MEMMOVE (bb->buf, &bb->buf[bcslen], remlen); + bb->len = remlen; + + arg->xlen = ucslen; + return 0; +} + + +static int read_handler (hcl_t* hcl, hcl_io_cmd_t cmd, void* arg) +{ + switch (cmd) + { + case HCL_IO_OPEN: + return open_read_stream(hcl, (hcl_io_cciarg_t*)arg); + + case HCL_IO_CLOSE: + return close_read_stream(hcl, (hcl_io_cciarg_t*)arg); + + case HCL_IO_READ: + return read_read_stream(hcl, (hcl_io_cciarg_t*)arg); + + default: + hcl_seterrnum (hcl, HCL_EINTERN); + return -1; + } +} + +static int scan_handler (hcl_t* hcl, hcl_io_cmd_t cmd, void* arg) +{ + switch (cmd) + { + case HCL_IO_OPEN: + return 0; + + case HCL_IO_CLOSE: + return 0; + + case HCL_IO_READ: +#if 0 + { + worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); + hcl_io_udiarg_t* inarg = (hcl_io_udiarg_t*)arg; + +// what if it writes a request to require more input?? + if (hcl_xproto_handle_incoming(xtn->proto) <= -1) + { + } + } +#else + /* TODO: read from the input buffer or pipe*/ +#endif + + default: + hcl_seterrnum (hcl, HCL_EINTERN); + return -1; + } +} + +static int print_handler (hcl_t* hcl, hcl_io_cmd_t cmd, void* arg) +{ + switch (cmd) + { + case HCL_IO_OPEN: +printf ("IO OPEN SOMETHING...........\n"); + return 0; + + case HCL_IO_CLOSE: +printf ("IO CLOSE SOMETHING...........\n"); + return 0; + + case HCL_IO_WRITE: + { + worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); + hcl_io_udoarg_t* outarg = (hcl_io_udoarg_t*)arg; + +printf ("IO WRITE SOMETHING...........\n"); + if (send_stdout_chars(xtn->worker->proto, outarg->ptr, outarg->len) <= -1) + { + /* TODO: change error code and message. propagage the errormessage from proto */ + hcl_seterrbfmt (hcl, HCL_EIOERR, "failed to write message via proto"); + + /* writing failure on the socket is a critical failure. + * execution must get aborted */ + hcl_abort (hcl); + return -1; + } + + outarg->xlen = outarg->len; + return 0; + } + case HCL_IO_WRITE_BYTES: + { + worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); + hcl_io_udoarg_t* outarg = (hcl_io_udoarg_t*)arg; + +printf ("IO WRITE SOMETHING BYTES...........\n"); + if (send_stdout_bytes(xtn->worker->proto, outarg->ptr, outarg->len) <= -1) + { + /* TODO: change error code and message. propagage the errormessage from proto */ + hcl_seterrbfmt (hcl, HCL_EIOERR, "failed to write message via proto"); + + /* writing failure on the socket is a critical failure. + * execution must get aborted */ + hcl_abort (hcl); + return -1; + } + outarg->xlen = outarg->len; + return 0; + } + + default: + hcl_seterrnum (hcl, HCL_EINTERN); + return -1; + } +} + +/* ========================================================================= */ + +static void server_log_write (hcl_t* hcl, hcl_bitmask_t mask, const hcl_ooch_t* msg, hcl_oow_t len) +{ + worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); + hcl_server_t* server; + + server = xtn->worker->server; + pthread_mutex_lock (&server->log_mutex); + server->prim.log_write (server, xtn->worker->wid, mask, msg, len); + pthread_mutex_unlock (&server->log_mutex); +} + +static void server_log_write_for_dummy (hcl_t* hcl, hcl_bitmask_t mask, const hcl_ooch_t* msg, hcl_oow_t len) +{ + server_hcl_xtn_t* xtn = (server_hcl_xtn_t*)hcl_getxtn(hcl); + hcl_server_t* server; + + server = xtn->server; + pthread_mutex_lock (&server->log_mutex); + server->prim.log_write (server, HCL_SERVER_WID_INVALID, mask, msg, len); + pthread_mutex_unlock (&server->log_mutex); +} + +/* ========================================================================= */ + +static int vm_startup (hcl_t* hcl) +{ + worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); + xtn->vm_running = 1; + return 0; +} + +static void vm_cleanup (hcl_t* hcl) +{ + worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); + xtn->vm_running = 0; +} + +static void vm_checkbc (hcl_t* hcl, hcl_oob_t bcode) +{ + worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); + if (xtn->worker->server->stopreq) hcl_abort(hcl); +} + +/* +static void gc_hcl (hcl_t* hcl) +{ + worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); +} + +static void fini_hcl (hcl_t* hcl) +{ + worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); +} +*/ +/* ========================================================================= */ + + +static hcl_server_worker_t* proto_to_worker (hcl_xproto_t* proto) +{ + proto_xtn_t* prtxtn; + prtxtn = (proto_xtn_t*)hcl_xproto_getxtn(proto); + return prtxtn->worker; +} + +/* ========================================================================= */ + +#define SERVER_LOGMASK_INFO (HCL_LOG_INFO | HCL_LOG_APP) +#define SERVER_LOGMASK_ERROR (HCL_LOG_ERROR | HCL_LOG_APP) + +static int on_fed_cnode (hcl_t* hcl, hcl_cnode_t* obj) +{ + /*worker_hcl_xtn_t* xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl);*/ + /*hcl_xproto_t* proto = xtn->proto;*/ + int flags = 0; + +printf ("on_fed_cnode......\n"); + /* the compile error must not break the input loop. + * this function returns 0 to go on despite a compile-time error. + * + * if a single line or continued lines contain multiple expressions, + * execution is delayed until the last expression is compiled. */ + +printf ("COMPILING hcl_copingll......\n"); + if (hcl_compile(hcl, obj, flags) <= -1) + { +hcl_logbfmt (hcl, HCL_LOG_STDERR, "COMPILER ERROR - %js\n", hcl_geterrmsg(hcl)); +#if 0 + print_error(hcl, "failed to compile"); + xtn->feed.pos = xtn->feed.len; /* arrange to discard the rest of the line */ + show_prompt (hcl, 0); +#endif + } + + return 0; +} + +static void exec_runtime_handler (hcl_tmr_t* tmr, const hcl_ntime_t* now, hcl_tmr_event_t* evt) +{ + /* [NOTE] this handler is executed in the main server thread + * when it calls hcl_tmr_fire(). */ + hcl_server_worker_t* worker; + + worker = proto_to_worker((hcl_xproto_t*)evt->ctx); +/* TODO: can we use worker->hcl for logging before abort?? */ + HCL_LOG1 (worker->server->dummy_hcl, SERVER_LOGMASK_INFO, "Aborting script execution for max_actor_runtime exceeded [%zu]\n", worker->wid); + hcl_abort (worker->hcl); +} + +static void exec_runtime_updater (hcl_tmr_t* tmr, hcl_tmr_index_t old_index, hcl_tmr_index_t new_index, hcl_tmr_event_t* evt) +{ + /* [NOTE] this handler is executed in the main server thread + * when it calls hcl_tmr_fire() */ + hcl_xproto_t* proto; + hcl_server_worker_t* worker; + + proto = (hcl_xproto_t*)evt->ctx; + worker = proto_to_worker(proto); + HCL_ASSERT (worker->hcl, worker->exec_runtime_event_index == old_index); + + /* the event is being removed by hcl_tmr_fire() or by hcl_tmr_delete() + * if new_index is HCL_TMR_INVALID_INDEX. it's being updated if not. */ + worker->exec_runtime_event_index = new_index; +} + +static int insert_exec_timer (hcl_xproto_t* proto, const hcl_ntime_t* tmout) +{ + /* [NOTE] this is executed in the worker thread */ + + hcl_tmr_event_t event; + hcl_tmr_index_t index; + hcl_server_worker_t* worker; + hcl_server_t* server; + + worker = proto_to_worker(proto); + server = worker->server; + + HCL_ASSERT (worker->hcl, worker->exec_runtime_event_index == HCL_TMR_INVALID_INDEX); + + HCL_MEMSET (&event, 0, HCL_SIZEOF(event)); + event.ctx = proto; + worker->hcl->vmprim.vm_gettime (worker->hcl, &event.when); + HCL_ADD_NTIME (&event.when, &event.when, tmout); + event.handler = exec_runtime_handler; + event.updater = exec_runtime_updater; + + pthread_mutex_lock (&server->tmr_mutex); + index = hcl_tmr_insert(server->tmr, &event); + worker->exec_runtime_event_index = index; + if (index != HCL_TMR_INVALID_INDEX) + { + /* inform the server of timer event change */ + write (server->mux_pipe[1], "X", 1); /* don't care even if it fails */ + } + pthread_mutex_unlock (&server->tmr_mutex); + + return (index == HCL_TMR_INVALID_INDEX)? -1: 0; +} + +static void delete_exec_timer (hcl_xproto_t* proto) +{ + /* [NOTE] this is executed in the worker thread. if the event has been fired + * in the server thread, worker->exec_runtime_event_index should be + * HCL_TMR_INVALID_INDEX as set by exec_runtime_handler */ + hcl_server_worker_t* worker; + hcl_server_t* server; + + worker = proto_to_worker(proto); + server = worker->server; + + pthread_mutex_lock (&server->tmr_mutex); + if (worker->exec_runtime_event_index != HCL_TMR_INVALID_INDEX) + { + /* the event has not been fired yet. let's delete it + * if it has been fired, the index it shall be HCL_TMR_INVALID_INDEX already */ + + hcl_tmr_delete (server->tmr, worker->exec_runtime_event_index); + HCL_ASSERT (worker->hcl, worker->exec_runtime_event_index == HCL_TMR_INVALID_INDEX); + /*worker->exec_runtime_event_index = HCL_TMR_INVALID_INDEX; */ + } + pthread_mutex_unlock (&server->tmr_mutex); +} + +static int execute_script (hcl_xproto_t* proto, const hcl_bch_t* trigger) +{ + hcl_oop_t obj; + const hcl_ooch_t* failmsg = HCL_NULL; + hcl_server_worker_t* worker; + hcl_server_t* server; + + worker = proto_to_worker(proto); + server = worker->server; + +#if 0 + hcl_xproto_start_reply (proto); +#endif + if (server->cfg.actor_max_runtime.sec <= 0 && server->cfg.actor_max_runtime.sec <= 0) + { + obj = hcl_execute(worker->hcl); + if (!obj) failmsg = hcl_geterrmsg(worker->hcl); + } + else + { + if (insert_exec_timer(proto, &server->cfg.actor_max_runtime) <= -1) + { + HCL_LOG0 (worker->hcl, SERVER_LOGMASK_ERROR, "Cannot start execution timer\n"); + hcl_seterrbfmt (worker->hcl, HCL_ESYSMEM, "cannot start execution timer"); /* i do this just to compose the error message */ + failmsg = hcl_geterrmsg(worker->hcl); + } + else + { + obj = hcl_execute(worker->hcl); + if (!obj) failmsg = hcl_geterrmsg(worker->hcl); + delete_exec_timer (proto); + } + } + +#if 0 + if (hcl_xproto_end_reply(proto, failmsg) <= -1) + { + HCL_LOG1 (worker->hcl, SERVER_LOGMASK_ERROR, "Cannot finalize reply for %hs\n", trigger); + return -1; + } +#endif + + return 0; +} + + +static void send_error_message (hcl_xproto_t* proto, const hcl_ooch_t* errmsg) +{ +#if 0 + hcl_xproto_start_reply (proto); + if (hcl_xproto_end_reply(proto, errmsg) <= -1) + { + HCL_LOG1 (proto->hcl, SERVER_LOGMASK_ERROR, "Unable to send error message - %s\n", errmsg); + } +#endif +} + +static void reformat_synerr (hcl_t* hcl) +{ + hcl_synerr_t synerr; + const hcl_ooch_t* orgmsg; + static hcl_ooch_t nullstr[] = { '\0' }; + + hcl_getsynerr (hcl, &synerr); + + orgmsg = hcl_backuperrmsg(hcl); + hcl_seterrbfmt ( + hcl, HCL_ESYNERR, + "%js%hs%.*js at %js%hsline %zu column %zu", + orgmsg, + (synerr.tgt.len > 0? " near ": ""), + synerr.tgt.len, synerr.tgt.val, + (synerr.loc.file? synerr.loc.file: nullstr), + (synerr.loc.file? " ": ""), + synerr.loc.line, synerr.loc.colm + ); +} + +static void send_proto_hcl_error (hcl_xproto_t* proto) +{ + hcl_server_worker_t* worker; + worker = proto_to_worker(proto); + if (HCL_ERRNUM(worker->hcl) == HCL_ESYNERR) reformat_synerr (worker->hcl); + send_error_message (proto, hcl_geterrmsg(worker->hcl)); +} + +static void show_server_workers (hcl_xproto_t* proto) +{ + hcl_server_worker_t* worker, * w; + hcl_server_t* server; + + worker = proto_to_worker(proto); + server = worker->server; + + pthread_mutex_lock (&server->worker_mutex); + for (w = server->worker_list[HCL_SERVER_WORKER_STATE_ALIVE].head; w; w = w->next_worker) + { + /* TODO: implement this better... */ + hcl_prbfmt (worker->hcl, "%zu %d %d\n", w->wid, w->sck, 1000); + } + pthread_mutex_unlock (&server->worker_mutex); +} + +static int kill_server_worker (hcl_xproto_t* proto, hcl_oow_t wid) +{ + hcl_server_worker_t* worker; + hcl_server_t* server; + int xret = 0; + + worker = proto_to_worker(proto); + server = worker->server; + + pthread_mutex_lock (&server->worker_mutex); + if (wid >= server->wid_map.capa) + { + hcl_server_seterrnum (server, HCL_ENOENT); + xret = -1; + } + else + { + hcl_server_worker_t* worker; + + if (!server->wid_map.ptr[wid].used) + { + hcl_server_seterrnum (server, HCL_ENOENT); + xret = -1; + } + else + { + worker = server->wid_map.ptr[wid].u.worker; + if (!worker) + { + hcl_server_seterrnum (server, HCL_ENOENT); + xret = -1; + } + else + { + if (worker->sck) shutdown (worker->sck, SHUT_RDWR); + if (worker->hcl) hcl_abort (worker->hcl); + } + } + } + pthread_mutex_unlock (&server->worker_mutex); + return xret; +} + +static int handle_packet (hcl_xproto_t* proto, hcl_xpkt_type_t type, const void* data, hcl_oow_t len) +{ + hcl_server_worker_t* worker; + hcl_t* hcl; + + worker = proto_to_worker(proto); + hcl = worker->hcl; + +printf ("HANDLE PACKET TYPE => %d\n", type); + switch (type) + { + case HCL_XPKT_CODE: +printf ("FEEDING [%.*s]\n", (int)len, data); + if (hcl_feedbchars(hcl, data, len) <= -1) + { + /* TODO: backup error message...and create a new message */ + goto oops; + } + break; + + case HCL_XPKT_EXECUTE: + { + hcl_oop_t retv; +printf ("EXECUTING hcl_executing......\n"); + + hcl_decode (hcl, hcl_getcode(hcl), 0, hcl_getbclen(hcl)); + + retv = hcl_execute(hcl); + hcl_flushudio (hcl); + hcl_clearcode (hcl); + if (!retv) + { + /* TODO: backup error message...and create a new message */ + goto oops; + } + + break; + } + + case HCL_XPKT_STDIN: + /* store ... push stdin pipe... */ + /*if (hcl_feedstdin() <= -1) */ + break; + + case HCL_XPKT_LIST_WORKERS: + break; + + case HCL_XPKT_KILL_WORKER: + break; + + case HCL_XPKT_DISCONNECT: + return 0; /* disconnect received */ + + default: + /* unknown packet type */ + /* TODO: proper error message */ + goto oops; + } + + return 1; + + +oops: + return -1; +} + +static int send_iov (int sck, struct iovec* iov, int count) +{ + int index = 0; + + while (1) + { + ssize_t nwritten; + struct msghdr msg; + + memset (&msg, 0, HCL_SIZEOF(msg)); + msg.msg_iov = (struct iovec*)&iov[index]; + msg.msg_iovlen = count - index; + nwritten = sendmsg(sck, &msg, 0); + if (nwritten <= -1) + { + /* error occurred inside the worker thread shouldn't affect the error information + * in the server object. so here, i just log a message */ + fprintf (stderr, "Unable to sendmsg on %d - %s\n", sck, 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; + } + + return 0; +} + +static int send_stdout_bytes (hcl_xproto_t* proto, const hcl_bch_t* data, hcl_oow_t len) +{ + hcl_server_worker_t* worker; + hcl_xpkt_hdr_t hdr; + struct iovec iov[2]; + const hcl_bch_t* ptr, * cur, * end; + hcl_uint16_t seglen; + + worker = proto_to_worker(proto); + + ptr = cur = data; + end = data + len; + +printf ("SENDING BYTES [%.*s]\n", (int)len, data); + while (ptr < end) + { + while (cur != end && cur - ptr < HCL_XPKT_MAX_PLD_LEN) cur++; + + seglen = cur - ptr; + + hdr.id = 1; /* TODO: */ + hdr.type = HCL_XPKT_STDOUT | (((seglen >> 8) & 0x0F) << 4); + hdr.len = seglen & 0xFF; + + iov[0].iov_base = &hdr; + iov[0].iov_len = HCL_SIZEOF(hdr); + iov[1].iov_base = ptr; + iov[1].iov_len = seglen; + + if (send_iov(worker->sck, iov, 2) <= -1) + { + /* TODO: error message */ + return -1; + } + + ptr = cur; + } + + return 0; +} + +#if defined(HCL_OOCH_IS_UCH) +static int send_stdout_chars (hcl_xproto_t* proto, const hcl_ooch_t* data, hcl_oow_t len) +{ + hcl_server_worker_t* worker; + const hcl_ooch_t* ptr, * end; + hcl_bch_t tmp[256]; + hcl_oow_t tln, pln; + int n; + + worker = proto_to_worker(proto); + + ptr = data; + end = data + len; + + while (ptr < end) + { + pln = end - ptr; + tln = HCL_COUNTOF(tmp); + n = hcl_convutobchars(worker->hcl, ptr, &pln, tmp, &tln); + if (n <= -1 && n != -2) return -1; + + if (send_stdout_bytes(proto, tmp, tln) <= -1) return -1; + ptr += pln; + } + + return 0; + +} +#endif + +/* ========================================================================= */ + +hcl_server_t* hcl_server_open (hcl_mmgr_t* mmgr, hcl_oow_t xtnsize, hcl_server_prim_t* prim, hcl_errnum_t* errnum) +{ + hcl_server_t* server = HCL_NULL; + hcl_t* hcl = HCL_NULL; + hcl_tmr_t* tmr = HCL_NULL; + server_hcl_xtn_t* xtn; + int pfd[2], fcv; + hcl_bitmask_t trait; + + server = (hcl_server_t*)HCL_MMGR_ALLOC(mmgr, HCL_SIZEOF(*server) + xtnsize); + if (!server) + { + if (errnum) *errnum = HCL_ESYSMEM; + return HCL_NULL; + } + + hcl = hcl_openstdwithmmgr(mmgr, HCL_SIZEOF(*xtn), errnum); + if (!hcl) goto oops; + + /* replace the vmprim.log_write function */ + hcl->vmprim.log_write = server_log_write_for_dummy; + + tmr = hcl_tmr_open(hcl, 0, 1024); /* TOOD: make the timer's default size configurable */ + if (!tmr) + { + if (errnum) *errnum = HCL_ESYSMEM; + goto oops; + } + + if (pipe(pfd) <= -1) + { + if (errnum) *errnum = hcl->vmprim.syserrstrb(hcl, 0, errno, HCL_NULL, 0); + goto oops; + } + +#if defined(O_NONBLOCK) || defined(O_CLOEXEC) + fcv = fcntl(pfd[0], F_GETFD, 0); + if (fcv >= 0) + { + #if defined(O_NONBLOCK) + fcv |= O_NONBLOCK; + #endif + #if defined(O_CLOEXEC) + fcv |= O_CLOEXEC; + #endif + fcntl(pfd[0], F_SETFD, fcv); + } + + fcv = fcntl(pfd[1], F_GETFD, 0); + if (fcv >= 0) + { + #if defined(O_NONBLOCK) + fcv |= O_NONBLOCK; + #endif + #if defined(O_CLOEXEC) + fcv |= O_CLOEXEC; + #endif + fcntl(pfd[1], F_SETFD, fcv); + } +#endif + + xtn = (server_hcl_xtn_t*)hcl_getxtn(hcl); + xtn->server = server; + + HCL_MEMSET (server, 0, HCL_SIZEOF(*server) + xtnsize); + server->_instsize = HCL_SIZEOF(*server); + server->_mmgr = mmgr; + server->_cmgr = hcl_get_utf8_cmgr(); + server->prim = *prim; + server->dummy_hcl = hcl; + server->tmr = tmr; + + server->cfg.logmask = ~(hcl_bitmask_t)0; + server->cfg.worker_stack_size = 512000UL; + server->cfg.actor_heap_size = 512000UL; + + HCL_INIT_NTIME (&server->cfg.worker_idle_timeout, 0, 0); + HCL_INIT_NTIME (&server->cfg.actor_max_runtime, 0, 0); + + server->mux_pipe[0] = pfd[0]; + server->mux_pipe[1] = pfd[1]; + + server->wid_map.free_first = HCL_SERVER_WID_INVALID; + server->wid_map.free_last = HCL_SERVER_WID_INVALID; + + server->listener.ep_fd = -1; + + pthread_mutex_init (&server->worker_mutex, HCL_NULL); + pthread_mutex_init (&server->tmr_mutex, HCL_NULL); + pthread_mutex_init (&server->log_mutex, HCL_NULL); + + /* the dummy hcl is used for this server 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. */ + hcl_setoption (server->dummy_hcl, HCL_LOG_MASK, &server->cfg.logmask); + hcl_setcmgr (server->dummy_hcl, hcl_server_getcmgr(server)); + hcl_getoption (server->dummy_hcl, HCL_TRAIT, &trait); +#if defined(HCL_BUILD_DEBUG) + if (server->cfg.trait & HCL_SERVER_TRAIT_DEBUG_GC) trait |= HCL_TRAIT_DEBUG_GC; + if (server->cfg.trait & HCL_SERVER_TRAIT_DEBUG_BIGINT) trait |= HCL_TRAIT_DEBUG_BIGINT; +#endif + hcl_setoption (server->dummy_hcl, HCL_TRAIT, &trait); + + return server; + +oops: + /* NOTE: pipe should be closed if jump to here is made after pipe() above */ + if (tmr) hcl_tmr_close (tmr); + if (hcl) hcl_close (hcl); + if (server) HCL_MMGR_FREE (mmgr, server); + + return HCL_NULL; +} + +void hcl_server_close (hcl_server_t* server) +{ + HCL_ASSERT (server->dummy_hcl, server->listener.head == HCL_NULL); + HCL_ASSERT (server->dummy_hcl, server->listener.count == 0); + HCL_ASSERT (server->dummy_hcl, server->listener.ep_fd == -1); + + if (server->wid_map.ptr) + { + hcl_server_freemem(server, server->wid_map.ptr); + server->wid_map.capa = 0; + server->wid_map.free_first = HCL_SERVER_WID_INVALID; + server->wid_map.free_last = HCL_SERVER_WID_INVALID; + } + + pthread_mutex_destroy (&server->log_mutex); + pthread_mutex_destroy (&server->tmr_mutex); + pthread_mutex_destroy (&server->worker_mutex); + + close (server->mux_pipe[0]); + close (server->mux_pipe[1]); + + hcl_tmr_close (server->tmr); + hcl_close (server->dummy_hcl); + + HCL_MMGR_FREE (server->_mmgr, server); +} + +static HCL_INLINE int prepare_to_acquire_wid (hcl_server_t* server) +{ + hcl_oow_t new_capa; + hcl_oow_t i, j; + hcl_server_wid_map_data_t* tmp; + + HCL_ASSERT (server->dummy_hcl, server->wid_map.free_first == HCL_SERVER_WID_INVALID); + HCL_ASSERT (server->dummy_hcl, server->wid_map.free_last == HCL_SERVER_WID_INVALID); + + new_capa = HCL_ALIGN_POW2(server->wid_map.capa + 1, HCL_SERVER_WID_MAP_ALIGN); + if (new_capa > HCL_SERVER_WID_MAX) + { + if (server->wid_map.capa >= HCL_SERVER_WID_MAX) + { + hcl_server_seterrnum (server, HCL_EFLOOD); + return -1; + } + + new_capa = HCL_SERVER_WID_MAX; + } + + tmp = (hcl_server_wid_map_data_t*)hcl_server_reallocmem(server, server->wid_map.ptr, HCL_SIZEOF(*tmp) * new_capa); + if (!tmp) return -1; + + server->wid_map.free_first = server->wid_map.capa; + for (i = server->wid_map.capa, j = server->wid_map.capa + 1; j < new_capa; i++, j++) + { + tmp[i].used = 0; + tmp[i].u.next = j; + } + tmp[i].used = 0; + tmp[i].u.next = HCL_SERVER_WID_INVALID; + server->wid_map.free_last = i; + + server->wid_map.ptr = tmp; + server->wid_map.capa = new_capa; + + return 0; +} + +static HCL_INLINE void acquire_wid (hcl_server_t* server, hcl_server_worker_t* worker) +{ + hcl_oow_t wid; + + wid = server->wid_map.free_first; + worker->wid = wid; + + server->wid_map.free_first = server->wid_map.ptr[wid].u.next; + if (server->wid_map.free_first == HCL_SERVER_WID_INVALID) server->wid_map.free_last = HCL_SERVER_WID_INVALID; + + server->wid_map.ptr[wid].used = 1; + server->wid_map.ptr[wid].u.worker = worker; +} + +static HCL_INLINE void release_wid (hcl_server_t* server, hcl_server_worker_t* worker) +{ + hcl_oow_t wid; + + wid = worker->wid; + HCL_ASSERT (server->dummy_hcl, wid < server->wid_map.capa && wid != HCL_SERVER_WID_INVALID); + + server->wid_map.ptr[wid].used = 0; + server->wid_map.ptr[wid].u.next = HCL_SERVER_WID_INVALID; + if (server->wid_map.free_last == HCL_SERVER_WID_INVALID) + { + HCL_ASSERT (server->dummy_hcl, server->wid_map.free_first <= HCL_SERVER_WID_INVALID); + server->wid_map.free_first = wid; + } + else + { + server->wid_map.ptr[server->wid_map.free_last].u.next = wid; + } + server->wid_map.free_last = wid; + worker->wid = HCL_SERVER_WID_INVALID; +} + +static hcl_server_worker_t* alloc_worker (hcl_server_t* server, int cli_sck, const hcl_sckaddr_t* peeraddr) +{ + hcl_server_worker_t* worker; + + worker = (hcl_server_worker_t*)hcl_server_allocmem(server, HCL_SIZEOF(*worker)); + if (!worker) return HCL_NULL; + + HCL_MEMSET (worker, 0, HCL_SIZEOF(*worker)); + worker->state = HCL_SERVER_WORKER_STATE_ZOMBIE; + worker->opstate = HCL_SERVER_WORKER_OPSTATE_IDLE; + worker->sck = cli_sck; + worker->peeraddr = *peeraddr; + worker->server = server; + worker->exec_runtime_event_index = HCL_TMR_INVALID_INDEX; + + server->dummy_hcl->vmprim.vm_gettime (server->dummy_hcl, &worker->alloc_time); /* TODO: the callback may return monotonic time. find a way to guarantee it is realtime??? */ + + if (server->wid_map.free_first == HCL_SERVER_WID_INVALID && prepare_to_acquire_wid(server) <= -1) + { + hcl_server_freemem (server, worker); + return HCL_NULL; + } + + acquire_wid (server, worker); + return worker; +} + +static void fini_worker_socket (hcl_server_worker_t* worker) +{ + if (worker->sck >= 0) + { + if (worker->hcl) + { + HCL_LOG2 (worker->hcl, SERVER_LOGMASK_INFO, "Closing worker socket %d [%zu]\n", worker->sck, worker->wid); + } + else + { + /* this should be in the main server thread. i use dummy_hcl for logging */ + HCL_LOG2 (worker->server->dummy_hcl, SERVER_LOGMASK_INFO, "Closing worker socket %d [%zu]\n", worker->sck, worker->wid); + } + close (worker->sck); + worker->sck = -1; + } +} + +static void free_worker (hcl_server_worker_t* worker) +{ + fini_worker_socket (worker); + + if (worker->hcl) + { + HCL_LOG1 (worker->hcl, SERVER_LOGMASK_INFO, "Killing worker [%zu]\n", worker->wid); + } + else + { + /* this should be in the main server thread. i use dummy_hcl for logging */ + HCL_LOG1 (worker->server->dummy_hcl, SERVER_LOGMASK_INFO, "Killing worker [%zu]\n", worker->wid); + } + + release_wid (worker->server, worker); + hcl_server_freemem (worker->server, worker); +} + +static void add_worker_to_server (hcl_server_t* server, hcl_server_worker_state_t wstate, hcl_server_worker_t* worker) +{ + HCL_ASSERT (server->dummy_hcl, worker->server == server); + + if (server->worker_list[wstate].tail) + { + server->worker_list[wstate].tail->next_worker = worker; + worker->prev_worker = server->worker_list[wstate].tail; + server->worker_list[wstate].tail = worker; + worker->next_worker = HCL_NULL; + } + else + { + server->worker_list[wstate].tail = worker; + server->worker_list[wstate].head = worker; + worker->prev_worker = HCL_NULL; + worker->next_worker = HCL_NULL; + } + + server->worker_list[wstate].count++; + worker->state = wstate; +} + +static void zap_worker_in_server (hcl_server_t* server, hcl_server_worker_t* worker) +{ + hcl_server_worker_state_t wstate; + + HCL_ASSERT (server->dummy_hcl, worker->server == server); + + wstate = worker->state; + if (worker->prev_worker) worker->prev_worker->next_worker = worker->next_worker; + else server->worker_list[wstate].head = worker->next_worker; + if (worker->next_worker) worker->next_worker->prev_worker = worker->prev_worker; + else server->worker_list[wstate].tail = worker->prev_worker; + + HCL_ASSERT (server->dummy_hcl, server->worker_list[wstate].count > 0); + server->worker_list[wstate].count--; + worker->state = HCL_SERVER_WORKER_STATE_ZOMBIE; + worker->prev_worker = HCL_NULL; + worker->next_worker = HCL_NULL; +} + +static int worker_step (hcl_server_worker_t* worker) +{ + hcl_xproto_t* proto = worker->proto; + hcl_server_t* server = worker->server; + hcl_t* hcl = worker->hcl; + struct pollfd pfd; + int tmout, actual_tmout; + ssize_t x; + int n; + + //HCL_ASSERT (hcl, proto->rcv.len < proto->rcv.len_needed); + + if (HCL_UNLIKELY(hcl_xproto_geteof(proto))) + { +// TODO: may not be an error if writable needs to be checked... + hcl_seterrbfmt (hcl, HCL_EGENERIC, "connection closed"); + return -1; + } + + tmout = HCL_SECNSEC_TO_MSEC(server->cfg.worker_idle_timeout.sec, server->cfg.worker_idle_timeout.nsec); + actual_tmout = (tmout <= 0)? 10000: tmout; + + pfd.fd = worker->sck; + pfd.events = 0; + if (!hcl_xproto_ready(proto)) pfd.events |= POLLIN; + //if (proto->snd.len > 0) pfd.events |= POLLOUT; + + if (pfd.events) + { + n = poll(&pfd, 1, actual_tmout); + if (n <= -1) + { + if (errno == EINTR) return 0; + hcl_seterrwithsyserr (hcl, 0, errno); + return -1; + } + else if (n == 0) + { + /* timed out - no activity on the pfd */ + if (tmout > 0) + { + /* timeout explicity set. no activity for that duration. considered idle */ + hcl_seterrbfmt (hcl, HCL_EGENERIC, "no activity on the worker socket %d", worker->sck); + return -1; + } + + goto carry_on; + } + + if (pfd.revents & POLLERR) + { + hcl_seterrbfmt (hcl, HCL_EGENERIC, "error condition detected on workder socket %d", worker->sck); + return -1; + } + + if (pfd.revents & POLLOUT) + { + } + + if (pfd.revents & POLLIN) + { + hcl_oow_t bcap; + hcl_uint8_t* bptr; + + bptr = hcl_xproto_getbuf(proto, &bcap);; + x = recv(worker->sck, bptr, bcap, 0); + if (x <= -1) + { + if (errno == EINTR) goto carry_on; /* didn't read read */ + hcl_seterrwithsyserr (hcl, 0, errno); + return -1; + } + + if (x == 0) hcl_xproto_seteof(proto, 1); + hcl_xproto_advbuf (proto, x); + } + } + + /* the receiver buffer has enough data */ + while (hcl_xproto_ready(worker->proto)) + { + if ((n = hcl_xproto_process(worker->proto)) <= -1) + { + /* TODO: proper error message */ + return -1; + } + if (n == 0) + { + /* TODO: chceck if there is remaining data in the buffer...?? */ + return 0; /* tell the caller to break the step loop */ + } + } + +carry_on: + return 1; /* carry on */ +} + +static int init_worker_hcl (hcl_server_worker_t* worker) +{ + hcl_server_t* server = worker->server; + hcl_t* hcl; + worker_hcl_xtn_t* xtn; + hcl_bitmask_t trait; + hcl_cb_t hclcb; + + hcl = hcl_openstdwithmmgr(hcl_server_getmmgr(server), HCL_SIZEOF(*xtn), HCL_NULL); + if (HCL_UNLIKELY(!hcl)) goto oops; + + /* replace the vmprim.log_write function */ + hcl->vmprim.log_write = server_log_write; + + xtn = (worker_hcl_xtn_t*)hcl_getxtn(hcl); + xtn->worker = worker; + + hcl_setoption (hcl, HCL_MOD_INCTX, &server->cfg.module_inctx); + hcl_setoption (hcl, HCL_LOG_MASK, &server->cfg.logmask); + hcl_setcmgr (hcl, hcl_server_getcmgr(server)); + + hcl_getoption (hcl, HCL_TRAIT, &trait); +#if defined(HCL_BUILD_DEBUG) + if (server->cfg.trait & HCL_SERVER_TRAIT_DEBUG_GC) trait |= HCL_TRAIT_DEBUG_GC; + if (server->cfg.trait & HCL_SERVER_TRAIT_DEBUG_BIGINT) trait |= HCL_TRAIT_DEBUG_BIGINT; +#endif + trait |= HCL_TRAIT_LANG_ENABLE_BLOCK; + trait |= HCL_TRAIT_LANG_ENABLE_EOL; + hcl_setoption (hcl, HCL_TRAIT, &trait); + + HCL_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 (hcl, &hclcb); + + if (hcl_ignite(hcl, server->cfg.actor_heap_size) <= -1) goto oops; + if (hcl_addbuiltinprims(hcl) <= -1) goto oops; + + if (hcl_attachccio(hcl, read_handler) <= -1) goto oops; + if (hcl_attachudio(hcl, scan_handler, print_handler) <= -1) goto oops; + + if (hcl_beginfeed(hcl, on_fed_cnode) <= -1) goto oops; + + worker->hcl = hcl; + return 0; + +oops: + if (hcl) hcl_close (hcl); + return -1; +} + + +static void fini_worker_hcl (hcl_server_worker_t* worker) +{ + if (HCL_LIKELY(worker->hcl)) + { + hcl_endfeed (worker->hcl); + hcl_close (worker->hcl); + worker->hcl = HCL_NULL; + } +} + + +static int init_worker_proto (hcl_server_worker_t* worker) +{ + hcl_xproto_t* proto; + proto_xtn_t* xtn; + hcl_xproto_cb_t cb; + + HCL_MEMSET (&cb, 0, HCL_SIZEOF(cb)); + cb.on_packet = handle_packet; + + proto = hcl_xproto_open(hcl_server_getmmgr(worker->server), &cb, HCL_SIZEOF(*xtn)); + if (HCL_UNLIKELY(!proto)) return -1; + + xtn = hcl_xproto_getxtn(proto); + xtn->worker = worker; + + worker->proto = proto; + return 0; +} + +static void fini_worker_proto (hcl_server_worker_t* worker) +{ + if (HCL_LIKELY(worker->proto)) + { + hcl_xproto_close (worker->proto); + worker->proto = HCL_NULL; + } +} + +static void* worker_main (void* ctx) +{ + hcl_server_worker_t* worker = (hcl_server_worker_t*)ctx; + hcl_server_t* server = worker->server; + sigset_t set; + int n; + + sigfillset (&set); + pthread_sigmask (SIG_BLOCK, &set, HCL_NULL); + + worker->thr = pthread_self(); + + n = init_worker_hcl(worker); + if (HCL_UNLIKELY(n <= -1)) + { + /* TODO: capture error ... */ + return HCL_NULL; + } + + n = init_worker_proto(worker); + if (HCL_UNLIKELY(n <= -1)) + { + fini_worker_hcl (worker); + return HCL_NULL; + } + + pthread_mutex_lock (&server->worker_mutex); + add_worker_to_server (server, HCL_SERVER_WORKER_STATE_ALIVE, worker); + pthread_mutex_unlock (&server->worker_mutex); + + /* the worker loop */ + while (!server->stopreq) + { + int n; + worker->opstate = HCL_SERVER_WORKER_OPSTATE_WAIT; + + if ((n = worker_step(worker)) <= 0) + { + worker->opstate = (n <= -1)? HCL_SERVER_WORKER_OPSTATE_ERROR: HCL_SERVER_WORKER_OPSTATE_IDLE; + break; + } + } + + hcl_xproto_close (worker->proto); + worker->proto = HCL_NULL; + + fini_worker_hcl (worker); + + pthread_mutex_lock (&server->worker_mutex); + fini_worker_socket (worker); + if (!worker->claimed) + { + zap_worker_in_server (server, worker); + add_worker_to_server (server, HCL_SERVER_WORKER_STATE_DEAD, worker); + } + pthread_mutex_unlock (&server->worker_mutex); + + return HCL_NULL; +} + +static void purge_all_workers (hcl_server_t* server, hcl_server_worker_state_t wstate) +{ + hcl_server_worker_t* worker; + + while (1) + { + pthread_mutex_lock (&server->worker_mutex); + worker = server->worker_list[wstate].head; + if (worker) + { + zap_worker_in_server (server, worker); + worker->claimed = 1; + if (worker->sck >= 0) shutdown (worker->sck, SHUT_RDWR); + } + pthread_mutex_unlock (&server->worker_mutex); + if (!worker) break; + + pthread_join (worker->thr, HCL_NULL); + free_worker (worker); + } +} + +void hcl_server_logbfmt (hcl_server_t* server, hcl_bitmask_t mask, const hcl_bch_t* fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + hcl_logbfmtv (server->dummy_hcl, mask, fmt, ap); + va_end (ap); +} + +void hcl_server_logufmt (hcl_server_t* server, hcl_bitmask_t mask, const hcl_uch_t* fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + hcl_logufmtv (server->dummy_hcl, mask, fmt, ap); + va_end (ap); +} + +static void set_err_with_syserr (hcl_server_t* server, int syserr_type, int syserr_code, const char* bfmt, ...) +{ + hcl_t* hcl = server->dummy_hcl; + hcl_errnum_t errnum; + hcl_oow_t tmplen, tmplen2; + va_list ap; + + static hcl_bch_t b_dash[] = { ' ', '-', ' ', '\0' }; + static hcl_uch_t u_dash[] = { ' ', '-', ' ', '\0' }; + + if (hcl->shuterr) return; + + if (hcl->vmprim.syserrstrb) + { + errnum = hcl->vmprim.syserrstrb(hcl, syserr_type, syserr_code, hcl->errmsg.tmpbuf.bch, HCL_COUNTOF(hcl->errmsg.tmpbuf.bch)); + + va_start (ap, bfmt); + hcl_seterrbfmtv (hcl, errnum, bfmt, ap); + va_end (ap); + + #if defined(HCL_OOCH_IS_UCH) + hcl->errmsg.len += hcl_copy_ucstr(&hcl->errmsg.buf[hcl->errmsg.len], HCL_COUNTOF(hcl->errmsg.buf) - hcl->errmsg.len, u_dash); + tmplen2 = HCL_COUNTOF(hcl->errmsg.buf) - hcl->errmsg.len; + hcl_convbtoucstr (hcl, hcl->errmsg.tmpbuf.bch, &tmplen, &hcl->errmsg.buf[hcl->errmsg.len], &tmplen2); + hcl->errmsg.len += tmplen2; /* ignore conversion errors */ + #else + hcl->errmsg.len += hcl_copy_bcstr(&hcl->errmsg.buf[hcl->errmsg.len], HCL_COUNTOF(hcl->errmsg.buf) - hcl->errmsg.len, b_dash); + hcl->errmsg.len += hcl_copy_bcstr(&hcl->errmsg.buf[hcl->errmsg.len], HCL_COUNTOF(hcl->errmsg.buf) - hcl->errmsg.len, hcl->errmsg.tmpbuf.bch); + + #endif + } + else + { + HCL_ASSERT (hcl, hcl->vmprim.syserrstru != HCL_NULL); + + errnum = hcl->vmprim.syserrstru(hcl, syserr_type, syserr_code, hcl->errmsg.tmpbuf.uch, HCL_COUNTOF(hcl->errmsg.tmpbuf.uch)); + + va_start (ap, bfmt); + hcl_seterrbfmtv (hcl, errnum, bfmt, ap); + va_end (ap); + + #if defined(HCL_OOCH_IS_UCH) + hcl->errmsg.len += hcl_copy_ucstr(&hcl->errmsg.buf[hcl->errmsg.len], HCL_COUNTOF(hcl->errmsg.buf) - hcl->errmsg.len, u_dash); + hcl->errmsg.len += hcl_copy_ucstr(&hcl->errmsg.buf[hcl->errmsg.len], HCL_COUNTOF(hcl->errmsg.buf) - hcl->errmsg.len, hcl->errmsg.tmpbuf.uch); + #else + hcl->errmsg.len += hcl_copy_bcstr(&hcl->errmsg.buf[hcl->errmsg.len], HCL_COUNTOF(hcl->errmsg.buf) - hcl->errmsg.len, b_dash); + tmplen2 = HCL_COUNTOF(hcl->errmsg.buf) - hcl->errmsg.len; + hcl_convutobcstr (hcl, hcl->errmsg.tmpbuf.uch, &tmplen, &hcl->errmsg.buf[hcl->errmsg.len], &tmplen2); + hcl->errmsg.len += tmplen2; /* ignore conversion errors */ + #endif + } + + server->errnum = errnum; + hcl_copy_oochars (server->errmsg.buf, server->dummy_hcl->errmsg.buf, HCL_COUNTOF(server->errmsg.buf)); + server->errmsg.len = server->dummy_hcl->errmsg.len; +} + +static void free_all_listeners (hcl_server_t* server) +{ + hcl_server_listener_t* lp; + struct epoll_event dummy_ev; + + epoll_ctl (server->listener.ep_fd, EPOLL_CTL_DEL, server->mux_pipe[0], &dummy_ev); + + while (server->listener.head) + { + lp = server->listener.head; + server->listener.head = lp->next_listener; + server->listener.count--; + + epoll_ctl (server->listener.ep_fd, EPOLL_CTL_DEL, lp->sck, &dummy_ev); + close (lp->sck); + hcl_server_freemem (server, lp); + } + + HCL_ASSERT (server->dummy_hcl, server->listener.ep_fd >= 0); + close (server->listener.ep_fd); + server->listener.ep_fd = -1; +} + +static int setup_listeners (hcl_server_t* server, const hcl_bch_t* addrs) +{ + const hcl_bch_t* addr_ptr, * comma; + int ep_fd, fcv; + struct epoll_event ev; + + ep_fd = epoll_create(1024); + if (ep_fd <= -1) + { + set_err_with_syserr (server, 0, errno, "unable to create multiplexer"); + HCL_LOG1 (server->dummy_hcl, SERVER_LOGMASK_ERROR, "%js\n", hcl_server_geterrmsg(server)); + return -1; + } + +#if defined(O_CLOEXEC) + fcv = fcntl(ep_fd, F_GETFD, 0); + if (fcv >= 0) fcntl(ep_fd, F_SETFD, fcv | O_CLOEXEC); +#endif + + HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev)); + ev.events = EPOLLIN | EPOLLHUP | EPOLLERR; + ev.data.fd = server->mux_pipe[0]; + if (epoll_ctl(ep_fd, EPOLL_CTL_ADD, server->mux_pipe[0], &ev) <= -1) + { + set_err_with_syserr (server, 0, errno, "unable to register pipe %d to multiplexer", server->mux_pipe[0]); + HCL_LOG1 (server->dummy_hcl, SERVER_LOGMASK_ERROR, "%js\n", hcl_server_geterrmsg(server)); + close (ep_fd); + return -1; + } + + server->listener.ep_fd = ep_fd; + addr_ptr = addrs; + while (1) + { + hcl_sckaddr_t srv_addr; + int srv_fd, sck_fam, optval; + hcl_scklen_t srv_len; + hcl_oow_t addr_len; + hcl_server_listener_t* listener; + + comma = hcl_find_bchar_in_bcstr(addr_ptr, ','); + addr_len = comma? comma - addr_ptr: hcl_count_bcstr(addr_ptr); + /* [NOTE] no whitespaces are allowed before and after a comma */ + + sck_fam = hcl_bchars_to_sckaddr(addr_ptr, addr_len, &srv_addr, &srv_len); + if (sck_fam <= -1) + { + hcl_server_seterrbfmt (server, HCL_EINVAL, "unable to convert address - %.*hs", addr_len, addr_ptr); + HCL_LOG1 (server->dummy_hcl, SERVER_LOGMASK_ERROR, "%js\n", hcl_server_geterrmsg(server)); + goto next_segment; + } + + srv_fd = socket(sck_fam, SOCK_STREAM, 0); + if (srv_fd <= -1) + { + set_err_with_syserr (server, 0, errno, "unable to open server socket for %.*hs", addr_len, addr_ptr); + HCL_LOG1 (server->dummy_hcl, SERVER_LOGMASK_ERROR, "%js\n", hcl_server_geterrmsg(server)); + goto next_segment; + } + + optval = 1; + setsockopt (srv_fd, SOL_SOCKET, SO_REUSEADDR, &optval, HCL_SIZEOF(int)); + + #if defined(O_NONBLOCK) || defined(O_CLOEXEC) + fcv = fcntl(srv_fd, F_GETFD, 0); + if (fcv >= 0) + { + #if defined(O_NONBLOCK) + fcv |= O_NONBLOCK; + #endif + #if defined(O_CLOEXEC) + fcv |= O_CLOEXEC; + #endif + + fcntl(srv_fd, F_SETFL, fcv); + } + #endif + + if (bind(srv_fd, (struct sockaddr*)&srv_addr, srv_len) == -1) + { + set_err_with_syserr (server, 0, errno, "unable to bind server socket %d for %.*hs", srv_fd, addr_len, addr_ptr); + HCL_LOG1 (server->dummy_hcl, SERVER_LOGMASK_ERROR, "%js\n", hcl_server_geterrmsg(server)); + close (srv_fd); + goto next_segment; + } + + if (listen(srv_fd, 128) <= -1) + { + set_err_with_syserr (server, 0, errno, "unable to listen on server socket %d for %.*hs", srv_fd, addr_len, addr_ptr); + HCL_LOG1 (server->dummy_hcl, SERVER_LOGMASK_ERROR, "%js\n", hcl_server_geterrmsg(server)); + close (srv_fd); + goto next_segment; + } + + HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev)); + ev.events = EPOLLIN | EPOLLHUP | EPOLLERR; + ev.data.fd = srv_fd; + if (epoll_ctl(ep_fd, EPOLL_CTL_ADD, srv_fd, &ev) <= -1) + { + set_err_with_syserr (server, 0, errno, "unable to register server socket %d to multiplexer for %.*hs", srv_fd, addr_len, addr_ptr); + HCL_LOG1 (server->dummy_hcl, SERVER_LOGMASK_ERROR, "%js\n", hcl_server_geterrmsg(server)); + close (srv_fd); + goto next_segment; + } + + listener = (hcl_server_listener_t*)hcl_server_allocmem(server, HCL_SIZEOF(*listener)); + if (!listener) + { + close(srv_fd); + goto next_segment; + } + + HCL_MEMSET (listener, 0, HCL_SIZEOF(*listener)); + listener->sck = srv_fd; + listener->sckaddr = srv_addr; + listener->next_listener = server->listener.head; + server->listener.head = listener; + server->listener.count++; + + next_segment: + if (!comma) break; + addr_ptr = comma + 1; + } + + + if (!server->listener.head) + { + /* no valid server has been configured */ + hcl_server_seterrbfmt (server, HCL_EINVAL, "unable to set up listeners with %hs", addrs); + free_all_listeners (server); + return -1; + } + + return 0; +} + +int hcl_server_start (hcl_server_t* server, const hcl_bch_t* addrs) +{ + int xret = 0, fcv; + pthread_attr_t thr_attr; + + if (setup_listeners(server, addrs) <= -1) return -1; + + pthread_attr_init (&thr_attr); + pthread_attr_setstacksize (&thr_attr, server->cfg.worker_stack_size); + + server->stopreq = 0; + while (!server->stopreq) + { + hcl_sckaddr_t cli_addr; + hcl_scklen_t cli_len; + int cli_fd; + pthread_t thr; + hcl_ntime_t tmout; + hcl_server_worker_t* worker; + int n; + + pthread_mutex_lock (&server->tmr_mutex); + n = hcl_tmr_gettmout(server->tmr, HCL_NULL, &tmout); + pthread_mutex_unlock (&server->tmr_mutex); + if (n <= -1) HCL_INIT_NTIME (&tmout, 10, 0); + + n = epoll_wait(server->listener.ep_fd, server->listener.ev_buf, HCL_COUNTOF(server->listener.ev_buf), HCL_SECNSEC_TO_MSEC(tmout.sec, tmout.nsec)); + + purge_all_workers (server, HCL_SERVER_WORKER_STATE_DEAD); + if (n <= -1) + { + if (server->stopreq) break; /* normal termination requested */ + if (errno == EINTR) continue; /* interrupted but not termination requested */ + + set_err_with_syserr (server, 0, errno, "unable to poll for events in server"); + xret = -1; + break; + } + + pthread_mutex_lock (&server->tmr_mutex); + hcl_tmr_fire (server->tmr, HCL_NULL, HCL_NULL); + pthread_mutex_unlock (&server->tmr_mutex); + + while (n > 0) + { + struct epoll_event* evp; + + --n; + + evp = &server->listener.ev_buf[n]; + if (!evp->events /*& (POLLIN | POLLHUP | POLLERR) */) continue; + + if (evp->data.fd == server->mux_pipe[0]) + { + char tmp[128]; + while (read(server->mux_pipe[0], tmp, HCL_SIZEOF(tmp)) > 0) /* nothing */; + } + else + { + /* the reset should be the listener's socket */ + + cli_len = HCL_SIZEOF(cli_addr); + cli_fd = accept(evp->data.fd, (struct sockaddr*)&cli_addr, &cli_len); + if (cli_fd == -1) + { + if (server->stopreq) break; /* normal termination requested */ + if (errno == EINTR) continue; /* interrupted but no termination requested */ + #if defined(EWOULDBLOCK) && defined(EAGAIN) && (EWOULDBLOCK != EAGAIN) + if (errno == EWOULDBLOCK || errno == EAGAIN) continue; + #elif defined(EWOULDBLOCK) + if (errno == EWOULDBLOCK) continue; + #elif defined(EAGAIN) + if (errno == EAGAIN) continue; + #endif + + set_err_with_syserr (server, 0, errno, "unable to accept worker on server socket %d", evp->data.fd); + xret = -1; + break; + } + + #if defined(O_NONBLOCK) || defined(O_CLOEXEC) + fcv = fcntl(cli_fd, F_GETFD, 0); + if (fcv >= 0) + { + #if defined(O_NONBLOCK) + fcv &= ~O_NONBLOCK; // force the accepted socket to be blocking + #endif + #if defined(O_CLOEXEC) + fcv |= O_CLOEXEC; + #endif + fcntl(cli_fd, F_SETFD, fcv); + } + #endif + + if (server->cfg.worker_max_count > 0) + { + int flood; + pthread_mutex_lock (&server->worker_mutex); + flood = (server->worker_list[HCL_SERVER_WORKER_STATE_ALIVE].count >= server->cfg.worker_max_count); + pthread_mutex_unlock (&server->worker_mutex); + if (flood) + { + HCL_LOG1 (server->dummy_hcl, SERVER_LOGMASK_ERROR, "Not accepting connection for too many workers - socket %d\n", cli_fd); + goto drop_connection; + } + } + + worker = alloc_worker(server, cli_fd, &cli_addr); + if (!worker) + { + HCL_LOG1 (server->dummy_hcl, SERVER_LOGMASK_ERROR, "Unable to accomodate worker - socket %d\n", cli_fd); + drop_connection: + close (cli_fd); + } + else + { + HCL_LOG2 (server->dummy_hcl, SERVER_LOGMASK_INFO, "Accomodated worker [%zu] - socket %d\n", worker->wid, cli_fd); + if (pthread_create(&thr, &thr_attr, worker_main, worker) != 0) + { + free_worker (worker); + } + } + } + } + } + + purge_all_workers (server, HCL_SERVER_WORKER_STATE_ALIVE); + purge_all_workers (server, HCL_SERVER_WORKER_STATE_DEAD); + + pthread_attr_destroy (&thr_attr); + + free_all_listeners (server); + return xret; +} + +void hcl_server_stop (hcl_server_t* server) +{ + server->stopreq = 1; + write (server->mux_pipe[1], "Q", 1); /* don't care about failure */ +} + +int hcl_server_setoption (hcl_server_t* server, hcl_server_option_t id, const void* value) +{ + switch (id) + { + case HCL_SERVER_TRAIT: + server->cfg.trait = *(const hcl_bitmask_t*)value; + if (server->dummy_hcl) + { + /* setting this affects the dummy hcl immediately. + * existing hcl instances inside worker threads won't get + * affected. new hcl instances to be created later + * is supposed to use the new value */ + hcl_bitmask_t trait; + + hcl_getoption (server->dummy_hcl, HCL_TRAIT, &trait); + #if defined(HCL_BUILD_DEBUG) + if (server->cfg.trait & HCL_SERVER_TRAIT_DEBUG_GC) trait |= HCL_TRAIT_DEBUG_GC; + if (server->cfg.trait & HCL_SERVER_TRAIT_DEBUG_BIGINT) trait |= HCL_TRAIT_DEBUG_BIGINT; + #endif + hcl_setoption (server->dummy_hcl, HCL_TRAIT, &trait); + } + return 0; + + case HCL_SERVER_LOG_MASK: + server->cfg.logmask = *(const hcl_bitmask_t*)value; + if (server->dummy_hcl) + { + /* setting this affects the dummy hcl immediately. + * existing hcl instances inside worker threads won't get + * affected. new hcl instances to be created later + * is supposed to use the new value */ + hcl_setoption (server->dummy_hcl, HCL_LOG_MASK, value); + } + return 0; + + case HCL_SERVER_WORKER_MAX_COUNT: + server->cfg.worker_max_count = *(hcl_oow_t*)value; + return 0; + + case HCL_SERVER_WORKER_STACK_SIZE: + server->cfg.worker_stack_size = *(hcl_oow_t*)value; + return 0; + + case HCL_SERVER_WORKER_IDLE_TIMEOUT: + server->cfg.worker_idle_timeout = *(hcl_ntime_t*)value; + return 0; + + case HCL_SERVER_ACTOR_HEAP_SIZE: + server->cfg.actor_heap_size = *(hcl_oow_t*)value; + return 0; + + case HCL_SERVER_ACTOR_MAX_RUNTIME: + server->cfg.actor_max_runtime = *(hcl_ntime_t*)value; + return 0; + + case HCL_SERVER_SCRIPT_INCLUDE_PATH: + hcl_copy_oocstr (server->cfg.script_include_path, HCL_COUNTOF(server->cfg.script_include_path), (const hcl_ooch_t*)value); + return 0; + + case HCL_SERVER_MODULE_INCTX: + server->cfg.module_inctx = *(void**)value; + return 0; + } + + hcl_server_seterrnum (server, HCL_EINVAL); + return -1; +} + +int hcl_server_getoption (hcl_server_t* server, hcl_server_option_t id, void* value) +{ + switch (id) + { + case HCL_SERVER_TRAIT: + *(hcl_bitmask_t*)value = server->cfg.trait; + return 0; + + case HCL_SERVER_LOG_MASK: + *(hcl_bitmask_t*)value = server->cfg.logmask; + return 0; + + case HCL_SERVER_WORKER_MAX_COUNT: + *(hcl_oow_t*)value = server->cfg.worker_max_count; + return 0; + + case HCL_SERVER_WORKER_STACK_SIZE: + *(hcl_oow_t*)value = server->cfg.worker_stack_size; + return 0; + + case HCL_SERVER_WORKER_IDLE_TIMEOUT: + *(hcl_ntime_t*)value = server->cfg.worker_idle_timeout; + return 0; + + case HCL_SERVER_ACTOR_HEAP_SIZE: + *(hcl_oow_t*)value = server->cfg.actor_heap_size; + return 0; + + case HCL_SERVER_ACTOR_MAX_RUNTIME: + *(hcl_ntime_t*)value = server->cfg.actor_max_runtime; + return 0; + + case HCL_SERVER_SCRIPT_INCLUDE_PATH: + *(hcl_ooch_t**)value = server->cfg.script_include_path; + return 0; + + case HCL_SERVER_MODULE_INCTX: + *(void**)value = server->cfg.module_inctx; + return 0; + }; + + hcl_server_seterrnum (server, HCL_EINVAL); + return -1; +} + +void* hcl_server_getxtn (hcl_server_t* server) +{ + return (void*)((hcl_uint8_t*)server + server->_instsize); +} + +hcl_mmgr_t* hcl_server_getmmgr (hcl_server_t* server) +{ + return server->_mmgr; +} + +hcl_cmgr_t* hcl_server_getcmgr (hcl_server_t* server) +{ + return server->_cmgr; +} + +void hcl_server_setcmgr (hcl_server_t* server, hcl_cmgr_t* cmgr) +{ + server->_cmgr = cmgr; +} + +hcl_errnum_t hcl_server_geterrnum (hcl_server_t* server) +{ + return server->errnum; +} + +const hcl_ooch_t* hcl_server_geterrstr (hcl_server_t* server) +{ + return hcl_errnum_to_errstr(server->errnum); +} + +const hcl_ooch_t* hcl_server_geterrmsg (hcl_server_t* server) +{ + if (server->errmsg.len <= 0) return hcl_errnum_to_errstr(server->errnum); + return server->errmsg.buf; +} + +void hcl_server_seterrnum (hcl_server_t* server, hcl_errnum_t errnum) +{ + /*if (server->shuterr) return; */ + server->errnum = errnum; + server->errmsg.len = 0; +} + +void hcl_server_seterrbfmt (hcl_server_t* server, hcl_errnum_t errnum, const hcl_bch_t* fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + hcl_seterrbfmtv (server->dummy_hcl, errnum, fmt, ap); + va_end (ap); + + HCL_ASSERT (server->dummy_hcl, HCL_COUNTOF(server->errmsg.buf) == HCL_COUNTOF(server->dummy_hcl->errmsg.buf)); + server->errnum = errnum; + hcl_copy_oochars (server->errmsg.buf, server->dummy_hcl->errmsg.buf, HCL_COUNTOF(server->errmsg.buf)); + server->errmsg.len = server->dummy_hcl->errmsg.len; +} + +void hcl_server_seterrufmt (hcl_server_t* server, hcl_errnum_t errnum, const hcl_uch_t* fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + hcl_seterrufmtv (server->dummy_hcl, errnum, fmt, ap); + va_end (ap); + + HCL_ASSERT (server->dummy_hcl, HCL_COUNTOF(server->errmsg.buf) == HCL_COUNTOF(server->dummy_hcl->errmsg.buf)); + server->errnum = errnum; + server->errnum = errnum; + hcl_copy_oochars (server->errmsg.buf, server->dummy_hcl->errmsg.buf, HCL_COUNTOF(server->errmsg.buf)); + server->errmsg.len = server->dummy_hcl->errmsg.len; +} + +void* hcl_server_allocmem (hcl_server_t* server, hcl_oow_t size) +{ + void* ptr; + + ptr = HCL_MMGR_ALLOC(server->_mmgr, size); + if (!ptr) hcl_server_seterrnum (server, HCL_ESYSMEM); + return ptr; +} + +void* hcl_server_callocmem (hcl_server_t* server, hcl_oow_t size) +{ + void* ptr; + + ptr = HCL_MMGR_ALLOC(server->_mmgr, size); + if (!ptr) hcl_server_seterrnum (server, HCL_ESYSMEM); + else HCL_MEMSET (ptr, 0, size); + return ptr; +} + +void* hcl_server_reallocmem (hcl_server_t* server, void* ptr, hcl_oow_t size) +{ + ptr = HCL_MMGR_REALLOC(server->_mmgr, ptr, size); + if (!ptr) hcl_server_seterrnum (server, HCL_ESYSMEM); + return ptr; +} + +void hcl_server_freemem (hcl_server_t* server, void* ptr) +{ + HCL_MMGR_FREE (server->_mmgr, ptr); +}