diff --git a/bin/main-c.c b/bin/main-c.c index 8b841ba..86cfdd8 100644 --- a/bin/main-c.c +++ b/bin/main-c.c @@ -452,42 +452,6 @@ static int handle_logopt (hcl_client_t* client, const hcl_bch_t* str) /* ========================================================================= */ -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); - /*nwritten = writev(proto->worker->sck, (const struct iovec*)&iov[index], count - index);*/ - 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; -} - -/* ========================================================================= */ - struct proto_xtn_t { int x; @@ -627,7 +591,7 @@ static int handle_request (hcl_client_t* client, const char* ipaddr, const char* iov[1].iov_base = scptr; iov[1].iov_len = seglen; - send_iov (sck, iov, 2); /* TODO: error check */ + hcl_sys_send_iov (sck, iov, 2); /* TODO: error check */ scptr = sccur; @@ -639,7 +603,7 @@ static int handle_request (hcl_client_t* client, const char* ipaddr, const char* iov[0].iov_base = &hdr; iov[0].iov_len = HCL_SIZEOF(hdr); - send_iov (sck, iov, 1); + hcl_sys_send_iov (sck, iov, 1); if (shut_wr_after_req) { @@ -653,7 +617,7 @@ static int handle_request (hcl_client_t* client, const char* ipaddr, const char* iov[0].iov_base = &hdr; iov[0].iov_len = HCL_SIZEOF(hdr); - send_iov (sck, iov, 1); + hcl_sys_send_iov (sck, iov, 1); } } } diff --git a/lib/hcl-c.c b/lib/hcl-c.c deleted file mode 100644 index b9ca253..0000000 --- a/lib/hcl-c.c +++ /dev/null @@ -1,1049 +0,0 @@ -/* - 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. - */ -#if 0 - -#include "hcl-c.h" -#include "hcl-prv.h" - -#include -#include - -#define HCL_CLIENT_TOKEN_NAME_ALIGN 64 - -struct client_hcl_xtn_t -{ - hcl_client_t* client; -}; -typedef struct client_hcl_xtn_t client_hcl_xtn_t; - -enum hcl_client_reply_attr_type_t -{ - HCL_CLIENT_REPLY_ATTR_TYPE_UNKNOWN, - HCL_CLIENT_REPLY_ATTR_TYPE_DATA -}; -typedef enum hcl_client_reply_attr_type_t hcl_client_reply_attr_type_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; - - hcl_client_state_t state; - struct - { - struct - { - hcl_ooch_t* ptr; - hcl_oow_t len; - hcl_oow_t capa; - } tok; - - hcl_client_reply_type_t type; - hcl_client_reply_attr_type_t last_attr_type; - struct - { - hcl_ooch_t* ptr; - hcl_oow_t len; - hcl_oow_t capa; - } last_attr_key; /* the last attr key shown */ - - union - { - struct - { - hcl_oow_t nsplen; /* length remembered when the white space was shown */ - } reply_value_unquoted; - - struct - { - int escaped; - } reply_value_quoted; - - struct - { - hcl_oow_t nsplen; /* length remembered when the white space was shown */ - } attr_value_unquoted; - - struct - { - int escaped; - } attr_value_quoted; - - struct - { - hcl_oow_t max; - hcl_oow_t tally; - } length_bounded_data; - - struct - { - int in_data_part; - int negated; - hcl_oow_t max; /* chunk length */ - hcl_oow_t tally; - hcl_oow_t total; - hcl_oow_t clcount; - } chunked_data; - } u; - } rep; -}; - - -/* ========================================================================= */ - -static void 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); -} - -/* ========================================================================= */ - -static HCL_INLINE int is_spacechar (hcl_bch_t c) -{ - /* TODO: handle other space unicode characters */ - switch (c) - { - /* [NOTE]: '\n' is special and used as delimiter. so it's not treated - * as a space character */ - case ' ': - case '\f': /* formfeed */ - case '\r': /* carriage return */ - case '\t': /* horizon tab */ - case '\v': /* vertical tab */ - return 1; - - default: - return 0; - } -} - -static HCL_INLINE int is_alphachar (hcl_ooci_t c) -{ -/* TODO: support full unicode */ - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); -} - -static HCL_INLINE int is_digitchar (hcl_ooci_t c) -{ -/* TODO: support full unicode */ - return (c >= '0' && c <= '9'); -} - -static void clear_reply_token (hcl_client_t* client) -{ - client->rep.tok.len = 0; -} - -static int add_to_reply_token (hcl_client_t* client, hcl_ooch_t ch) -{ - if (client->rep.tok.len >= client->rep.tok.capa) - { - hcl_ooch_t* tmp; - hcl_oow_t newcapa; - - newcapa = HCL_ALIGN_POW2(client->rep.tok.len + 1, HCL_CLIENT_TOKEN_NAME_ALIGN); - tmp = (hcl_ooch_t*)hcl_client_reallocmem(client, client->rep.tok.ptr, newcapa * HCL_SIZEOF(*tmp)); - if (!tmp) return -1; - - client->rep.tok.capa = newcapa; - client->rep.tok.ptr = tmp; - } - - client->rep.tok.ptr[client->rep.tok.len++] = ch; - return 0; -} - -static HCL_INLINE int is_token (hcl_client_t* client, const hcl_bch_t* str) -{ - return hcl_comp_oochars_bcstr(client->rep.tok.ptr, client->rep.tok.len, str) == 0; -} - -static HCL_INLINE int is_token_integer (hcl_client_t* client, hcl_oow_t* value) -{ - hcl_oow_t i; - hcl_oow_t v = 0; - - if (client->rep.tok.len <= 0) return 0; - - for (i = 0; i < client->rep.tok.len; i++) - { - if (!is_digitchar(client->rep.tok.ptr[i])) return 0; - v = v * 10 + (client->rep.tok.ptr[i] - '0'); - } - - *value = v; - return 1; -} - -static HCL_INLINE hcl_ooch_t unescape (hcl_ooch_t c) -{ -#if 0 - /* as of this writing, the server side only escapes \ and ". - * i don't know if n, r, f, t, v should be supported here */ - switch (c) - { - case 'a': return '\a'; - case 'b': return '\b'; - case 'f': return '\f'; - case 'n': return '\n'; - case 'r': return '\r'; - case 't': return '\t'; - case 'v': return '\v'; - default: return c; - } -#else - return c; -#endif -} - -static int handle_char (hcl_client_t* client, hcl_ooci_t c, hcl_oow_t nbytes) -{ - switch (client->state) - { - case HCL_CLIENT_STATE_START: - if (c == HCL_OOCI_EOF) - { - hcl_client_seterrbfmt (client, HCL_EFINIS, "unexpected end before reply name"); - goto oops; - } - else if (c == '.') - { - client->state = HCL_CLIENT_STATE_IN_REPLY_NAME; - clear_reply_token (client); - if (add_to_reply_token(client, c) <= -1) goto oops; - break; - } - else if (is_spacechar(c)) - { - /* skip whitespaces at the beginning of the start line before the reply name */ - break; - } - else - { - hcl_client_seterrbfmt (client, HCL_EINVAL, "reply line not starting with a period - %jc", (hcl_ooch_t)c); - goto oops; - } - - case HCL_CLIENT_STATE_IN_REPLY_NAME: - if (is_alphachar(c) || (client->rep.tok.len > 2 && c == '-')) - { - if (add_to_reply_token(client, c) <= -1) goto oops; - break; - } - else - { - if (is_token(client, ".OK")) - { - client->rep.type = HCL_CLIENT_REPLY_TYPE_OK; - } - else if (is_token(client, ".ERROR")) - { - client->rep.type = HCL_CLIENT_REPLY_TYPE_ERROR; - } - else - { - hcl_client_seterrbfmt (client, HCL_EINVAL, "unknown reply name %.*js", client->rep.tok.len, client->rep.tok.ptr); - goto oops; - } - - client->state = HCL_CLIENT_STATE_IN_REPLY_VALUE_START; - clear_reply_token (client); - /* [IMPORTANT] fall thru */ - } - - case HCL_CLIENT_STATE_IN_REPLY_VALUE_START: - if (c == HCL_OOCI_EOF) - { - hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of reply line without newline"); - goto oops; - } - else if (is_spacechar(c)) - { - /* do nothing. skip it */ - break; - } - else if (c == '\n') - { - /* no value is specified. even no whitespaces. - * switch to a long-format response */ - if (client->prim.start_reply(client, client->rep.type, HCL_NULL, 0) <= -1) goto oops; - - client->state = HCL_CLIENT_STATE_IN_ATTR_KEY; - HCL_ASSERT (client->dummy_hcl, client->rep.tok.len == 0); - break; - } - else if (c == '\"') - { - client->state = HCL_CLIENT_STATE_IN_REPLY_VALUE_QUOTED; - HCL_ASSERT (client->dummy_hcl, client->rep.tok.len == 0); - client->rep.u.reply_value_quoted.escaped = 0; - break; - } - else - { - /* the first value character has been encountered */ - client->state = HCL_CLIENT_STATE_IN_REPLY_VALUE_UNQUOTED; - HCL_ASSERT (client->dummy_hcl, client->rep.tok.len == 0); - client->rep.u.reply_value_unquoted.nsplen = 0; - /* [IMPORTANT] fall thru */ - } - - case HCL_CLIENT_STATE_IN_REPLY_VALUE_UNQUOTED: - if (c == HCL_OOCI_EOF) - { - hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of reply line without newline"); - goto oops; - } - else if (c == '\n') - { - client->rep.tok.len = client->rep.u.reply_value_unquoted.nsplen; - goto reply_value_end; - } - else - { - if (add_to_reply_token(client, c) <= -1) goto oops; - if (!is_spacechar(c)) client->rep.u.reply_value_unquoted.nsplen = client->rep.tok.len; - break; - } - - case HCL_CLIENT_STATE_IN_REPLY_VALUE_QUOTED: - if (c == HCL_OOCI_EOF) - { - hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of reply line without closing quote"); - goto oops; - } - else - { - if (client->rep.u.reply_value_quoted.escaped) - { - c = unescape(c); - } - else if (c == '\\') - { - client->rep.u.reply_value_quoted.escaped = 1; - break; - } - else if (c == '\"') - { - client->state = HCL_CLIENT_STATE_IN_REPLY_VALUE_QUOTED_TRAILER; - break; - } - - client->rep.u.reply_value_quoted.escaped = 0; - if (add_to_reply_token(client, c) <= -1) goto oops; - break; - } - - case HCL_CLIENT_STATE_IN_REPLY_VALUE_QUOTED_TRAILER: - if (c == HCL_OOCI_EOF) - { - hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of reply line without newline"); - goto oops; - } - else if (c == '\n') - { - reply_value_end: - /* short-form format. the data pointer is passed to the start_reply - * callback. no end_reply callback is invoked. the data is assumed - * to be in UTF-8 encoding. this is different from the data in the - * long-format reply which is treated as octet stream */ - if (client->prim.start_reply(client, client->rep.type, client->rep.tok.ptr, client->rep.tok.len) <= -1) goto oops; - /* no end_reply() callback for the short-form reply */ - - client->state = HCL_CLIENT_STATE_START; - clear_reply_token (client); - break; - } - else if (is_spacechar(c)) - { - /* skip white spaces after the closing quote */ - break; - } - else - { - hcl_client_seterrbfmt (client, HCL_EINVAL, "garbage after quoted reply value"); - goto oops; - } - - case HCL_CLIENT_STATE_IN_ATTR_KEY: - if (c == HCL_OOCI_EOF) - { - hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of attribute line"); - goto oops; - } - else if (client->rep.tok.len == 0) - { - - if (is_spacechar(c)) - { - /* skip whitespaces at the beginning of the start line before the reply name */ - break; - } - else if (c != '.') - { - hcl_client_seterrbfmt (client, HCL_EINVAL, "attribute name not starting with a period - [%jc]", (hcl_ooch_t)c); - goto oops; - } - - if (add_to_reply_token(client, c) <= -1) goto oops; - break; - } - else if (is_alphachar(c) || (client->rep.tok.len > 2 && c == '-')) - { - if (add_to_reply_token(client, c) <= -1) goto oops; - break; - } - else - { - if (client->rep.tok.len <= 1) - { - hcl_client_seterrbfmt (client, HCL_EINVAL, "attribute name too short"); - goto oops; - } - - if (is_token(client, ".DATA")) - { - client->rep.last_attr_type = HCL_CLIENT_REPLY_ATTR_TYPE_DATA; - } - /* PUT more known attribute handling here */ - else - { - client->rep.last_attr_type = HCL_CLIENT_REPLY_ATTR_TYPE_UNKNOWN; - } - - /* remember the attribute name */ - if (client->rep.tok.len > client->rep.last_attr_key.capa) - { - hcl_ooch_t* tmp; - - tmp = (hcl_ooch_t*)hcl_client_reallocmem(client, client->rep.last_attr_key.ptr, client->rep.tok.capa * HCL_SIZEOF(*tmp)); - if (!tmp) goto oops; - - client->rep.last_attr_key.ptr = tmp; - client->rep.last_attr_key.capa = client->rep.tok.capa; - } - - hcl_copy_oochars (client->rep.last_attr_key.ptr, client->rep.tok.ptr, client->rep.tok.len); - client->rep.last_attr_key.len = client->rep.tok.len; - - client->state = HCL_CLIENT_STATE_IN_ATTR_VALUE_START; - clear_reply_token (client); - /* [IMPORTANT] fall thru */ - } - - case HCL_CLIENT_STATE_IN_ATTR_VALUE_START: - if (c == HCL_OOCI_EOF) - { - hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end without attribute value"); - goto oops; - } - else if (is_spacechar(c)) - { - /* do nothing. skip it */ - break; - } - else if (c == '\n') - { - hcl_client_seterrbfmt (client, HCL_EINVAL, "no attribute value for %.*js\n", client->rep.last_attr_key.len, client->rep.last_attr_key.ptr); - goto oops; - } - else if (c == '\"') - { - client->state = HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED; - HCL_ASSERT (client->dummy_hcl, client->rep.tok.len == 0); - client->rep.u.attr_value_quoted.escaped = 0; - break; - } - else - { - /* the first value character has been encountered */ - client->state = HCL_CLIENT_STATE_IN_ATTR_VALUE_UNQUOTED; - HCL_ASSERT (client->dummy_hcl, client->rep.tok.len == 0); - client->rep.u.attr_value_unquoted.nsplen = 0; - /* [IMPORTANT] fall thru */ - } - - case HCL_CLIENT_STATE_IN_ATTR_VALUE_UNQUOTED: - if (c == HCL_OOCI_EOF) - { - hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of attribute line without newline"); - goto oops; - } - else if (c == '\n') - { - client->rep.tok.len = client->rep.u.attr_value_unquoted.nsplen; - goto attr_value_end; - } - else - { - if (add_to_reply_token(client, c) <= -1) goto oops; - if (!is_spacechar(c)) client->rep.u.attr_value_unquoted.nsplen = client->rep.tok.len; - break; - } - - case HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED: - if (c == HCL_OOCI_EOF) - { - hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of attribute value without closing quote"); - goto oops; - } - else - { - if (client->rep.u.attr_value_quoted.escaped) - { - c = unescape(c); - /* TODO: more escaping handling like \0NNN \xXXXX \uXXXX \UXXXX */ - } - else if (c == '\\') - { - client->rep.u.attr_value_quoted.escaped = 1; - break; - } - else if (c == '\"') - { - client->state = HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED_TRAILER; - break; - } - - client->rep.u.attr_value_quoted.escaped = 0; - if (add_to_reply_token(client, c) <= -1) goto oops; - break; - } - - case HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED_TRAILER: - if (c == HCL_OOCI_EOF) - { - hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of attribute line without newline"); - goto oops; - } - else if (c == '\n') - { - attr_value_end: - if (client->prim.feed_attr) - { - hcl_oocs_t key, val; - key.ptr = client->rep.last_attr_key.ptr; - key.len = client->rep.last_attr_key.len; - val.ptr = client->rep.tok.ptr; - val.len = client->rep.tok.len; - if (client->prim.feed_attr (client, &key, &val) <= -1) goto oops; - } - - if (client->rep.last_attr_type == HCL_CLIENT_REPLY_ATTR_TYPE_DATA) - { - hcl_oow_t length; - - /* this must be the last attr. the trailing part are all data */ - if (is_token(client, "chunked")) - { - client->state = HCL_CLIENT_STATE_IN_CHUNKED_DATA; - HCL_MEMSET (&client->rep.u.chunked_data, 0, HCL_SIZEOF(client->rep.u.chunked_data)); - } - else if (is_token_integer(client, &length)) - { - if (length > 0) - { - client->state = HCL_CLIENT_STATE_IN_LENGTH_BOUNDED_DATA; - /* [NOTE] the max length for the length-bounded transfer scheme is limited - * by the system word size as of this implementation */ - client->rep.u.length_bounded_data.max = length; - client->rep.u.length_bounded_data.tally = 0; - } - else - { - /* .DATA 0 has been received. this should be end of the reply */ - client->state = HCL_CLIENT_STATE_START; - } - } - else - { - hcl_client_seterrbfmt (client, HCL_EINVAL, "invalid attribute value for .DATA - %.*js", client->rep.tok.len, client->rep.tok.ptr); - goto oops; - } - } - else - { - /* continue processing the next attr */ - client->state = HCL_CLIENT_STATE_IN_ATTR_KEY; - } - - clear_reply_token (client); - break; - } - else if (is_spacechar(c)) - { - /* skip white spaces after the closing quote */ - break; - } - else - { - hcl_client_seterrbfmt (client, HCL_EINVAL, "garbage after quoted attribute value for %.*js", client->rep.last_attr_key.len, client->rep.last_attr_key.ptr); - goto oops; - } - - default: - hcl_client_seterrbfmt (client, HCL_EINTERN, "internal error - must not be called for state %d", (int)client->state); - goto oops; - } - - return 0; - -oops: - return -1; -} - -static int feed_reply_data (hcl_client_t* client, const hcl_bch_t* data, hcl_oow_t len, hcl_oow_t* xlen) -{ - const hcl_bch_t* ptr; - const hcl_bch_t* end; - - ptr = data; - end = ptr + len; - - while (ptr < end) - { - if (client->state == HCL_CLIENT_STATE_IN_LENGTH_BOUNDED_DATA) - { - /* the data is treated as raw octets by this client */ - hcl_oow_t capa, avail, taken; - - capa = client->rep.u.length_bounded_data.max - client->rep.u.length_bounded_data.tally; - avail = end - ptr; - taken = (avail < capa)? avail: capa; - - if (client->prim.feed_data) - { - if (client->prim.feed_data(client, ptr, taken) <= -1) goto oops; - } - - ptr += taken; - client->rep.u.length_bounded_data.tally += taken; - if (taken == capa) - { - /* read all data. no more */ - HCL_ASSERT (client->dummy_hcl, client->rep.u.length_bounded_data.max == client->rep.u.length_bounded_data.tally); - client->state = HCL_CLIENT_STATE_START; - if (client->prim.end_reply(client, HCL_CLIENT_END_REPLY_STATE_OK) <= -1) goto oops; - } - } - else if (client->state == HCL_CLIENT_STATE_IN_CHUNKED_DATA) - { - /* the data is treated as raw octets by this client */ - if (client->rep.u.chunked_data.in_data_part) - { - hcl_oow_t capa, avail, taken; - - capa = client->rep.u.chunked_data.max - client->rep.u.chunked_data.tally; - avail = end - ptr; - taken = (avail < capa)? avail: capa; - - if (client->prim.feed_data) - { - if (client->prim.feed_data(client, ptr, taken) <= -1) goto oops; - } - - ptr += taken; - client->rep.u.chunked_data.tally += taken; - client->rep.u.chunked_data.total += taken; - if (taken == capa) - { - /* finished one chunk */ - client->rep.u.chunked_data.negated = 0; - client->rep.u.chunked_data.max = 0; - client->rep.u.chunked_data.tally = 0; - client->rep.u.chunked_data.clcount = 0; - client->rep.u.chunked_data.in_data_part = 0; - } - } - else - { - while (ptr < end) - { - hcl_bchu_t bc = (hcl_bchu_t)*ptr++; - if (bc == '-' && client->rep.u.chunked_data.clcount == 0 && !client->rep.u.chunked_data.negated) - { - client->rep.u.chunked_data.negated = 1; - } - else if (bc == ':') - { - if (client->rep.u.chunked_data.clcount == 0) - { - hcl_client_seterrbfmt (client, HCL_EINVAL, "clone without valid chunk length"); - goto oops; - } - - if (client->rep.u.chunked_data.negated) - { - if (client->prim.end_reply(client, HCL_CLIENT_END_REPLY_STATE_REVOKED) <= -1) goto oops; - client->state = HCL_CLIENT_STATE_START; - - } - if (client->rep.u.chunked_data.max == 0) - { - if (client->prim.end_reply(client, HCL_CLIENT_END_REPLY_STATE_OK) <= -1) goto oops; - client->state = HCL_CLIENT_STATE_START; - } - else - { - client->rep.u.chunked_data.in_data_part = 1; - client->rep.u.chunked_data.tally = 0; - } - break; - } - else if (is_digitchar(bc)) - { - client->rep.u.chunked_data.max = client->rep.u.chunked_data.max * 10 + (bc - '0'); - client->rep.u.chunked_data.clcount++; - } - else - { - hcl_client_seterrbfmt (client, HCL_EINVAL, "invalid chunk length character - [%jc]", (hcl_ooch_t)bc); - goto oops; - } - } - } - } - else - { - hcl_ooci_t c; - hcl_oow_t bcslen; - - #if defined(HCL_OOCH_IS_UCH) - hcl_oow_t ucslen; - hcl_ooch_t uc; - int n; - - bcslen = end - ptr; - ucslen = 1; - - n = hcl_conv_bchars_to_uchars_with_cmgr(ptr, &bcslen, &uc, &ucslen, hcl_client_getcmgr(client), 0); - if (n <= -1) - { - if (n == -3) - { - /* incomplete sequence */ - *xlen = ptr - data; - return 0; /* feed more for incomplete sequence */ - } - - uc = *ptr; - bcslen = 1; - } - - ptr += bcslen; - c = uc; - #else - bcslen = 1; - c = *ptr++; - #endif - - if (handle_char(client, c, bcslen) <= -1) goto oops; - } - } - - *xlen = ptr - data; - return 1; - -oops: - /* TODO: compute the number of processed bytes so far and return it via a parameter??? */ -/*printf ("feed oops....\n");*/ - return -1; -} - - -/* ========================================================================= */ - -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 = 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) -{ - if (client->rep.tok.ptr) hcl_client_freemem (client, client->rep.tok.ptr); - if (client->rep.last_attr_key.ptr) hcl_client_freemem (client, client->rep.last_attr_key.ptr); - 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); -} - -/* ========================================================================= */ - -hcl_client_state_t hcl_client_getstate (hcl_client_t* client) -{ - return client->state; -} - -void hcl_client_reset (hcl_client_t* client) -{ - /* TODO: reset XXXXXXXXXXXXXXXXXXXXXXXXXXXxxxxx */ - client->state = HCL_CLIENT_STATE_START; -} - -int hcl_client_feed (hcl_client_t* client, const void* ptr, hcl_oow_t len, hcl_oow_t* xlen) -{ - int x; - hcl_oow_t total, ylen; - const hcl_bch_t* buf; - - buf = (const hcl_bch_t*)ptr; - total = 0; - while (total < len) - { - x = feed_reply_data(client, &buf[total], len - total, &ylen); - if (x <= -1) return -1; - - total += ylen; - if (x == 0) break; /* incomplete sequence encountered */ - } - - *xlen = total; - return 0; -} - -#endif diff --git a/lib/hcl-c.h b/lib/hcl-c.h deleted file mode 100644 index 74c47a4..0000000 --- a/lib/hcl-c.h +++ /dev/null @@ -1,271 +0,0 @@ -/* - 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. - */ -#if 0 - -#ifndef _HCL_C_H_ -#define _HCL_C_H_ - -#include - -typedef struct hcl_client_t hcl_client_t; - -enum hcl_client_option_t -{ - HCL_CLIENT_TRAIT, - HCL_CLIENT_LOG_MASK, -}; -typedef enum hcl_client_option_t hcl_client_option_t; - -enum hcl_client_trait_t -{ - /* no trait defined at this moment. XXXX is just a placeholder */ - HCL_CLIENT_XXXX = (1 << 0) -}; -typedef enum hcl_client_trait_t hcl_client_trait_t; - -/* ========================================================================= */ - -enum hcl_client_state_t -{ - HCL_CLIENT_STATE_START, - HCL_CLIENT_STATE_IN_REPLY_NAME, - HCL_CLIENT_STATE_IN_REPLY_VALUE_START, - HCL_CLIENT_STATE_IN_REPLY_VALUE_UNQUOTED, - HCL_CLIENT_STATE_IN_REPLY_VALUE_QUOTED, - HCL_CLIENT_STATE_IN_REPLY_VALUE_QUOTED_TRAILER, - HCL_CLIENT_STATE_IN_ATTR_KEY, - HCL_CLIENT_STATE_IN_ATTR_VALUE_START, - HCL_CLIENT_STATE_IN_ATTR_VALUE_UNQUOTED, - HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED, - HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED_TRAILER, - - HCL_CLIENT_STATE_IN_LENGTH_BOUNDED_DATA, - HCL_CLIENT_STATE_IN_CHUNKED_DATA -}; -typedef enum hcl_client_state_t hcl_client_state_t; - -/* ========================================================================= */ - -enum hcl_client_reply_type_t -{ - HCL_CLIENT_REPLY_TYPE_OK = 0, - HCL_CLIENT_REPLY_TYPE_ERROR = 1 -}; -typedef enum hcl_client_reply_type_t hcl_client_reply_type_t; - -typedef void (*hcl_client_log_write_t) ( - hcl_client_t* client, - hcl_bitmask_t mask, - const hcl_ooch_t* msg, - hcl_oow_t len -); - -typedef int (*hcl_client_start_reply_t) ( - hcl_client_t* client, - hcl_client_reply_type_t type, - const hcl_ooch_t* dptr, - hcl_oow_t dlen -); - -typedef int (*hcl_client_feed_attr_t) ( - hcl_client_t* client, - const hcl_oocs_t* key, - const hcl_oocs_t* val -); - -typedef int (*hcl_client_start_data_t) ( - hcl_client_t* client -); - -typedef int (*hcl_client_feed_data_t) ( - hcl_client_t* client, - const void* ptr, - hcl_oow_t len -); - -typedef int (*hcl_client_end_data_t) ( - hcl_client_t* client -); - -enum hcl_client_end_reply_state_t -{ - HCL_CLIENT_END_REPLY_STATE_OK, - HCL_CLIENT_END_REPLY_STATE_REVOKED -}; -typedef enum hcl_client_end_reply_state_t hcl_client_end_reply_state_t; - -typedef int (*hcl_client_end_reply_t) ( - hcl_client_t* client, - hcl_client_end_reply_state_t state -); - -struct hcl_client_prim_t -{ - hcl_client_log_write_t log_write; - - hcl_client_start_reply_t start_reply; /* mandatory */ - hcl_client_feed_attr_t feed_attr; /* optional */ - hcl_client_feed_data_t feed_data; /* optional */ - hcl_client_end_reply_t end_reply; /* mandatory */ -}; -typedef struct hcl_client_prim_t hcl_client_prim_t; - -/* ========================================================================= */ - -#if defined(__cplusplus) -extern "C" { -#endif - -HCL_EXPORT hcl_client_t* hcl_client_open ( - hcl_mmgr_t* mmgr, - hcl_oow_t xtnsize, - hcl_client_prim_t* prim, - hcl_errnum_t* errnum -); - -HCL_EXPORT void hcl_client_close ( - hcl_client_t* client -); - -HCL_EXPORT void hcl_client_reset ( - hcl_client_t* client -); - -HCL_EXPORT int hcl_client_feed ( - hcl_client_t* client, - const void* ptr, - hcl_oow_t len, - hcl_oow_t* xlen -); - -HCL_EXPORT hcl_client_state_t hcl_client_getstate ( - hcl_client_t* client -); - -HCL_EXPORT int hcl_client_setoption ( - hcl_client_t* client, - hcl_client_option_t id, - const void* value -); - -HCL_EXPORT int hcl_client_getoption ( - hcl_client_t* client, - hcl_client_option_t id, - void* value -); - - -HCL_EXPORT void* hcl_client_getxtn ( - hcl_client_t* client -); - -HCL_EXPORT hcl_mmgr_t* hcl_client_getmmgr ( - hcl_client_t* client -); - -HCL_EXPORT hcl_cmgr_t* hcl_client_getcmgr ( - hcl_client_t* client -); - -HCL_EXPORT void hcl_client_setcmgr ( - hcl_client_t* client, - hcl_cmgr_t* cmgr -); - - -HCL_EXPORT hcl_errnum_t hcl_client_geterrnum ( - hcl_client_t* client -); - -HCL_EXPORT const hcl_ooch_t* hcl_client_geterrstr ( - hcl_client_t* client -); - -HCL_EXPORT const hcl_ooch_t* hcl_client_geterrmsg ( - hcl_client_t* client -); - -HCL_EXPORT void hcl_client_seterrnum ( - hcl_client_t* client, - hcl_errnum_t errnum -); - -HCL_EXPORT void hcl_client_seterrbfmt ( - hcl_client_t* client, - hcl_errnum_t errnum, - const hcl_bch_t* fmt, - ... -); - -HCL_EXPORT void hcl_client_seterrufmt ( - hcl_client_t* client, - hcl_errnum_t errnum, - const hcl_uch_t* fmt, - ... -); - -HCL_EXPORT void hcl_client_logbfmt ( - hcl_client_t* client, - hcl_bitmask_t mask, - const hcl_bch_t* fmt, - ... -); - -HCL_EXPORT void hcl_client_logufmt ( - hcl_client_t* client, - hcl_bitmask_t mask, - const hcl_uch_t* fmt, - ... -); - -HCL_EXPORT void* hcl_client_allocmem ( - hcl_client_t* client, - hcl_oow_t size -); - -HCL_EXPORT void* hcl_client_callocmem ( - hcl_client_t* client, - hcl_oow_t size -); - -HCL_EXPORT void* hcl_client_reallocmem ( - hcl_client_t* client, - void* ptr, - hcl_oow_t size -); - - -HCL_EXPORT void hcl_client_freemem ( - hcl_client_t* client, - void* ptr -); - -#if defined(__cplusplus) -} -#endif - -#endif - - -#endif diff --git a/lib/hcl-s.c b/lib/hcl-s.c deleted file mode 100644 index 03a0d70..0000000 --- a/lib/hcl-s.c +++ /dev/null @@ -1,2560 +0,0 @@ -/* - 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. - */ -#if 0 - -#include "hcl-s.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_SERVER_PROTO_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 worker_hcl_xtn_t -{ - hcl_server_proto_t* proto; - 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_proto_token_type_t -{ - HCL_SERVER_PROTO_TOKEN_EOF, - HCL_SERVER_PROTO_TOKEN_NL, - - HCL_SERVER_PROTO_TOKEN_BEGIN, - HCL_SERVER_PROTO_TOKEN_END, - HCL_SERVER_PROTO_TOKEN_SCRIPT, - HCL_SERVER_PROTO_TOKEN_EXIT, - - /*HCL_SERVER_PROTO_TOKEN_AUTH,*/ - HCL_SERVER_PROTO_TOKEN_KILL_WORKER, - HCL_SERVER_PROTO_TOKEN_SHOW_WORKERS, - - HCL_SERVER_PROTO_TOKEN_IDENT, - HCL_SERVER_PROTO_TOKEN_NUMBER -}; -typedef enum hcl_server_proto_token_type_t hcl_server_proto_token_type_t; - -typedef struct hcl_server_proto_token_t hcl_server_proto_token_t; -struct hcl_server_proto_token_t -{ - hcl_server_proto_token_type_t type; - hcl_ooch_t* ptr; - hcl_oow_t len; - hcl_oow_t capa; - hcl_loc_t loc; -}; - -enum hcl_server_proto_req_state_t -{ - HCL_SERVER_PROTO_REQ_IN_TOP_LEVEL, - HCL_SERVER_PROTO_REQ_IN_BLOCK_LEVEL -}; - -enum hcl_server_proto_reply_type_t -{ - HCL_SERVER_PROTO_REPLY_SIMPLE = 0, - HCL_SERVER_PROTO_REPLY_CHUNKED -}; -typedef enum hcl_server_proto_reply_type_t hcl_server_proto_reply_type_t; - -struct hcl_server_proto_t -{ - hcl_server_worker_t* worker; - - hcl_t* hcl; - hcl_lxc_t* lxc; - hcl_oow_t unread_count; - hcl_lxc_t unread_lxc; - hcl_server_proto_token_t tok; - hcl_tmr_index_t exec_runtime_event_index; - - struct - { - int state; - } req; - - struct - { - hcl_server_proto_reply_type_t type; - hcl_oow_t nchunks; - hcl_bch_t buf[HCL_SERVER_PROTO_REPLY_BUF_SIZE]; - hcl_oow_t len; - } reply; -}; - -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_server_proto_t* proto; - - 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]; - - 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 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->proto->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->proto->worker->sck; - } - - HCL_ASSERT (hcl, bb->fd >= 0); - - arg->handle = bb; - return 0; - -oops: - if (bb) - { - if (bb->fd >= 0 && bb->fd != xtn->proto->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->proto->worker->sck) close (bb->fd); - hcl_freemem (hcl, bb); - - arg->handle = HCL_NULL; - return 0; -} - -static HCL_INLINE int read_input (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->proto->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_input(hcl, (hcl_io_cciarg_t*)arg); - - 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: - return 0; - - case HCL_IO_CLOSE: - 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; - - if (hcl_server_proto_feed_reply(xtn->proto, outarg->ptr, outarg->len, 0) <= -1) - { - /* TODO: change error code and message. propagage the errormessage from proto */ - hcl_seterrbfmt (hcl, HCL_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; - - if (hcl_server_proto_feed_reply_bytes(xtn->proto, outarg->ptr, outarg->len, 0) <= -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 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->proto->worker->server; - pthread_mutex_lock (&server->log_mutex); - server->prim.log_write (server, xtn->proto->worker->wid, mask, msg, len); - pthread_mutex_unlock (&server->log_mutex); -} - -static void 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->proto->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); -} -*/ -/* ========================================================================= */ - -#define SERVER_LOGMASK_INFO (HCL_LOG_INFO | HCL_LOG_APP) -#define SERVER_LOGMASK_ERROR (HCL_LOG_ERROR | HCL_LOG_APP) - -hcl_server_proto_t* hcl_server_proto_open (hcl_oow_t xtnsize, hcl_server_worker_t* worker) -{ - hcl_server_proto_t* proto; - hcl_cb_t hclcb; - worker_hcl_xtn_t* xtn; - hcl_bitmask_t trait; - - proto = (hcl_server_proto_t*)hcl_server_allocmem(worker->server, HCL_SIZEOF(*proto)); - if (!proto) return HCL_NULL; - - HCL_MEMSET (proto, 0, HCL_SIZEOF(*proto)); - proto->worker = worker; - proto->exec_runtime_event_index = HCL_TMR_INVALID_INDEX; - - proto->hcl = hcl_openstdwithmmgr(hcl_server_getmmgr(proto->worker->server), HCL_SIZEOF(*xtn), HCL_NULL); - if (!proto->hcl) goto oops; - - /* replace the vmprim.log_write function */ - proto->hcl->vmprim.log_write = log_write; - - xtn = (worker_hcl_xtn_t*)hcl_getxtn(proto->hcl); - xtn->proto = proto; - - hcl_setoption (proto->hcl, HCL_MOD_INCTX, &proto->worker->server->cfg.module_inctx); - hcl_setoption (proto->hcl, HCL_LOG_MASK, &proto->worker->server->cfg.logmask); - hcl_setcmgr (proto->hcl, hcl_server_getcmgr(proto->worker->server)); - - hcl_getoption (proto->hcl, HCL_TRAIT, &trait); -#if defined(HCL_BUILD_DEBUG) - if (proto->worker->server->cfg.trait & HCL_SERVER_TRAIT_DEBUG_GC) trait |= HCL_TRAIT_DEBUG_GC; - if (proto->worker->server->cfg.trait & HCL_SERVER_TRAIT_DEBUG_BIGINT) trait |= HCL_TRAIT_DEBUG_BIGINT; -#endif - hcl_setoption (proto->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 (proto->hcl, &hclcb); - - if (hcl_ignite(proto->hcl, worker->server->cfg.actor_heap_size) <= -1) goto oops; - if (hcl_addbuiltinprims(proto->hcl) <= -1) goto oops; - - if (hcl_attachccio(proto->hcl, read_handler) <= -1) goto oops; - if (hcl_attachudio(proto->hcl, HCL_NULL, print_handler) <= -1) goto oops; - return proto; - -oops: - if (proto) - { - if (proto->hcl) hcl_close (proto->hcl); - hcl_server_freemem (proto->worker->server, proto); - } - return HCL_NULL; -} - -void hcl_server_proto_close (hcl_server_proto_t* proto) -{ - if (proto->tok.ptr) hcl_server_freemem (proto->worker->server, proto->tok.ptr); - hcl_close (proto->hcl); - hcl_server_freemem (proto->worker->server, proto); -} - -static int write_reply_chunk (hcl_server_proto_t* proto) -{ - struct msghdr msg; - struct iovec iov[3]; - hcl_bch_t cl[16]; /* ensure that this is large enough for the chunk length string */ - int index = 0, count = 0; - - if (proto->reply.type == HCL_SERVER_PROTO_REPLY_CHUNKED) - { - if (proto->reply.nchunks <= 0) - { - /* this is the first chunk */ - iov[count].iov_base = (void*)".OK\n.DATA chunked\n"; - iov[count++].iov_len = 18; - } - - iov[count].iov_base = cl, - iov[count++].iov_len = snprintf(cl, HCL_SIZEOF(cl), "%zu:", proto->reply.len); - } - iov[count].iov_base = proto->reply.buf; - iov[count++].iov_len = proto->reply.len; - - while (1) - { - ssize_t nwritten; - - HCL_MEMSET (&msg, 0, HCL_SIZEOF(msg)); - msg.msg_iov = (struct iovec*)&iov[index]; - msg.msg_iovlen = count - index; - nwritten = sendmsg(proto->worker->sck, &msg, 0); - /*nwritten = writev(proto->worker->sck, (const struct iovec*)&iov[index], count - index);*/ - 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 */ - HCL_LOG2 (proto->hcl, SERVER_LOGMASK_ERROR, "Unable to sendmsg on %d - %hs\n", proto->worker->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; - } - - if (proto->reply.len <= 0) - { - /* this should be the last chunk */ - proto->reply.nchunks = 0; - } - else - { - proto->reply.nchunks++; - proto->reply.len = 0; - } - - return 0; -} - -void hcl_server_proto_start_reply (hcl_server_proto_t* proto) -{ - proto->reply.type = HCL_SERVER_PROTO_REPLY_CHUNKED; - proto->reply.nchunks = 0; - proto->reply.len = 0; -} - -int hcl_server_proto_feed_reply (hcl_server_proto_t* proto, const hcl_ooch_t* ptr, hcl_oow_t len, int escape) -{ -#if defined(HCL_OOCH_IS_UCH) - hcl_oow_t bcslen, ucslen, donelen; - int x; - - donelen = 0; - while (donelen < len) - { - if (escape) - { - if (ptr[donelen] == '\\' || ptr[donelen] == '\"') - { - /* i know that these characters don't need encoding conversion */ - if (proto->reply.len >= HCL_COUNTOF(proto->reply.buf) && write_reply_chunk(proto) <=-1) return -1; - proto->reply.buf[proto->reply.len++] = '\\'; - } - bcslen = HCL_COUNTOF(proto->reply.buf) - proto->reply.len; - if (bcslen < HCL_BCSIZE_MAX) - { - if (write_reply_chunk(proto) <=-1) return -1; - bcslen = HCL_COUNTOF(proto->reply.buf) - proto->reply.len; - } - ucslen = 1; /* i must go one by one for escaping */ - } - else - { - bcslen = HCL_COUNTOF(proto->reply.buf) - proto->reply.len; - if (bcslen < HCL_BCSIZE_MAX) - { - if (write_reply_chunk(proto) <=-1) return -1; - bcslen = HCL_COUNTOF(proto->reply.buf) - proto->reply.len; - } - ucslen = len - donelen; - } - - x = hcl_convootobchars(proto->hcl, &ptr[donelen], &ucslen, &proto->reply.buf[proto->reply.len], &bcslen); - if (x <= -1 && ucslen <= 0) return -1; - - donelen += ucslen; - proto->reply.len += bcslen; - } - - return 0; -#else - while (len > 0) - { - if (escape && (*ptr == '\\' || *ptr == '\"')) - { - if (proto->reply.len >= HCL_COUNTOF(proto->reply.buf) && write_reply_chunk(proto) <=-1) return -1; - proto->reply.buf[proto->reply.len++] = '\\'; - } - - if (proto->reply.len >= HCL_COUNTOF(proto->reply.buf) && write_reply_chunk(proto) <=-1) return -1; - proto->reply.buf[proto->reply.len++] = *ptr++; - len--; - } - - return 0; -#endif -} - -int hcl_server_proto_feed_reply_bytes (hcl_server_proto_t* proto, const hcl_bch_t* ptr, hcl_oow_t len, int escape) -{ - while (len > 0) - { - if (escape && (*ptr == '\\' || *ptr == '\"')) - { - if (proto->reply.len >= HCL_COUNTOF(proto->reply.buf) && write_reply_chunk(proto) <=-1) return -1; - proto->reply.buf[proto->reply.len++] = '\\'; - } - - if (proto->reply.len >= HCL_COUNTOF(proto->reply.buf) && write_reply_chunk(proto) <=-1) return -1; - proto->reply.buf[proto->reply.len++] = *ptr++; - len--; - } - - return 0; -} - -int hcl_server_proto_end_reply (hcl_server_proto_t* proto, const hcl_ooch_t* failmsg) -{ - HCL_ASSERT (proto->hcl, proto->reply.type == HCL_SERVER_PROTO_REPLY_CHUNKED); - - if (failmsg) - { - if (proto->reply.nchunks <= 0 && proto->reply.len <= 0) - { - static hcl_ooch_t err1[] = { '.','E','R','R','O','R',' ','\"' }; - static hcl_ooch_t err2[] = { '\"','\n' }; - proto->reply.type = HCL_SERVER_PROTO_REPLY_SIMPLE; /* switch to the simple mode forcibly */ - - simple_error: - if (hcl_server_proto_feed_reply(proto, err1, 8, 0) <= -1 || - hcl_server_proto_feed_reply(proto, failmsg, hcl_count_oocstr(failmsg), 1) <= -1 || - hcl_server_proto_feed_reply(proto, err2, 2, 0) <= -1) return -1; - - if (write_reply_chunk(proto) <= -1) return -1; - } - else - { - /* some chunks have beed emitted. but at the end, an error has occurred. - * send -1: as the last chunk. the receiver must rub out the reply - * buffer received so far and expect the following .ERROR response */ - static hcl_ooch_t err0[] = { '-','1',':' }; - if (proto->reply.len > 0 && write_reply_chunk(proto) <= -1) return -1; - - proto->reply.type = HCL_SERVER_PROTO_REPLY_SIMPLE; /* switch to the simple mode forcibly */ - proto->reply.nchunks = 0; - proto->reply.len = 0; - - if (hcl_server_proto_feed_reply(proto, err0, 3, 0) <= -1) return -1; - goto simple_error; - } - } - else - { - if (proto->reply.nchunks <= 0 && proto->reply.len <= 0) - { - /* in the chunked mode. but no output has been made so far */ - static hcl_ooch_t ok[] = { '.','O','K',' ','\"','\"','\n' }; - proto->reply.type = HCL_SERVER_PROTO_REPLY_SIMPLE; /* switch to the simple mode forcibly */ - if (hcl_server_proto_feed_reply(proto, ok, 7, 0) <= -1) return -1; - if (write_reply_chunk(proto) <= -1) return -1; - } - else - { - if (proto->reply.len > 0 && write_reply_chunk(proto) <= -1) return -1; - if (write_reply_chunk(proto) <= -1) return -1; /* write 0: */ - } - } - - return 0; -} - -static HCL_INLINE int is_spacechar (hcl_ooci_t c) -{ - /* TODO: handle other space unicode characters */ - switch (c) - { - case ' ': - case '\f': /* formfeed */ - case '\r': /* carriage return */ - case '\t': /* horizon tab */ - case '\v': /* vertical tab */ - return 1; - - default: - return 0; - } -} - -static HCL_INLINE int is_alphachar (hcl_ooci_t c) -{ -/* TODO: support full unicode */ - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); -} - -static HCL_INLINE int is_digitchar (hcl_ooci_t c) -{ -/* TODO: support full unicode */ - return (c >= '0' && c <= '9'); -} - -static HCL_INLINE int read_char (hcl_server_proto_t* proto) -{ - if (proto->unread_count > 0) - { - proto->lxc = &proto->unread_lxc; - proto->unread_count--; - return 0; - } - - proto->lxc = hcl_readbasesrchar(proto->hcl); - if (!proto->lxc) return -1; - return 0; -} - -static HCL_INLINE int unread_last_char (hcl_server_proto_t* proto) -{ - if (proto->unread_count >= 1) - { - /* only 1 character can be unread */ - hcl_seterrbfmt (proto->hcl, HCL_EFLOOD, "too many unread characters"); - return -1; - } - - if (proto->lxc != &proto->unread_lxc) proto->unread_lxc = *proto->lxc; - proto->unread_count++; - return 0; -} - -#define GET_CHAR_TO(proto,ch) \ - do { \ - if (read_char(proto) <= -1) return -1; \ - ch = (proto)->lxc->c; \ - } while(0) - -#define GET_CHAR_TO_WITH_GOTO(proto,ch,oops) \ - do { \ - if (read_char(proto) <= -1) goto oops; \ - ch = (proto)->lxc->c; \ - } while(0) - -#define UNGET_LAST_CHAR(proto) \ - do { \ - if (unread_last_char(proto) <= -1) return -1; \ - } while (0) - -#define SET_TOKEN_TYPE(proto,tv) ((proto)->tok.type = (tv)) -#define ADD_TOKEN_CHAR(proto,ch) \ - do { if (add_token_char(proto, ch) <= -1) return -1; } while (0) - - -static HCL_INLINE int add_token_char (hcl_server_proto_t* proto, hcl_ooch_t c) -{ - if (proto->tok.len >= proto->tok.capa) - { - hcl_ooch_t* tmp; - hcl_oow_t capa; - - capa = HCL_ALIGN_POW2(proto->tok.len + 1, HCL_SERVER_TOKEN_NAME_ALIGN); - tmp = (hcl_ooch_t*)hcl_server_reallocmem(proto->worker->server, proto->tok.ptr, capa * HCL_SIZEOF(*tmp)); - if (!tmp) - { - hcl_seterrbfmt (proto->hcl, HCL_ESYSMEM, "Out of memory in allocating token buffer"); - return -1; - } - - proto->tok.ptr = tmp; - proto->tok.capa = capa; - } - - proto->tok.ptr[proto->tok.len++] = c; - return 0; -} - -static void classify_current_ident_token (hcl_server_proto_t* proto) -{ - static struct cmd_t - { - hcl_server_proto_token_type_t type; - hcl_ooch_t name[32]; - } tab[] = - { - { HCL_SERVER_PROTO_TOKEN_BEGIN, { '.','B','E','G','I','N','\0' } }, - { HCL_SERVER_PROTO_TOKEN_END, { '.','E','N','D','\0' } }, - { HCL_SERVER_PROTO_TOKEN_SCRIPT, { '.','S','C','R','I','P','T','\0' } }, - { HCL_SERVER_PROTO_TOKEN_EXIT, { '.','E','X','I','T','\0' } }, - - { HCL_SERVER_PROTO_TOKEN_KILL_WORKER, { '.','K','I','L','L','-','W','O','R','K','E','R','\0' } }, - { HCL_SERVER_PROTO_TOKEN_SHOW_WORKERS, { '.','S','H','O','W','-','W','O','R','K','E','R','S','\0' } }, - /* TODO: add more */ - }; - hcl_oow_t i; - - for (i = 0; i < HCL_COUNTOF(tab); i++) - { - if (hcl_comp_oochars_oocstr(proto->tok.ptr, proto->tok.len, tab[i].name) == 0) - { - SET_TOKEN_TYPE (proto, tab[i].type); - break; - } - } -} - -static int get_token (hcl_server_proto_t* proto) -{ - hcl_ooci_t c; - - GET_CHAR_TO (proto, c); - - /* skip spaces */ - while (is_spacechar(c)) GET_CHAR_TO (proto, c); - - SET_TOKEN_TYPE (proto, HCL_SERVER_PROTO_TOKEN_EOF); /* is it correct? */ - proto->tok.len = 0; - proto->tok.loc = proto->lxc->l; /* set token location */ - - switch (c) - { - case HCL_OOCI_EOF: - SET_TOKEN_TYPE (proto, HCL_SERVER_PROTO_TOKEN_EOF); - break; - - case '\n': - SET_TOKEN_TYPE (proto, HCL_SERVER_PROTO_TOKEN_NL); - break; - - case '.': - SET_TOKEN_TYPE (proto, HCL_SERVER_PROTO_TOKEN_IDENT); - - ADD_TOKEN_CHAR(proto, c); - GET_CHAR_TO(proto, c); - if (!is_alphachar(c)) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "Alphabetic character expected after a period"); - return -1; - } - - do - { - ADD_TOKEN_CHAR(proto, c); - GET_CHAR_TO(proto, c); - } - while (is_alphachar(c) || c == '-'); - - UNGET_LAST_CHAR (proto); - - classify_current_ident_token (proto); - break; - - default: - if (is_digitchar(c)) - { - SET_TOKEN_TYPE (proto, HCL_SERVER_PROTO_TOKEN_NUMBER); - - do - { - ADD_TOKEN_CHAR(proto, c); - GET_CHAR_TO(proto, c); - } - while (is_digitchar(c)); - - UNGET_LAST_CHAR (proto); - break; - } - - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "Unrecognized character - [%jc]", c); - return -1; - } - 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_proto_t* proto; - proto = (hcl_server_proto_t*)evt->ctx; - - HCL_LOG1 (proto->worker->server->dummy_hcl, SERVER_LOGMASK_INFO, "Aborting script execution for max_actor_runtime exceeded [%zu]\n", proto->worker->wid); - hcl_abort (proto->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_server_proto_t* proto; - proto = (hcl_server_proto_t*)evt->ctx; - HCL_ASSERT (proto->hcl, proto->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; -} - -static int insert_exec_timer (hcl_server_proto_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_t* server; - - HCL_ASSERT (proto->hcl, proto->exec_runtime_event_index == HCL_TMR_INVALID_INDEX); - - server = proto->worker->server; - - HCL_MEMSET (&event, 0, HCL_SIZEOF(event)); - event.ctx = proto; - proto->hcl->vmprim.vm_gettime (proto->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); - proto->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_server_proto_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 - * HCL_TMR_INVALID_INDEX as set by exec_runtime_handler */ - hcl_server_t* server; - - server = proto->worker->server; - - pthread_mutex_lock (&server->tmr_mutex); - if (proto->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 (proto->hcl, proto->exec_runtime_event_index == HCL_TMR_INVALID_INDEX); - /*proto->exec_runtime_event_index = HCL_TMR_INVALID_INDEX; */ - } - pthread_mutex_unlock (&server->tmr_mutex); -} - -static int execute_script (hcl_server_proto_t* proto, const hcl_bch_t* trigger) -{ - hcl_oop_t obj; - const hcl_ooch_t* failmsg = HCL_NULL; - hcl_server_t* server; - - server = proto->worker->server; - - hcl_server_proto_start_reply (proto); - if (server->cfg.actor_max_runtime.sec <= 0 && server->cfg.actor_max_runtime.sec <= 0) - { - obj = hcl_execute(proto->hcl); - if (!obj) failmsg = hcl_geterrmsg(proto->hcl); - } - else - { - if (insert_exec_timer(proto, &server->cfg.actor_max_runtime) <= -1) - { - HCL_LOG0 (proto->hcl, SERVER_LOGMASK_ERROR, "Cannot start execution timer\n"); - hcl_seterrbfmt (proto->hcl, HCL_ESYSMEM, "cannot start execution timer"); /* i do this just to compose the error message */ - failmsg = hcl_geterrmsg(proto->hcl); - } - else - { - obj = hcl_execute(proto->hcl); - if (!obj) failmsg = hcl_geterrmsg(proto->hcl); - delete_exec_timer (proto); - } - } - - if (hcl_server_proto_end_reply(proto, failmsg) <= -1) - { - HCL_LOG1 (proto->hcl, SERVER_LOGMASK_ERROR, "Cannot finalize reply for %hs\n", trigger); - return -1; - } - - return 0; -} - - -static void send_error_message (hcl_server_proto_t* proto, const hcl_ooch_t* errmsg) -{ - hcl_server_proto_start_reply (proto); - if (hcl_server_proto_end_reply(proto, errmsg) <= -1) - { - HCL_LOG1 (proto->hcl, SERVER_LOGMASK_ERROR, "Unable to send error message - %s\n", errmsg); - } -} - -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_server_proto_t* proto) -{ - if (HCL_ERRNUM(proto->hcl) == HCL_ESYNERR) reformat_synerr (proto->hcl); - send_error_message (proto, hcl_geterrmsg(proto->hcl)); -} - -static void show_server_workers (hcl_server_proto_t* proto) -{ - hcl_server_t* server; - hcl_server_worker_t* w; - - server = proto->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 (proto->hcl, "%zu %d %d\n", w->wid, w->sck, 1000); - } - pthread_mutex_unlock (&server->worker_mutex); -} - -static int kill_server_worker (hcl_server_proto_t* proto, hcl_oow_t wid) -{ - hcl_server_t* server; - int xret = 0; - - server = proto->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->proto) hcl_abort (worker->proto->hcl); - } - } - } - pthread_mutex_unlock (&server->worker_mutex); - return xret; -} - -int hcl_server_proto_handle_request (hcl_server_proto_t* proto) -{ - if (get_token(proto) <= -1) goto fail_with_errmsg; - - switch (proto->tok.type) - { - case HCL_SERVER_PROTO_TOKEN_NL: - /* ignore new lines */ - break; - - case HCL_SERVER_PROTO_TOKEN_EOF: - if (proto->req.state != HCL_SERVER_PROTO_REQ_IN_TOP_LEVEL) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "Unexpected EOF without .END"); - goto fail_with_errmsg; - } - /* drop connection silently */ - return 0; - - case HCL_SERVER_PROTO_TOKEN_EXIT: - if (proto->req.state != HCL_SERVER_PROTO_REQ_IN_TOP_LEVEL) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, ".EXIT allowed in the top level only"); - goto fail_with_errmsg; - } - - if (get_token(proto) <= -1) goto fail_with_errmsg; - if (proto->tok.type != HCL_SERVER_PROTO_TOKEN_NL) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "No new line after .EXIT"); - goto fail_with_errmsg; - } - - hcl_server_proto_start_reply (proto); - if (hcl_server_proto_end_reply(proto, HCL_NULL) <= -1) - { - HCL_LOG0 (proto->hcl, SERVER_LOGMASK_ERROR, "Unable to finalize reply for .EXIT\n"); - return -1; - } - return 0; - - case HCL_SERVER_PROTO_TOKEN_BEGIN: - if (proto->req.state != HCL_SERVER_PROTO_REQ_IN_TOP_LEVEL) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, ".BEGIN not allowed to be nested"); - goto fail_with_errmsg; - } - - if (get_token(proto) <= -1) goto fail_with_errmsg; - if (proto->tok.type != HCL_SERVER_PROTO_TOKEN_NL) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "No new line after .BEGIN"); - goto fail_with_errmsg; - } - - proto->req.state = HCL_SERVER_PROTO_REQ_IN_BLOCK_LEVEL; - hcl_reset (proto->hcl); - break; - - case HCL_SERVER_PROTO_TOKEN_END: - if (proto->req.state != HCL_SERVER_PROTO_REQ_IN_BLOCK_LEVEL) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, ".END without opening .BEGIN"); - goto fail_with_errmsg; - } - - if (get_token(proto) <= -1) goto fail_with_errmsg; - if (proto->tok.type != HCL_SERVER_PROTO_TOKEN_NL) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "No new line after .END"); - goto fail_with_errmsg; - } - - if (hcl_endfeed(proto->hcl) <= -1) goto fail_with_errmsg; - - proto->worker->opstate = HCL_SERVER_WORKER_OPSTATE_EXECUTE; - /* i must not jump to fail_with_errmsg when execute_script() fails. - * it may have produced some normal output already. so the function - * is supposed to handle an error in itself */ - if (execute_script(proto, ".END") <= -1) return -1; - proto->req.state = HCL_SERVER_PROTO_REQ_IN_TOP_LEVEL; - break; - - case HCL_SERVER_PROTO_TOKEN_SCRIPT: - { - hcl_ooci_t c; - hcl_ooch_t ch; - hcl_oow_t feed_count = 0; - - if (proto->req.state == HCL_SERVER_PROTO_REQ_IN_TOP_LEVEL) - { - hcl_setbasesrloc (proto->hcl, 1, 1); - hcl_reset(proto->hcl); - } - - /* check the first character after .SCRIPT */ - GET_CHAR_TO_WITH_GOTO (proto, c, fail_with_errmsg); - if (c == HCL_OOCI_EOF) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "No contents on the .SCRIPT line"); - goto fail_with_errmsg; - } - - if (c == '\n') goto script_eol; - - if (!is_spacechar(c)) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "No space after .SCRIPT"); - goto fail_with_errmsg; - } - - while (1) - { - GET_CHAR_TO_WITH_GOTO (proto, c, fail_with_errmsg); - if (c == '\n' || c == HCL_OOCI_EOF) - { - if (c == '\n') - { - script_eol: - ch = c; - if (hcl_feed(proto->hcl, &ch, 1) <= -1) goto fail_with_errmsg; - feed_count++; - } - break; - } - - ch = c; - if (hcl_feed(proto->hcl, &ch, 1) <= -1) goto fail_with_errmsg; - feed_count++; - } - - /* - if (feed_count == 0) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "No contents on the .SCRIPT line"); - goto fail_with_errmsg; - }*/ - - if (proto->req.state == HCL_SERVER_PROTO_REQ_IN_TOP_LEVEL) - { - if (hcl_endfeed(proto->hcl) <= -1) goto fail_with_errmsg; - proto->worker->opstate = HCL_SERVER_WORKER_OPSTATE_EXECUTE; - if (execute_script(proto, ".SCRIPT") <= -1) return -1; - } - break; - } - - case HCL_SERVER_PROTO_TOKEN_SHOW_WORKERS: - if (proto->req.state != HCL_SERVER_PROTO_REQ_IN_TOP_LEVEL) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, ".SHOW-WORKERS not allowed to be nested"); - goto fail_with_errmsg; - } - - if (get_token(proto) <= -1) goto fail_with_errmsg; - if (proto->tok.type != HCL_SERVER_PROTO_TOKEN_NL) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "No new line after .SHOW-WORKERS"); - goto fail_with_errmsg; - } - - hcl_server_proto_start_reply (proto); - proto->worker->opstate = HCL_SERVER_WORKER_OPSTATE_EXECUTE; - show_server_workers (proto); - if (hcl_server_proto_end_reply(proto, HCL_NULL) <= -1) - { - HCL_LOG0 (proto->hcl, SERVER_LOGMASK_ERROR, "Unable to finalize reply for .SHOW-WORKERS\n"); - return -1; - } - - break; - - case HCL_SERVER_PROTO_TOKEN_KILL_WORKER: - { - int n; - hcl_oow_t wid, wp; - - static hcl_ooch_t failmsg[] = { 'U','n','a','b','l','e',' ','t','o',' ','k','i','l','l','\0' }; - - if (proto->req.state != HCL_SERVER_PROTO_REQ_IN_TOP_LEVEL) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, ".KILL-WORKER not allowed to be nested"); - goto fail_with_errmsg; - } - - if (get_token(proto) <= -1) goto fail_with_errmsg; - if (proto->tok.type != HCL_SERVER_PROTO_TOKEN_NUMBER) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "No worker ID after .KILL-WORKER"); - goto fail_with_errmsg; - } - - for (wid = 0, wp = 0; wp < proto->tok.len; wp++) wid = wid * 10 + (proto->tok.ptr[wp] - '0'); - - if (get_token(proto) <= -1) goto fail_with_errmsg; - if (proto->tok.type != HCL_SERVER_PROTO_TOKEN_NL) - { - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "No new line after worker ID of .KILL-WORKER"); - goto fail_with_errmsg; - } - - hcl_server_proto_start_reply (proto); - proto->worker->opstate = HCL_SERVER_WORKER_OPSTATE_EXECUTE; - n = kill_server_worker(proto, wid); - if (hcl_server_proto_end_reply(proto, (n <= -1? failmsg: HCL_NULL)) <= -1) - { - HCL_LOG0 (proto->hcl, SERVER_LOGMASK_ERROR, "Unable to finalize reply for .KILL-WORKER\n"); - return -1; - } - break; - } - - default: - hcl_seterrbfmt (proto->hcl, HCL_EINVAL, "Unknown token %.*js", proto->tok.len, proto->tok.ptr); - goto fail_with_errmsg; - } - - return 1; - -fail_with_errmsg: - send_proto_hcl_error (proto); - HCL_LOG1 (proto->hcl, SERVER_LOGMASK_ERROR, "Unable to compile .SCRIPT contents - %js\n", hcl_geterrmsg(proto->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) -{ - 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 = 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; - - 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 close_worker_socket (hcl_server_worker_t* worker) -{ - if (worker->sck >= 0) - { - if (worker->proto) - { - HCL_LOG2 (worker->proto->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) -{ - close_worker_socket (worker); - - if (worker->proto) - { - HCL_LOG1 (worker->proto->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 void* worker_main (void* ctx) -{ - hcl_server_worker_t* worker = (hcl_server_worker_t*)ctx; - hcl_server_t* server = worker->server; - sigset_t set; - - sigfillset (&set); - pthread_sigmask (SIG_BLOCK, &set, HCL_NULL); - - worker->thr = pthread_self(); - worker->proto = hcl_server_proto_open(0, worker); - if (!worker->proto) - { - free_worker (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); - - while (!server->stopreq) - { - worker->opstate = HCL_SERVER_WORKER_OPSTATE_WAIT; - if (hcl_server_proto_handle_request(worker->proto) <= 0) - { - worker->opstate = HCL_SERVER_WORKER_OPSTATE_ERROR; - break; - } - } - - hcl_server_proto_close (worker->proto); - worker->proto = HCL_NULL; - - pthread_mutex_lock (&server->worker_mutex); - close_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); -} - -#endif diff --git a/lib/hcl-s.h b/lib/hcl-s.h deleted file mode 100644 index 6aa9fc2..0000000 --- a/lib/hcl-s.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - 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. - */ - -#if 0 -#ifndef _HCL_S_H_ -#define _HCL_S_H_ - -#include - -typedef struct hcl_server_proto_t hcl_server_proto_t; -typedef struct hcl_server_worker_t hcl_server_worker_t; -typedef struct hcl_server_t hcl_server_t; - -enum hcl_server_option_t -{ - HCL_SERVER_TRAIT, - HCL_SERVER_LOG_MASK, - HCL_SERVER_WORKER_MAX_COUNT, - HCL_SERVER_WORKER_STACK_SIZE, - HCL_SERVER_WORKER_IDLE_TIMEOUT, - HCL_SERVER_ACTOR_HEAP_SIZE, - HCL_SERVER_ACTOR_MAX_RUNTIME, - HCL_SERVER_SCRIPT_INCLUDE_PATH, - HCL_SERVER_MODULE_INCTX -}; -typedef enum hcl_server_option_t hcl_server_option_t; - -enum hcl_server_trait_t -{ -#if defined(HCL_BUILD_DEBUG) - HCL_SERVER_TRAIT_DEBUG_GC = (1 << 0), - HCL_SERVER_TRAIT_DEBUG_BIGINT = (1 << 1) -#endif -}; -typedef enum hcl_server_trait_t hcl_server_trait_t; - -#define HCL_SERVER_WID_INVALID ((hcl_oow_t)-1) -#define HCL_SERVER_WID_MAX (HCL_SERVER_WID_INVALID - 1) - -typedef void (*hcl_server_log_write_t) ( - hcl_server_t* server, - hcl_oow_t wid, - hcl_bitmask_t mask, - const hcl_ooch_t* msg, - hcl_oow_t len -); - -struct hcl_server_prim_t -{ - hcl_server_log_write_t log_write; -}; -typedef struct hcl_server_prim_t hcl_server_prim_t; - - -#if defined(__cplusplus) -extern "C" { -#endif - -HCL_EXPORT hcl_server_t* hcl_server_open ( - hcl_mmgr_t* mmgr, - hcl_oow_t xtnsize, - hcl_server_prim_t* prim, - hcl_errnum_t* errnum -); - -HCL_EXPORT void hcl_server_close ( - hcl_server_t* server -); - -HCL_EXPORT int hcl_server_start ( - hcl_server_t* server, - const hcl_bch_t* addrs -); - -HCL_EXPORT void hcl_server_stop ( - hcl_server_t* server -); - -HCL_EXPORT int hcl_server_setoption ( - hcl_server_t* server, - hcl_server_option_t id, - const void* value -); - -HCL_EXPORT int hcl_server_getoption ( - hcl_server_t* server, - hcl_server_option_t id, - void* value -); - -HCL_EXPORT void* hcl_server_getxtn ( - hcl_server_t* server -); - -HCL_EXPORT hcl_mmgr_t* hcl_server_getmmgr ( - hcl_server_t* server -); - - -HCL_EXPORT hcl_cmgr_t* hcl_server_getcmgr ( - hcl_server_t* server -); - -HCL_EXPORT void hcl_server_setcmgr ( - hcl_server_t* server, - hcl_cmgr_t* cmgr -); - -HCL_EXPORT hcl_errnum_t hcl_server_geterrnum ( - hcl_server_t* server -); - -HCL_EXPORT const hcl_ooch_t* hcl_server_geterrstr ( - hcl_server_t* server -); - -HCL_EXPORT const hcl_ooch_t* hcl_server_geterrmsg ( - hcl_server_t* server -); - -HCL_EXPORT void hcl_server_seterrnum ( - hcl_server_t* server, - hcl_errnum_t errnum -); - -HCL_EXPORT void hcl_server_seterrbfmt ( - hcl_server_t* server, - hcl_errnum_t errnum, - const hcl_bch_t* fmt, - ... -); - -HCL_EXPORT void hcl_server_seterrufmt ( - hcl_server_t* server, - hcl_errnum_t errnum, - const hcl_uch_t* fmt, - ... -); - -HCL_EXPORT void hcl_server_logbfmt ( - hcl_server_t* server, - hcl_bitmask_t mask, - const hcl_bch_t* fmt, - ... -); - -HCL_EXPORT void hcl_server_logufmt ( - hcl_server_t* server, - hcl_bitmask_t mask, - const hcl_uch_t* fmt, - ... -); - - -HCL_EXPORT void* hcl_server_allocmem ( - hcl_server_t* server, - hcl_oow_t size -); - -HCL_EXPORT void* hcl_server_callocmem ( - hcl_server_t* server, - hcl_oow_t size -); - -HCL_EXPORT void* hcl_server_reallocmem ( - hcl_server_t* server, - void* ptr, - hcl_oow_t size -); - - -HCL_EXPORT void hcl_server_freemem ( - hcl_server_t* server, - void* ptr -); - - -HCL_EXPORT int hcl_server_proto_feed_reply ( - hcl_server_proto_t* proto, - const hcl_ooch_t* ptr, - hcl_oow_t len, - int escape -); - -HCL_EXPORT int hcl_server_proto_feed_reply_bytes ( - hcl_server_proto_t* proto, - const hcl_bch_t* ptr, - hcl_oow_t len, - int escape -); - - -HCL_EXPORT int hcl_server_proto_handle_request ( - hcl_server_proto_t* proto -); - -#if defined(__cplusplus) -} -#endif - -#endif - - - -#endif diff --git a/lib/hcl-x.c b/lib/hcl-x.c deleted file mode 100644 index f646a94..0000000 --- a/lib/hcl-x.c +++ /dev/null @@ -1,2570 +0,0 @@ -/* - 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(proto->rcv.eof)) - { -// 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 (proto->rcv.len < proto->rcv.len_needed) 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); -} - - -/* ------------------------------------------------------------------------ */ - -#if 0 -hcl_client_proto_t* hcl_client_proto_open (hcl_mmgr_t* mmgr, hcl_oow_t xtnsize, hcl_errnum_t* errnum) -{ -} - -void hcl_client_proto_close (hcl_client_proto_t* proto) -{ -} - -int hcl_client_proto_send_code (hcl_client_proto_t* client) -{ -} - -int hcl_client_proto_handle_response (hcl_client_proto_t* proto) -{ -} - -#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/hcl-x.h b/lib/hcl-x.h index a1e58dc..f8eeb01 100644 --- a/lib/hcl-x.h +++ b/lib/hcl-x.h @@ -25,8 +25,8 @@ #ifndef _HCL_X_T_ #define _HCL_X_T_ -/*#include */ #include +#include enum hcl_xpkt_type_t { @@ -85,6 +85,11 @@ typedef struct hcl_xproto_cb_t hcl_xproto_cb_t; /* ---------------------------------------------------------------------- */ +/* forward declaration to skip including just for struct iovec */ +typedef struct iovec hcl_iovec_t; + +/* ---------------------------------------------------------------------- */ + typedef struct hcl_server_proto_t hcl_server_proto_t; typedef struct hcl_server_worker_t hcl_server_worker_t; typedef struct hcl_server_t hcl_server_t; @@ -426,33 +431,41 @@ HCL_EXPORT void* hcl_xproto_getxtn ( hcl_xproto_t* proto ); -hcl_uint8_t* hcl_xproto_getbuf ( +HCL_EXPORT hcl_uint8_t* hcl_xproto_getbuf ( hcl_xproto_t* proto, hcl_oow_t* capa ); -int hcl_xproto_geteof ( +HCL_EXPORT int hcl_xproto_geteof ( hcl_xproto_t* proto ); -void hcl_xproto_seteof ( +HCL_EXPORT void hcl_xproto_seteof ( hcl_xproto_t* proto, int v ); -void hcl_xproto_advbuf ( +HCL_EXPORT void hcl_xproto_advbuf ( hcl_xproto_t* proto, hcl_oow_t inc ); -int hcl_xproto_ready ( +HCL_EXPORT int hcl_xproto_ready ( hcl_xproto_t* proto ); -int hcl_xproto_process ( +HCL_EXPORT int hcl_xproto_process ( hcl_xproto_t* proto ); +/* ---------------------------------------------------------------------- */ + +HCL_EXPORT int hcl_sys_send_iov ( + int sck, + hcl_iovec_t* iov, /* note this is not read-only and can change */ + int count +); + #if defined(__cplusplus) } #endif diff --git a/lib/x-server.c b/lib/x-server.c index 9592e32..62e3c77 100644 --- a/lib/x-server.c +++ b/lib/x-server.c @@ -994,39 +994,6 @@ 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; @@ -1056,9 +1023,10 @@ printf ("SENDING BYTES [%.*s]\n", (int)len, data); iov[1].iov_base = ptr; iov[1].iov_len = seglen; - if (send_iov(worker->sck, iov, 2) <= -1) + if (hcl_sys_send_iov(worker->sck, iov, 2) <= -1) { /* TODO: error message */ + fprintf (stderr, "Unable to sendmsg on %d - %s\n", worker->sck, strerror(errno)); return -1; } @@ -2283,3 +2251,32 @@ void hcl_server_freemem (hcl_server_t* server, void* ptr) { HCL_MMGR_FREE (server->_mmgr, ptr); } + +/* ========================================================================= */ + +int hcl_sys_send_iov (int sck, hcl_iovec_t* 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) 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; +}