mirror of
https://github.com/eddyem/snippets_library.git
synced 2025-12-06 02:35:20 +03:00
Version 0.3.1: add sockets
This commit is contained in:
parent
54e88cfd92
commit
19c14db40b
@ -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}")
|
||||
|
||||
|
||||
13
config.c
13
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;
|
||||
}
|
||||
|
||||
|
||||
@ -16,31 +16,149 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
Binary file not shown.
@ -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 <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\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 ""
|
||||
|
||||
@ -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 <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\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"
|
||||
|
||||
|
||||
48
ringbuffer.c
48
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
|
||||
|
||||
582
socket.c
582
socket.c
@ -16,5 +16,587 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/un.h> // unix socket
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
121
usefull_macros.h
121
usefull_macros.h
@ -28,6 +28,8 @@
|
||||
#endif
|
||||
|
||||
#include <errno.h> // errno
|
||||
#include <netdb.h> // struct addrinfo
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h> // alloc, free
|
||||
#include <sys/types.h> // pid_t
|
||||
#include <unistd.h> // 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);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user