diff --git a/CMakeLists.txt b/CMakeLists.txt
index 31c9332..0c61f5a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.9)
set(PROJ usefull_macros)
set(MINOR_VERSION "1")
-set(MID_VERSION "2")
+set(MID_VERSION "3")
set(MAJOR_VERSION "0")
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
diff --git a/config.c b/config.c
index b0d13ea..74e2958 100644
--- a/config.c
+++ b/config.c
@@ -34,7 +34,7 @@
* also if `key` have several words only first saved, `value` can be long string with or without quotations
*/
int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN]){
- DBG("got pair: '%s'", pair);
+ //DBG("got pair: '%s'", pair);
if(!pair || !*pair) return 0; // empty line
char *kstart = sl_omitspaces(pair);
if(!*kstart || *kstart == SL_COMMENT_CHAR) return 0; // only spaces
@@ -44,13 +44,13 @@ int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN]
char *cmnt = strchr(kstart, SL_COMMENT_CHAR);
if(eq && cmnt && cmnt < eq) eq = NULL; // comment starting before equal sign
if(eq){ do{
- DBG("got equal symbol: '%s'", eq);
+ //DBG("got equal symbol: '%s'", eq);
char *vstart = sl_omitspaces(eq + 1);
if(!*vstart) break; // empty line or only spaces after `=`
char *vend = sl_omitspacesr(vstart);
size_t l = SL_VAL_LEN - 1; // truncate value to given length
if(l > (size_t)(vend - vstart)) l = vend - vstart;
- DBG("l=%zd", l);
+ //DBG("l=%zd", l);
strncpy(value, vstart, l);
value[l] = 0;
cmnt = strchr(value, SL_COMMENT_CHAR);
@@ -59,18 +59,19 @@ int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN]
*sl_omitspacesr(value) = 0;
}
if(!*value) break;
- DBG("Got value: '%s'", value);
+ //DBG("Got value: '%s'", value);
ret = 2;
}while(0);
- }else eq = kstart + strlen(kstart) - 1;
+ }else eq = kstart + strlen(kstart);
for(; kend < eq && !isspace(*kend); ++kend);
size_t l = SL_KEY_LEN - 1;
if(l > (size_t)(kend - kstart)) l = kend - kstart;
+ //DBG("kend=%c, kstart=%c, l=%zd", *kend, *kstart, l);
strncpy(key, kstart, l);
key[l] = 0;
cmnt = strchr(key, SL_COMMENT_CHAR);
if(cmnt) *cmnt = 0;
- DBG("Got key: '%s'", key);
+ //DBG("Got key: '%s'", key);
return ret;
}
diff --git a/examples/clientserver.c b/examples/clientserver.c
index cfc1941..f57defe 100644
--- a/examples/clientserver.c
+++ b/examples/clientserver.c
@@ -16,31 +16,149 @@
* along with this program. If not, see .
*/
+#include
+#include
+#include
#include
+static sl_sock_t *s = NULL;
+
typedef struct{
int help;
int verbose;
+ int isserver;
+ int isunix;
+ int maxclients;
char *logfile;
+ char *node;
} parameters;
-static parameters G = {0};
+static parameters G = {
+ .maxclients = 2,
+};
static sl_option_t cmdlnopts[] = {
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose), "verbose level (each -v adds 1)"},
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), "log file name"},
+ {"node", NEED_ARG, NULL, 'n', arg_string, APTR(&G.node), "node \"IP\", \"name:IP\" or path (could be \"\\0path\" for anonymous UNIX-socket)"},
+ {"server", NO_ARGS, NULL, 's', arg_int, APTR(&G.isserver), "create server"},
+ {"unixsock", NO_ARGS, NULL, 'u', arg_int, APTR(&G.isunix), "UNIX socket instead of INET"},
+ {"maxclients", NEED_ARG, NULL, 'm', arg_int, APTR(&G.maxclients),"max amount of clients connected to server (default: 2)"},
end_option
};
+void signals(int sig){
+ if(sig){
+ signal(sig, SIG_IGN);
+ DBG("Get signal %d, quit.\n", sig);
+ }
+ LOGERR("Exit with status %d", sig);
+ sl_restore_con();
+ if(s) sl_sock_delete(&s);
+ exit(sig);
+}
+
+static void runclient(sl_sock_t *s){
+ char buf[300];
+ if(!s) return;
+ do{
+ printf("send > ");
+ char *r = fgets(buf, 300, stdin);
+ if(r){
+ DBG("try");
+ if(-1 == sl_sock_sendstrmessage(s, buf)){
+ WARNX("Error send");
+ return;
+ }
+ DBG("OK");
+ }else break;
+ ssize_t got = 0;
+ double t0 = sl_dtime();
+ do{
+ got = sl_sock_readline(s, buf, 299);
+ if(got > 0) printf("server > %s\n", buf);
+ }while(s && s->connected && (got > 0 || sl_dtime() - t0 < 0.3));
+ if(got < 0) break;
+ } while(s && s->connected);
+ WARNX("Ctrl+D or disconnected");
+}
+
+// flags for standard handlers
+static sl_sock_int_t iflag = {0};
+static sl_sock_double_t dflag = {0};
+
+static sl_sock_hresult_e dtimeh(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
+ char buf[32];
+ snprintf(buf, 31, "UNIXT=%.2f\n", sl_dtime());
+ sl_sock_sendstrmessage(client, buf);
+ return RESULT_SILENCE;
+}
+
+static sl_sock_hresult_e show(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
+ if(!G.isunix){
+ if(*client->IP){
+ printf("Client \"%s\" (fd=%d) ask for flags:\n", client->IP, client->fd);
+ }else printf("Can't get client's IP, flags:\n");
+ }else printf("Socket fd=%d asks for flags:\n", client->fd);
+ printf("\tiflag=%" PRId64 ", dflag=%g\n", iflag.val, dflag.val);
+ return RESULT_OK;
+}
+
+static void toomuch(int fd){
+ const char *m = "Try later: too much clients connected\n";
+ send(fd, m, sizeof(m+1), MSG_NOSIGNAL);
+ LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
+}
+
+static sl_sock_hitem_t handlers[] = {
+ {sl_sock_inthandler, "int", "set/get integer flag", (void*)&iflag},
+ {sl_sock_dblhandler, "dbl", "set/get double flag", (void*)&dflag},
+ {show, "show", "show current flags @ server console", NULL},
+ {dtimeh, "dtime", "get server's UNIX time", NULL},
+ {NULL, NULL, NULL, NULL}
+};
+
int main(int argc, char **argv){
sl_init();
sl_parseargs(&argc, &argv, cmdlnopts);
if(G.help) sl_showhelp(-1, cmdlnopts);
+ if(!G.node) ERRX("Point node");
+ sl_socktype_e type = (G.isunix) ? SOCKT_UNIX : SOCKT_NET;
+ if(G.isserver){
+ sl_sock_changemaxclients(G.maxclients);
+ sl_sock_maxclhandler(toomuch);
+ s = sl_sock_run_server(type, G.node, -1, handlers);
+ } else {
+ s = sl_sock_run_client(type, G.node, -1);
+ }
+ if(!s) ERRX("Can't create socket and/or run threads");
sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR;
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
if(G.logfile) OPENLOG(G.logfile, lvl, 1);
- LOGMSG("hello");
- LOGERRADD("additional to err");
+ LOGMSG("Started");
+ signal(SIGTERM, signals);
+ signal(SIGINT, signals);
+ signal(SIGQUIT, signals);
+ signal(SIGTSTP, SIG_IGN);
+ signal(SIGHUP, signals);
+ if(G.isserver){
+ //double t0 = sl_dtime();
+ while(s && s->connected){
+ if(!s->rthread){
+ WARNX("Server handlers thread is dead");
+ LOGERR("Server handlers thread is dead");
+ break;
+ }
+ /*double tn = sl_dtime();
+ if(tn - t0 > 10.){
+ sl_sock_sendall((uint8_t*)"PING\n", 5);
+ t0 = tn;
+ }*/
+ }
+ }else runclient(s);
+ LOGMSG("Ended");
+ DBG("Close");
+ sl_sock_delete(&s);
return 0;
}
diff --git a/examples/ringbuffer.c b/examples/ringbuffer.c
index d7c8900..6b615fd 100644
--- a/examples/ringbuffer.c
+++ b/examples/ringbuffer.c
@@ -43,7 +43,7 @@ int main(int argc, char **argv){
sl_init();
sl_parseargs(&argc, &argv, cmdlnopts);
if(G.help) sl_showhelp(-1, cmdlnopts);
- sl_ringbuffer *b = sl_RB_new(G.size);
+ sl_ringbuffer_t *b = sl_RB_new(G.size);
if(!b) return 1;
printf("Created ring buffer of %d bytes\n", G.size);
printf("Enter lines of text to fill it or type (get) to get one line from buffer\n");
diff --git a/locale/ru/LC_MESSAGES/usefull_macros.mo b/locale/ru/LC_MESSAGES/usefull_macros.mo
index 00e50e2..83e41d1 100644
Binary files a/locale/ru/LC_MESSAGES/usefull_macros.mo and b/locale/ru/LC_MESSAGES/usefull_macros.mo differ
diff --git a/locale/ru/messages.po b/locale/ru/messages.po
index c8f1267..d9a2fac 100644
--- a/locale/ru/messages.po
+++ b/locale/ru/messages.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-11-15 11:38+0300\n"
+"POT-Creation-Date: 2024-12-10 17:44+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -17,31 +17,31 @@ msgstr ""
"Content-Type: text/plain; charset=koi8-r\n"
"Content-Transfer-Encoding: 8bit\n"
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:225
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:226
msgid "Unsupported option type"
msgstr ""
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:228
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:229
#, c-format
msgid "Wrong number format '%s'"
msgstr ""
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:272
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:273
#, c-format
msgid "Can't open %s"
msgstr ""
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:283
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:284
#, c-format
msgid "Wrong key: '%s'"
msgstr ""
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:289
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:290
#, c-format
msgid "Key '%s' need value"
msgstr ""
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:295
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:296
#, c-format
msgid "Key '%s' have no argptr!"
msgstr ""
@@ -105,6 +105,30 @@ msgstr ""
msgid "Wrong argument \"%s\" of parameter \"%s\""
msgstr ""
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:133
+msgid "Server disconnected"
+msgstr ""
+
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:208
+msgid "Can't start server handlers thread"
+msgstr ""
+
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:245
+msgid "Limit of connections reached"
+msgstr ""
+
+#. buffer overflow
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:319
+#, c-format
+msgid "Server thread: buffer overflow from \"%s\""
+msgstr ""
+
+#. never reached
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:381
+#, c-format
+msgid "Unsupported socket type %d"
+msgstr ""
+
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:87
#, c-format
msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: 1/2"
@@ -185,6 +209,11 @@ msgid "Wrong double number format '%s'"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:428
#, c-format
msgid "Wrong integer number format '%s'"
msgstr ""
+
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:432
+msgid "Number out of integer limits"
+msgstr ""
diff --git a/locale/ru/ru.po b/locale/ru/ru.po
index 34b2c96..07f3beb 100644
--- a/locale/ru/ru.po
+++ b/locale/ru/ru.po
@@ -7,7 +7,7 @@
msgid ""
msgstr "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
- "POT-Creation-Date: 2024-11-15 11:38+0300\n"
+ "POT-Creation-Date: 2024-12-10 17:44+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -49,7 +49,7 @@ msgstr "
msgid "Can't munmap"
msgstr "Не могу munmap"
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:272
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:273
#, c-format
msgid "Can't open %s"
msgstr "Не могу открыть %s"
@@ -85,6 +85,10 @@ msgstr "
msgid "Can't setup console"
msgstr "Не могу настроить консоль"
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:208
+msgid "Can't start server handlers thread"
+msgstr "Не могу запустить поток-обработчик сервера"
+
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:256
#, c-format
msgid "Can't stat %s"
@@ -103,16 +107,20 @@ msgstr "
msgid "Integer out of range"
msgstr "Целое вне допустимого диапазона"
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:295
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:296
#, c-format
msgid "Key '%s' have no argptr!"
msgstr "У ключа '%s' нет указателя на переменную!"
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:289
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:290
#, c-format
msgid "Key '%s' need value"
msgstr "Ключ '%s' требует значения"
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:245
+msgid "Limit of connections reached"
+msgstr "Достигнут предел соединений"
+
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:262
msgid "Mmap error for input"
msgstr "Ошибка mmap"
@@ -130,14 +138,34 @@ msgstr "
msgid "No filename given!"
msgstr "Не задано имя файла!"
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:432
+msgid "Number out of integer limits"
+msgstr "Число за пределами int"
+
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162
msgid "Port name is missing"
msgstr "Отсутствует имя порта"
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:225
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:133
+msgid "Server disconnected"
+msgstr "Сервер отключен"
+
+#. buffer overflow
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:319
+#, c-format
+msgid "Server thread: buffer overflow from \"%s\""
+msgstr "Поток сервера: переполнение буфера от \"%s\""
+
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:226
msgid "Unsupported option type"
msgstr "Неподдерживаемый тип опции"
+#. never reached
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:381
+#, c-format
+msgid "Unsupported socket type %d"
+msgstr "Неподдерживаемый тип сокета %d"
+
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:87
#, c-format
msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: "
@@ -161,16 +189,17 @@ msgid "Wrong helpstring!"
msgstr "Неправильный формат строки помощи"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:428
#, c-format
msgid "Wrong integer number format '%s'"
msgstr "Неправильный формат целого: %s"
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:283
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:284
#, c-format
msgid "Wrong key: '%s'"
msgstr "Неправильный ключ: %s"
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:228
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:229
#, c-format
msgid "Wrong number format '%s'"
msgstr "Неправильный формат числа: %s"
@@ -189,4 +218,3 @@ msgstr "
#, c-format
msgid "double short arguments: -%c"
msgstr "дублирующийся короткий параметр: -%c"
-
diff --git a/ringbuffer.c b/ringbuffer.c
index e3ad12d..9ba1d2f 100644
--- a/ringbuffer.c
+++ b/ringbuffer.c
@@ -25,8 +25,8 @@
* @param size - RB size
* @return RB
*/
-sl_ringbuffer *sl_RB_new(size_t size){
- sl_ringbuffer *b = MALLOC(sl_ringbuffer, 1);
+sl_ringbuffer_t *sl_RB_new(size_t size){
+ sl_ringbuffer_t *b = MALLOC(sl_ringbuffer_t, 1);
b->data = MALLOC(uint8_t, size);
pthread_mutex_init(&b->busy, NULL);
b->head = b->tail = 0;
@@ -38,13 +38,11 @@ sl_ringbuffer *sl_RB_new(size_t size){
* @brief sl_RB_delete - free ringbuffer
* @param b - buffer to free
*/
-void sl_RB_delete(sl_ringbuffer **b){
+void sl_RB_delete(sl_ringbuffer_t **b){
if(!b || !*b) return;
- sl_ringbuffer *bptr = *b;
- pthread_mutex_lock(&bptr->busy);
+ sl_ringbuffer_t *bptr = *b;
FREE(bptr->data);
*b = 0;
- pthread_mutex_unlock(&bptr->busy);
FREE(bptr);
}
@@ -53,26 +51,34 @@ void sl_RB_delete(sl_ringbuffer **b){
* @param b - buffer
* @return N
*/
-static size_t datalen(sl_ringbuffer *b){
+static size_t datalen(sl_ringbuffer_t *b){
if(b->tail >= b->head) return (b->tail - b->head);
else return (b->length - b->head + b->tail);
}
// datalen but with blocking of RB
-size_t sl_RB_datalen(sl_ringbuffer *b){
+size_t sl_RB_datalen(sl_ringbuffer_t *b){
pthread_mutex_lock(&b->busy);
size_t l = datalen(b);
pthread_mutex_unlock(&b->busy);
return l;
}
+// size of free space in buffer
+size_t sl_RB_freesize(sl_ringbuffer_t *b){
+ pthread_mutex_lock(&b->busy);
+ size_t l = b->length - datalen(b);
+ pthread_mutex_unlock(&b->busy);
+ return l;
+}
+
/**
* @brief hasbyte - check if RB have given byte
* @param b - rb
* @param byte - what to find
* @return index of byte or -1 if not found or no data
*/
-static ssize_t hasbyte(sl_ringbuffer *b, uint8_t byte){
+static ssize_t hasbyte(sl_ringbuffer_t *b, uint8_t byte){
if(b->head == b->tail) return -1; // no data in buffer
size_t startidx = b->head;
if(b->head > b->tail){
@@ -86,19 +92,19 @@ static ssize_t hasbyte(sl_ringbuffer *b, uint8_t byte){
}
// hasbyte with block
-ssize_t sl_RB_hasbyte(sl_ringbuffer *b, uint8_t byte){
+ssize_t sl_RB_hasbyte(sl_ringbuffer_t *b, uint8_t byte){
pthread_mutex_lock(&b->busy);
size_t idx = hasbyte(b, byte);
pthread_mutex_unlock(&b->busy);
return idx;
}
// increment head or tail
-inline void incr(sl_ringbuffer *b, volatile size_t *what, size_t n){
+inline void incr(sl_ringbuffer_t *b, volatile size_t *what, size_t n){
*what += n;
if(*what >= b->length) *what -= b->length;
}
-static size_t rbread(sl_ringbuffer *b, uint8_t *s, size_t len){
+static size_t rbread(sl_ringbuffer_t *b, uint8_t *s, size_t len){
size_t l = datalen(b);
if(!l) return 0;
if(l > len) l = len;
@@ -122,7 +128,7 @@ static size_t rbread(sl_ringbuffer *b, uint8_t *s, size_t len){
* @param len - length of `s`
* @return amount of bytes read
*/
-size_t sl_RB_read(sl_ringbuffer *b, uint8_t *s, size_t len){
+size_t sl_RB_read(sl_ringbuffer_t *b, uint8_t *s, size_t len){
pthread_mutex_lock(&b->busy);
size_t got = rbread(b, s, len);
pthread_mutex_unlock(&b->busy);
@@ -137,7 +143,7 @@ size_t sl_RB_read(sl_ringbuffer *b, uint8_t *s, size_t len){
* @param len - length of `s`
* @return amount of bytes read or -1 if `s` have insufficient size
*/
-ssize_t sl_RB_readto(sl_ringbuffer *b, uint8_t byte, uint8_t *s, size_t len){
+ssize_t sl_RB_readto(sl_ringbuffer_t *b, uint8_t byte, uint8_t *s, size_t len){
ssize_t got = 0;
pthread_mutex_lock(&b->busy);
ssize_t idx = hasbyte(b, byte);
@@ -160,7 +166,7 @@ ret:
* @return amount of characters read or -1 if buffer too small
* !!! this function changes '\n' to 0 in `s`; `len` should include trailing '\0' too
*/
-ssize_t sl_RB_readline(sl_ringbuffer *b, char *s, size_t len){
+ssize_t sl_RB_readline(sl_ringbuffer_t *b, char *s, size_t len){
ssize_t got = 0;
pthread_mutex_lock(&b->busy);
ssize_t idx = hasbyte(b, '\n');
@@ -184,7 +190,7 @@ ret:
* @param byte - data byte
* @return FALSE if there's no place for `byte` in `b`
*/
-int sl_RB_putbyte(sl_ringbuffer *b, uint8_t byte){
+int sl_RB_putbyte(sl_ringbuffer_t *b, uint8_t byte){
int rtn = FALSE;
pthread_mutex_lock(&b->busy);
size_t s = datalen(b);
@@ -204,11 +210,11 @@ ret:
* @param len - data length
* @return amount of bytes wrote (can be less than `len`)
*/
-size_t sl_RB_write(sl_ringbuffer *b, const uint8_t *str, size_t len){
+size_t sl_RB_write(sl_ringbuffer_t *b, const uint8_t *str, size_t len){
pthread_mutex_lock(&b->busy);
size_t r = b->length - 1 - datalen(b); // rest length
if(len > r) len = r;
- if(!len){ r = 0; goto ret; }
+ if(!len) goto ret;
size_t _1st = b->length - b->tail;
if(_1st > len) _1st = len;
memcpy(b->data + b->tail, str, _1st);
@@ -218,14 +224,14 @@ size_t sl_RB_write(sl_ringbuffer *b, const uint8_t *str, size_t len){
incr(b, &b->tail, len);
ret:
pthread_mutex_unlock(&b->busy);
- return r;
+ return len;
}
/**
* @brief sl_RB_clearbuf - reset buffer
* @param b - rb
*/
-void sl_RB_clearbuf(sl_ringbuffer *b){
+void sl_RB_clearbuf(sl_ringbuffer_t *b){
pthread_mutex_lock(&b->busy);
b->head = 0;
b->tail = 0;
@@ -238,7 +244,7 @@ void sl_RB_clearbuf(sl_ringbuffer *b){
* @param s - string
* @return amount of bytes written (strlen of s) or 0
*/
-size_t sl_RB_writestr(sl_ringbuffer *b, char *s){
+size_t sl_RB_writestr(sl_ringbuffer_t *b, char *s){
size_t len = strlen(s);
pthread_mutex_lock(&b->busy);
size_t r = b->length - 1 - datalen(b); // rest length
diff --git a/socket.c b/socket.c
index 97b6fab..0bdcaa9 100644
--- a/socket.c
+++ b/socket.c
@@ -16,5 +16,587 @@
* along with this program. If not, see .
*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include // unix socket
+#include
+#include
+
#include "usefull_macros.h"
+// max clients amount
+static int maxclients = 32;
+// too much clients handler; it is running for client connected with number>maxclients (before closing its fd)
+static sl_sock_maxclh_t toomuchclients = NULL;
+
+/**
+ * @brief sl_sock_changemaxclients - change amount of max simultaneously connected clients
+ * SHOULD BE run BEFORE running of server
+ * @param val - maximal clients number
+ */
+void sl_sock_changemaxclients(int val){
+ maxclients = val;
+}
+int sl_sock_getmaxclients(){ return maxclients; }
+
+// setter of "too much clients handler"
+void sl_sock_maxclhandler(sl_sock_maxclh_t h){
+ toomuchclients = h;
+}
+
+// text messages for `hresult`
+static const char *resmessages[RESULT_AMOUNT] = {
+ [RESULT_OK] = "OK\n",
+ [RESULT_FAIL] = "FAIL\n",
+ [RESULT_BADKEY] = "BADKEY\n",
+ [RESULT_BADVAL] = "BADVAL\n",
+ [RESULT_SILENCE] = "",
+};
+
+/**
+ * @brief sl_sock_hresult2str - get string message by hresult value
+ * @param r - handler result
+ * @return string with message
+ */
+const char *sl_sock_hresult2str(sl_sock_hresult_e r){
+ if(r >= RESULT_AMOUNT) return "BADRESULT";
+ return resmessages[r];
+}
+
+// convert UNIX socket name for unaddr; result should be free'd
+static char *convunsname(const char *path){
+ char *apath = MALLOC(char, 106);
+ if(*path == 0){
+ DBG("convert name starting from 0");
+ apath[0] = 0;
+ strncpy(apath+1, path+1, 104);
+ }else if(strncmp("\\0", path, 2) == 0){
+ DBG("convert name starting from \\0");
+ apath[0] = 0;
+ strncpy(apath+1, path+2, 104);
+ }else strncpy(apath, path, 105);
+ return apath;
+}
+
+/**
+ * @brief sl_sock_delete - close socket and delete descriptor
+ * @param sock - pointer to socket descriptor
+ */
+void sl_sock_delete(sl_sock_t **sock){
+ if(!sock || !*sock) return;
+ sl_sock_t *ptr = *sock;
+ ptr->connected = 0;
+ if(ptr->rthread){
+ DBG("Cancel thread");
+ pthread_cancel(ptr->rthread);
+ }
+ DBG("close fd=%d", ptr->fd);
+ if(ptr->fd > -1) close(ptr->fd);
+ DBG("delete ring buffer");
+ sl_RB_delete(&ptr->buffer);
+ DBG("free addrinfo");
+ if(ptr->addrinfo) freeaddrinfo(ptr->addrinfo);
+ DBG("free other");
+ FREE(ptr->node);
+ FREE(ptr->service);
+ FREE(ptr->data);
+ DBG("free sock");
+ FREE(*sock);
+}
+
+/**
+ * @brief clientrbthread - thread to fill client's ringbuffer with incoming data
+ * If s->handlers is not NULL, process all incoming data HERE, you shouldn't use ringbuffer by hands!
+ * @param d - socket descriptor
+ * @return NULL
+ */
+static void *clientrbthread(void *d){
+ char buf[512];
+ sl_sock_t *s = (sl_sock_t*) d;
+ DBG("Start client read buffer thread");
+ while(s && s->connected){
+ pthread_mutex_lock(&s->mutex);
+ if(1 != sl_canread(s->fd)){
+ pthread_mutex_unlock(&s->mutex);
+ usleep(1000);
+ continue;
+ }
+ ssize_t n = read(s->fd, buf, 511);
+ DBG("read %zd from %d, unlock", n, s->fd);
+ pthread_mutex_unlock(&s->mutex);
+ if(n < 1){
+ WARNX(_("Server disconnected"));
+ goto errex;
+ }
+ ssize_t got = 0;
+ do{
+ ssize_t written = sl_RB_write(s->buffer, (uint8_t*)buf + got, n-got);
+ //DBG("Put %zd to buffer, got=%zd, n=%zd", written, got, n);
+ if(got > n) return NULL;
+ if(written > 0) got += written;
+ }while(got != n);
+ //DBG("All messsages done");
+ }
+errex:
+ s->rthread = 0;
+ s->connected = FALSE;
+ return NULL;
+}
+
+// common for server thread and `sendall`
+static sl_sock_t **clients = NULL;
+
+/**
+ * @brief sl_sock_sendall - send data to all clients connected (works only for server)
+ * @param data - message
+ * @param len - its length
+ * @return N of sends or -1 if no server process running
+ */
+int sl_sock_sendall(uint8_t *data, size_t len){
+ if(!clients) return -1;
+ int nsent = 0;
+ for(int i = maxclients; i > 0; --i){
+ if(!clients[i]) continue;
+ if(clients[i]->fd < 0 || !clients[i]->connected) continue;
+ if((ssize_t)len == sl_sock_sendbinmessage(clients[i], data, len)) ++nsent;
+ }
+ return nsent;
+}
+
+// parser of client's message
+static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
+ char key[SL_KEY_LEN], val[SL_VAL_LEN], *valptr;
+ if(!str || !*str) return RESULT_BADKEY;
+ int N = sl_get_keyval(str, key, val);
+ DBG("getval=%d, key=%s, val=%s", N, key, val);
+ if(N == 0) return RESULT_BADKEY;
+ if(N == 1) valptr = NULL;
+ else valptr = val;
+ if(0 == strcmp(key, "help")){
+ sl_sock_sendstrmessage(client, "\nHelp:\n");
+ for(sl_sock_hitem_t *h = client->handlers; h->handler; ++h){
+ if(h->help){
+ sl_sock_sendstrmessage(client, h->key);
+ sl_sock_sendstrmessage(client, ": ");
+ sl_sock_sendstrmessage(client, h->help);
+ sl_sock_sendbyte(client, '\n');
+ }
+ }
+ sl_sock_sendbyte(client, '\n');
+ return RESULT_SILENCE;
+ }
+ for(sl_sock_hitem_t *h = client->handlers; h->handler; ++h){
+ if(strcmp(h->key, key)) continue;
+ return h->handler(client, h, valptr);
+ }
+ return RESULT_BADKEY;
+}
+
+/**
+ * @brief serverrbthread - thread for standard server procedure (when user give non-NULL `handlers`)
+ * @param d - socket descriptor
+ * @return NULL
+ */
+static void *serverthread(void _U_ *d){
+ sl_sock_t *s = (sl_sock_t*) d;
+ if(!s || !s->handlers){
+ WARNX(_("Can't start server handlers thread"));
+ goto errex;
+ }
+ int sockfd = s->fd;
+ if(listen(sockfd, maxclients) == -1){
+ WARN("listen");
+ goto errex;
+ }
+ DBG("Start server handlers thread");
+ int nfd = 1; // only one socket @start
+ struct pollfd *poll_set = MALLOC(struct pollfd, maxclients+1);
+ clients = MALLOC(sl_sock_t*, maxclients+1);
+ // init default clients records
+ for(int i = maxclients; i > 0; --i){
+ DBG("fill %dth client info", i);
+ clients[i] = MALLOC(sl_sock_t, 1);
+ sl_sock_t *c = clients[i];
+ c->type = s->type;
+ if(s->node) c->node = strdup(s->node);
+ if(s->service) c->service = strdup(s->service);
+ c->handlers = s->handlers;
+ // fill addrinfo
+ c->addrinfo = MALLOC(struct addrinfo, 1);
+ c->addrinfo->ai_addr = MALLOC(struct sockaddr, 1);
+ }
+ // ZERO - listening server socket
+ poll_set[0].fd = sockfd;
+ poll_set[0].events = POLLIN;
+ while(s && s->connected){
+ poll(poll_set, nfd, 1);
+ if(poll_set[0].revents & POLLIN){ // check main for accept()
+ struct sockaddr a;
+ socklen_t len = sizeof(struct sockaddr);
+ int client = accept(sockfd, &a, &len);
+ DBG("New connection, nfd=%d, len=%d", nfd, len);
+ LOGMSG("SERVER got connection, fd=%d", client);
+ if(nfd == maxclients + 1){
+ WARNX(_("Limit of connections reached"));
+ if(toomuchclients) toomuchclients(client);
+ close(client);
+ }else{
+ memset(&poll_set[nfd], 0, sizeof(struct pollfd));
+ poll_set[nfd].fd = client;
+ poll_set[nfd].events = POLLIN;
+ DBG("got client[%d], fd=%d", nfd, client);
+ sl_sock_t *c = clients[nfd];
+ c->fd = client;
+ DBG("memcpy");
+ memcpy(c->addrinfo->ai_addr, &a, len);
+ DBG("set conn flag");
+ c->connected = 1;
+ struct sockaddr_in* inaddr = (struct sockaddr_in*)&a;
+ if(!inet_ntop(AF_INET, &inaddr->sin_addr, c->IP, INET_ADDRSTRLEN)){
+ WARN("inet_ntop()");
+ *c->IP = 0;
+ }
+ DBG("got IP:%s", c->IP);
+ if(!c->buffer){ // allocate memory for client's ringbuffer
+ DBG("allocate ringbuffer");
+ c->buffer = sl_RB_new(s->buffer->length); // the same size as for master
+ }
+ ++nfd;
+ }
+ }
+#define SBUFSZ (SL_KEY_LEN+SL_VAL_LEN+2)
+ uint8_t buf[SBUFSZ];
+ // scan connections
+ for(int fdidx = 1; fdidx < nfd; ++fdidx){
+ if((poll_set[fdidx].revents & POLLIN) == 0) continue;
+ int fd = poll_set[fdidx].fd;
+ sl_sock_t *c = clients[fdidx];
+ pthread_mutex_lock(&c->mutex);
+ int nread = sl_RB_freesize(c->buffer);
+ if(nread > SBUFSZ) nread = SBUFSZ;
+ else if(nread < 1){
+ pthread_mutex_unlock(&c->mutex);
+ continue;
+ }
+ ssize_t got = read(fd, buf, nread);
+ pthread_mutex_unlock(&c->mutex);
+ if(got <= 0){ // client disconnected
+ DBG("client \"%s\" (fd=%d) disconnected", c->IP, fd);
+ pthread_mutex_lock(&c->mutex);
+ DBG("close fd %d", fd);
+ c->connected = 0;
+ close(fd);
+ sl_RB_clearbuf(c->buffer);
+ // now move all data of last client to disconnected
+ if(nfd > 2 && fdidx != nfd - 1){ // don't move the only or the last
+ DBG("lock fd=%d", clients[nfd-1]->fd);
+ pthread_mutex_lock(&clients[nfd-1]->mutex);
+ clients[fdidx] = clients[nfd-1];
+ clients[nfd-1] = c;
+ DBG("unlock fd=%d", clients[fdidx]->fd);
+ pthread_mutex_unlock(&clients[fdidx]->mutex); // now fdidx is nfd-1
+ poll_set[fdidx] = poll_set[nfd - 1];
+ }
+ DBG("unlock fd=%d", c->fd);
+ pthread_mutex_unlock(&c->mutex);
+ --nfd;
+ --fdidx;
+ }else{
+ sl_RB_write(c->buffer, buf, got);
+ }
+ }
+ // and now check all incoming buffers
+ for(int fdidx = 1; fdidx < nfd; ++fdidx){
+ sl_sock_t *c = clients[fdidx];
+ if(!c->connected) continue;
+ ssize_t got = sl_RB_readline(c->buffer, (char*)buf, SBUFSZ);
+ if(got < 0){ // buffer overflow
+ ERRX(_("Server thread: buffer overflow from \"%s\""), c->IP);
+ sl_RB_clearbuf(c->buffer);
+ continue;
+ }else if(got == 0) continue;
+ sl_sock_hresult_e r = msgparser(c, (char*)buf);
+ if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
+ }
+ }
+ // clear memory
+ FREE(poll_set);
+ for(int i = maxclients; i > 0; --i){
+ DBG("Clear %dth client data", i);
+ sl_sock_t *c = clients[i];
+ if(c->fd > -1) close(c->fd);
+ if(c->buffer) sl_RB_delete(&c->buffer);
+ FREE(c->addrinfo->ai_addr);
+ FREE(c->addrinfo);
+ FREE(c->node);
+ FREE(c->service);
+ FREE(c);
+ }
+ FREE(clients);
+errex:
+ s->rthread = 0;
+ return NULL;
+}
+
+/**
+ * @brief sl_sock_open - open socket (client or server)
+ * @param type - socket type
+ * @param path - path (for UNIX socket); for NET: ":port" for server, "server:port" for client
+ * @param handlers - standard handlers when read data (or NULL)
+ * @param bufsiz - input ring buffer size
+ * @param isserver - 1 for server, 0 for client
+ * @return socket descriptor or NULL if failed
+ * to create anonymous UNIX-socket you can start "path" from 0 or from string "\0"
+ */
+static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hitem_t *handlers, int bufsiz, int isserver){
+ if(!path || type >= SOCKT_AMOUNT) return NULL;
+ if(bufsiz < 256) bufsiz = 256;
+ int sock = -1;
+ struct addrinfo ai = {0}, *res = &ai;
+ struct sockaddr_un unaddr = {0};
+ char *str = NULL;
+ switch(type){
+ case SOCKT_UNIX:
+ str = convunsname(path);
+ if(!str) return NULL;
+ unaddr.sun_family = AF_UNIX;
+ ai.ai_addr = (struct sockaddr*) &unaddr;
+ ai.ai_addrlen = sizeof(unaddr);
+ memcpy(unaddr.sun_path, str, 106);
+ ai.ai_family = AF_UNIX;
+ ai.ai_socktype = SOCK_SEQPACKET;
+ break;
+ case SOCKT_NET:
+ case SOCKT_NETLOCAL:
+ //ai.ai_socktype = SOCK_DGRAM; // = SOCK_STREAM;
+ ai.ai_family = AF_INET;
+ if(isserver) ai.ai_flags = AI_PASSIVE;
+ break;
+ default: // never reached
+ WARNX(_("Unsupported socket type %d"), type);
+ return NULL;
+ break;
+ }
+ sl_sock_t *s = MALLOC(sl_sock_t, 1);
+ s->type = type;
+ s->fd = -1;
+ s->handlers = handlers;
+ s->buffer = sl_RB_new(bufsiz);
+ if(!s->buffer) sl_sock_delete(&s);
+ else{ // fill node/service
+ if(type == SOCKT_UNIX) s->node = str; // str now is converted path
+ else{
+ char *delim = strchr(path, ':');
+ if(!delim) s->service = strdup(path); // only port
+ else{
+ if(delim == path) s->service = strdup(path+1);
+ else{
+ size_t l = delim - path;
+ s->node = MALLOC(char, l + 1);
+ strncpy(s->node, path, l);
+ s->service = strdup(delim + 1);
+ }
+ }
+ DBG("socket->service=%s", s->service);
+ }
+ DBG("socket->node=%s", s->node);
+ }
+ // now try to open socket
+ if(type != SOCKT_UNIX){
+ DBG("try to get addrinfo for node '%s' and service '%s'", s->node, s->service);
+ char *node = s->node;
+ if(isserver){
+ if(s->type == SOCKT_NET) node = NULL; // common net server -> node==NULL
+ else node = "127.0.0.1"; // localhost
+ }
+ DBG("---> node '%s', service '%s'", node, s->service);
+ int e = getaddrinfo(node, s->service, &ai, &res);
+ if(e){
+ WARNX("getaddrinfo(): %s", gai_strerror(e));
+ sl_sock_delete(&s);
+ return NULL;
+ }
+ }
+ for(struct addrinfo *p = res; p; p = p->ai_next){
+ if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) continue;
+ DBG("Try proto %d, type %d", p->ai_protocol, p->ai_socktype);
+ if(isserver){
+ int reuseaddr = 1;
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){
+ WARN("setsockopt()");
+ close(sock); sock = -1;
+ continue;
+ }
+ if(bind(sock, p->ai_addr, p->ai_addrlen) == -1){
+ WARN("bind()");
+ close(sock); sock = -1;
+ continue;
+ }
+ int enable = 1;
+ if(ioctl(sock, FIONBIO, (void *)&enable) < 0){ // make socket nonblocking
+ WARN("Can't make socket non-blocked");
+ }
+ }else{
+ if(connect(sock, p->ai_addr, p->ai_addrlen) == -1){
+ WARN("connect()");
+ close(sock); sock = -1;
+ }
+ }
+ break;
+ }
+ if(sock < 0) sl_sock_delete(&s);
+ else{
+ s->fd = sock;
+ pthread_mutex_init(&s->mutex, NULL);
+ DBG("s->fd=%d, node=%s, service=%s", s->fd, s->node, s->service);
+ int r = -1;
+ if(isserver){
+ if(s->handlers)
+ r = pthread_create(&s->rthread, NULL, serverthread, (void*)s);
+ else r = 0;
+ }else{
+ r = pthread_create(&s->rthread, NULL, clientrbthread, (void*)s);
+ }
+ if(r){
+ WARN("pthread_create()");
+ sl_sock_delete(&s);
+ }else{
+ s->connected = TRUE;
+ DBG("fd=%d CONNECTED", s->fd);
+ }
+ }
+ return s;
+}
+
+/**
+ * @brief sl_sock_run_client - try to connect to server and, socket read thread and process optional handlers
+ * @param type - server type
+ * @param path - path or address:port
+ * @param handlers - array with handlers
+ * @param bufsiz - input ring buffer size
+ * @return socket descriptor or NULL if failed
+ */
+sl_sock_t *sl_sock_run_client(sl_socktype_e type, const char *path, int bufsiz){
+ sl_sock_t *s = sl_sock_open(type, path, NULL, bufsiz, 0);
+ return s;
+}
+
+/**
+ * @brief sl_sock_run_server - run built-in server parser
+ * @param type - server type
+ * @param path - path or port
+ * @param handlers - array with handlers
+ * @param bufsiz - input ring buffer size
+ * @return socket descriptor or NULL if failed
+ */
+sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path, int bufsiz, sl_sock_hitem_t *handlers){
+ sl_sock_t *s = sl_sock_open(type, path, handlers, bufsiz, 1);
+ return s;
+}
+
+/**
+ * @brief sl_sock_sendbinmessage - send binary data
+ * @param socket - socket
+ * @param msg - data
+ * @param l - data length
+ * @return amount of bytes sent or -1 in case of error
+ */
+ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l){
+ if(!msg || l < 1) return -1;
+ DBG("send to fd=%d message with len=%zd (%s)", socket->fd, l, msg);
+ while(socket && socket->connected && !sl_canwrite(socket->fd));
+ if(!socket || !socket->connected) return -1;
+ DBG("lock");
+ pthread_mutex_lock(&socket->mutex);
+ DBG("SEND");
+ ssize_t r = send(socket->fd, msg, l, MSG_NOSIGNAL);
+ DBG("unlock");
+ pthread_mutex_unlock(&socket->mutex);
+ return r;
+}
+
+ssize_t sl_sock_sendstrmessage(sl_sock_t *socket, const char *msg){
+ if(!msg) return -1;
+ size_t l = strlen(msg);
+ return sl_sock_sendbinmessage(socket, (const uint8_t*)msg, l);
+}
+
+/**
+ * @brief sl_sock_sendbyte - send one byte over socket
+ * @param socket - socket
+ * @param byte - byte to send
+ * @return -1 in case of error, 1 if all OK
+ */
+ssize_t sl_sock_sendbyte(sl_sock_t *socket, uint8_t byte){
+ while(socket && socket->connected && !sl_canwrite(socket->fd));
+ if(!socket || !socket->connected) return -1;
+ DBG("lock");
+ pthread_mutex_lock(&socket->mutex);
+ ssize_t r = send(socket->fd, &byte, 1, MSG_NOSIGNAL);
+ DBG("unlock");
+ pthread_mutex_unlock(&socket->mutex);
+ return r;
+}
+
+/**
+ * @brief sl_sock_readline - read string line from incoming ringbuffer
+ * @param sock - socket
+ * @param str (o) - buffer to copy
+ * @param len - length of str
+ * @return amount of bytes read
+ */
+ssize_t sl_sock_readline(sl_sock_t *sock, char *str, size_t len){
+ if(!sock || !sock->buffer || !str || !len) return -1;
+ return sl_RB_readline(sock->buffer, str, len);
+}
+
+// default handlers - setters/getters of int64, double and string
+sl_sock_hresult_e sl_sock_inthandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str){
+ char buf[128];
+ sl_sock_int_t *i = (sl_sock_int_t *)hitem->data;
+ if(!str){ // getter
+ snprintf(buf, 127, "%s=%" PRId64 "\n", hitem->key, i->val);
+ sl_sock_sendstrmessage(client, buf);
+ return RESULT_SILENCE;
+ }
+ long long x;
+ if(!sl_str2ll(&x, str)) return RESULT_BADVAL;
+ if(x < INT64_MIN || x > INT64_MAX) return RESULT_BADVAL;
+ i->val = (int64_t)x;
+ i->timestamp = sl_dtime();
+ return RESULT_OK;
+}
+sl_sock_hresult_e sl_sock_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str){
+ char buf[128];
+ sl_sock_double_t *d = (sl_sock_double_t *)hitem->data;
+ if(!str){ // getter
+ snprintf(buf, 127, "%s=%g\n", hitem->key, d->val);
+ sl_sock_sendstrmessage(client, buf);
+ return RESULT_SILENCE;
+ }
+ double dv;
+ if(!sl_str2d(&dv, str)) return RESULT_BADVAL;
+ d->val = dv;
+ d->timestamp = sl_dtime();
+ return RESULT_OK;
+}
+sl_sock_hresult_e sl_sock_strhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str){
+ char buf[SL_VAL_LEN + SL_KEY_LEN + 3];
+ sl_sock_string_t *s = (sl_sock_string_t*) hitem->data;
+ if(!str){ // getter
+ sprintf(buf, "%s=%s\n", hitem->key, s->val);
+ sl_sock_sendstrmessage(client, buf);
+ return RESULT_SILENCE;
+ }
+ int l = strlen(str);
+ if(l > SL_VAL_LEN - 1) return RESULT_BADVAL;
+ s->len = l;
+ s->timestamp = sl_dtime();
+ memcpy(s->val, str, l);
+ return RESULT_OK;
+}
diff --git a/usefull_macros.c b/usefull_macros.c
index 59691c4..9680c39 100644
--- a/usefull_macros.c
+++ b/usefull_macros.c
@@ -419,6 +419,81 @@ int sl_str2ll(long long *num, const char *str){
if(num) *num = res;
return TRUE;
}
+int sl_str2i(int *num, const char *str){
+ long long res;
+ char *endptr;
+ if(!str) return FALSE;
+ res = strtoll(str, &endptr, 0);
+ if(endptr == str || *str == '\0' || *endptr != '\0'){
+ WARNX(_("Wrong integer number format '%s'"));
+ return FALSE;
+ }
+ if(res > INT_MAX || res < INT_MIN){
+ WARNX(_("Number out of integer limits"));
+ return FALSE;
+ }
+ if(num) *num = (int)res;
+ return TRUE;
+}
+
+
+/**
+ * @brief sl_canread - run select() for given fd
+ * @param fd - file descriptor
+ * @return -1 if EINTR, 0 if none, 1 if ready to read
+ */
+int sl_canread(int fd){
+ if(fd < 0) return -1;
+ fd_set fds;
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 100;
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ do{
+ int rc = select(fd+1, &fds, NULL, NULL, &timeout);
+ if(rc < 0){
+ if(errno != EINTR){
+ WARN("select()");
+ return -1;
+ }
+ continue;
+ }
+ break;
+ }while(1);
+ if(FD_ISSET(fd, &fds)) return 1;
+ return 0;
+}
+
+/**
+ * @brief sl_canwrite - run select() for given fd
+ * @param fd - file descriptor
+ * @return -1 if EINTR, 0 if none, 1 if ready to write without blocking
+ */
+int sl_canwrite(int fd){
+ fd_set fds;
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 100;
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ //DBG("try ability of written to %d", fd);
+ do{
+ int rc = select(fd+1, NULL, &fds, NULL, &timeout);
+ if(rc < 0){
+ DBG("-1");
+ if(errno != EINTR){
+ LOGWARN("select()");
+ WARN("select()");
+ return -1;
+ }
+ continue;
+ }
+ break;
+ }while(1);
+ if(FD_ISSET(fd, &fds)) return 1;
+ return 0;
+}
/******************************************************************************\
* Logging to file
diff --git a/usefull_macros.h b/usefull_macros.h
index f92ddf9..3014ca1 100644
--- a/usefull_macros.h
+++ b/usefull_macros.h
@@ -28,6 +28,8 @@
#endif
#include // errno
+#include // struct addrinfo
+#include
#include // alloc, free
#include // pid_t
#include // pid_t
@@ -152,9 +154,13 @@ char *sl_omitspaces(const char *str);
// omit trailing spaces
char *sl_omitspacesr(const char *v);
-// convert string to double with checking
+// convert string to double/integer with checking
int sl_str2d(double *num, const char *str);
int sl_str2ll(long long *num, const char *str);
+int sl_str2i(int *num, const char *str);
+
+int sl_canread(int fd);
+int sl_canwrite(int fd);
/******************************************************************************\
The original term.h
@@ -389,53 +395,114 @@ typedef struct{
size_t head; // head index
size_t tail; // tail index
pthread_mutex_t busy; // mutex of buffer activity
-} sl_ringbuffer;
+} sl_ringbuffer_t;
-sl_ringbuffer *sl_RB_new(size_t size);
-void sl_RB_delete(sl_ringbuffer **b);
-size_t sl_RB_read(sl_ringbuffer *b, uint8_t *s, size_t len);
-ssize_t sl_RB_readto(sl_ringbuffer *b, uint8_t byte, uint8_t *s, size_t len);
-ssize_t sl_RB_hasbyte(sl_ringbuffer *b, uint8_t byte);
-int sl_RB_putbyte(sl_ringbuffer *b, uint8_t byte);
-size_t sl_RB_write(sl_ringbuffer *b, const uint8_t *str, size_t len);
-size_t sl_RB_datalen(sl_ringbuffer *b);
-void sl_RB_clearbuf(sl_ringbuffer *b);
-ssize_t sl_RB_readline(sl_ringbuffer *b, char *s, size_t len);
-size_t sl_RB_writestr(sl_ringbuffer *b, char *s);
+sl_ringbuffer_t *sl_RB_new(size_t size);
+void sl_RB_delete(sl_ringbuffer_t **b);
+size_t sl_RB_read(sl_ringbuffer_t *b, uint8_t *s, size_t len);
+ssize_t sl_RB_readto(sl_ringbuffer_t *b, uint8_t byte, uint8_t *s, size_t len);
+ssize_t sl_RB_hasbyte(sl_ringbuffer_t *b, uint8_t byte);
+int sl_RB_putbyte(sl_ringbuffer_t *b, uint8_t byte);
+size_t sl_RB_write(sl_ringbuffer_t *b, const uint8_t *str, size_t len);
+size_t sl_RB_datalen(sl_ringbuffer_t *b);
+size_t sl_RB_freesize(sl_ringbuffer_t *b);
+void sl_RB_clearbuf(sl_ringbuffer_t *b);
+ssize_t sl_RB_readline(sl_ringbuffer_t *b, char *s, size_t len);
+size_t sl_RB_writestr(sl_ringbuffer_t *b, char *s);
/******************************************************************************\
The original socket.h
\******************************************************************************/
// handler result: what to send to client
-
typedef enum{
RESULT_OK, // all OK
RESULT_FAIL, // failed running command
RESULT_BADVAL, // bad value
RESULT_BADKEY, // bad (non-existant) key
RESULT_SILENCE, // send nothing to client (in case of handlers which sends data by themself)
- RESULT_NUM // total amount of enum fields
-} sl_hresult;
+ RESULT_AMOUNT // total amount of enum fields
+} sl_sock_hresult_e;
-typedef sl_hresult (*sl_msghandler)(int fd, const char *key, const char *val);
+// data types with timestamp
+typedef struct{
+ double timestamp; // time of last change
+ double val;
+} sl_sock_double_t;
typedef struct{
- sl_msghandler handler;
- const char *key;
- const char *help;
-} sl_handleritem;
+ double timestamp; // time of last change
+ int64_t val;
+} sl_sock_int_t;
+typedef struct{
+ double timestamp; // time of last change
+ char val[SL_VAL_LEN];
+ int len; // strlen of `val`
+} sl_sock_string_t;
+
+struct sl_sock_hitem;
+struct sl_sock;
+
+// socket `key` handlers
+typedef sl_sock_hresult_e (*sl_sock_msghandler)(struct sl_sock *client, struct sl_sock_hitem *item, const char *val);
+// handler item
+typedef struct sl_sock_hitem{
+ sl_sock_msghandler handler; // function-handler
+ const char *key; // key name
+ const char *help; // key help
+ void *data; // user data (e.g. struct key-varptr-limits)
+} sl_sock_hitem_t;
+
+// socket type
typedef enum{
SOCKT_UNIX, // UNIX socket
SOCKT_NETLOCAL, // INET socket but only for localhost
- SOCKT_NET // true INET socket
-} sl_socktype;
+ SOCKT_NET, // true INET socket
+ SOCKT_AMOUNT // amount of types
+} sl_socktype_e;
-const char *sl_hresult2str(sl_hresult r);
-int sl_start_socket(int isserver, int isnet, const char *path);
-void sl_sendbinmessage(int fd, const uint8_t *msg, int l);
-void sl_sendstrmessage(int fd, const char *msg);
+// socket itself
+typedef struct sl_sock{
+ int fd; // file descriptor
+ int connected; // == TRUE if connected
+ sl_socktype_e type; // type
+ sl_ringbuffer_t *buffer; // input data buffer
+ char *node; // original UNIX-socket path or node name for INET (NULL - localhost client or any server)
+ char *service; // NULL for UNIX-socket and port for INET
+ struct addrinfo *addrinfo; // filled addrinfo structure
+ void *data; // user data
+ pthread_mutex_t mutex; // read/write mutex
+ pthread_t rthread; // reading ring buffer thread for client and main server thread for server
+ char IP[INET_ADDRSTRLEN]; // client's IP address
+ sl_sock_hitem_t *handlers; // if non-NULL, run handler's thread when opened
+} sl_sock_t;
+#if 0
+// connected client descriptor
+typedef struct sl_sock_client{
+ sl_sock_t *socket; // socket file descriptor
+ const char *IP; // IP address formatted string
+ void *res; // user data
+} sl_sock_client_t;
+#endif
+const char *sl_sock_hresult2str(sl_sock_hresult_e r);
+void sl_sock_delete(sl_sock_t **sock);
+sl_sock_t *sl_sock_run_client(sl_socktype_e type, const char *path, int bufsiz);
+sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path, int bufsiz, sl_sock_hitem_t *handlers);
+void sl_sock_changemaxclients(int val);
+int sl_sock_getmaxclients();
+typedef void (*sl_sock_maxclh_t)(int fd);
+void sl_sock_maxclhandler(sl_sock_maxclh_t h);
+
+ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l);
+ssize_t sl_sock_sendbyte(sl_sock_t *socket, uint8_t byte);
+ssize_t sl_sock_sendstrmessage(sl_sock_t *socket, const char *msg);
+ssize_t sl_sock_readline(sl_sock_t *sock, char *str, size_t len);
+int sl_sock_sendall(uint8_t *data, size_t len);
+
+sl_sock_hresult_e sl_sock_inthandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);
+sl_sock_hresult_e sl_sock_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);
+sl_sock_hresult_e sl_sock_strhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);