Version 0.3.1: add sockets

This commit is contained in:
Edward Emelianov 2024-12-10 17:55:56 +03:00
parent 54e88cfd92
commit 19c14db40b
11 changed files with 980 additions and 74 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.9) cmake_minimum_required(VERSION 3.9)
set(PROJ usefull_macros) set(PROJ usefull_macros)
set(MINOR_VERSION "1") set(MINOR_VERSION "1")
set(MID_VERSION "2") set(MID_VERSION "3")
set(MAJOR_VERSION "0") set(MAJOR_VERSION "0")
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")

View File

@ -34,7 +34,7 @@
* also if `key` have several words only first saved, `value` can be long string with or without quotations * 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]){ 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 if(!pair || !*pair) return 0; // empty line
char *kstart = sl_omitspaces(pair); char *kstart = sl_omitspaces(pair);
if(!*kstart || *kstart == SL_COMMENT_CHAR) return 0; // only spaces 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); char *cmnt = strchr(kstart, SL_COMMENT_CHAR);
if(eq && cmnt && cmnt < eq) eq = NULL; // comment starting before equal sign if(eq && cmnt && cmnt < eq) eq = NULL; // comment starting before equal sign
if(eq){ do{ if(eq){ do{
DBG("got equal symbol: '%s'", eq); //DBG("got equal symbol: '%s'", eq);
char *vstart = sl_omitspaces(eq + 1); char *vstart = sl_omitspaces(eq + 1);
if(!*vstart) break; // empty line or only spaces after `=` if(!*vstart) break; // empty line or only spaces after `=`
char *vend = sl_omitspacesr(vstart); char *vend = sl_omitspacesr(vstart);
size_t l = SL_VAL_LEN - 1; // truncate value to given length size_t l = SL_VAL_LEN - 1; // truncate value to given length
if(l > (size_t)(vend - vstart)) l = vend - vstart; if(l > (size_t)(vend - vstart)) l = vend - vstart;
DBG("l=%zd", l); //DBG("l=%zd", l);
strncpy(value, vstart, l); strncpy(value, vstart, l);
value[l] = 0; value[l] = 0;
cmnt = strchr(value, SL_COMMENT_CHAR); 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; *sl_omitspacesr(value) = 0;
} }
if(!*value) break; if(!*value) break;
DBG("Got value: '%s'", value); //DBG("Got value: '%s'", value);
ret = 2; ret = 2;
}while(0); }while(0);
}else eq = kstart + strlen(kstart) - 1; }else eq = kstart + strlen(kstart);
for(; kend < eq && !isspace(*kend); ++kend); for(; kend < eq && !isspace(*kend); ++kend);
size_t l = SL_KEY_LEN - 1; size_t l = SL_KEY_LEN - 1;
if(l > (size_t)(kend - kstart)) l = kend - kstart; if(l > (size_t)(kend - kstart)) l = kend - kstart;
//DBG("kend=%c, kstart=%c, l=%zd", *kend, *kstart, l);
strncpy(key, kstart, l); strncpy(key, kstart, l);
key[l] = 0; key[l] = 0;
cmnt = strchr(key, SL_COMMENT_CHAR); cmnt = strchr(key, SL_COMMENT_CHAR);
if(cmnt) *cmnt = 0; if(cmnt) *cmnt = 0;
DBG("Got key: '%s'", key); //DBG("Got key: '%s'", key);
return ret; return ret;
} }

View File

@ -16,31 +16,149 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * 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> #include <usefull_macros.h>
static sl_sock_t *s = NULL;
typedef struct{ typedef struct{
int help; int help;
int verbose; int verbose;
int isserver;
int isunix;
int maxclients;
char *logfile; char *logfile;
char *node;
} parameters; } parameters;
static parameters G = {0}; static parameters G = {
.maxclients = 2,
};
static sl_option_t cmdlnopts[] = { static sl_option_t cmdlnopts[] = {
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"}, {"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)"}, {"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"}, {"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 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){ int main(int argc, char **argv){
sl_init(); sl_init();
sl_parseargs(&argc, &argv, cmdlnopts); sl_parseargs(&argc, &argv, cmdlnopts);
if(G.help) sl_showhelp(-1, 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; sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR;
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1; if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
if(G.logfile) OPENLOG(G.logfile, lvl, 1); if(G.logfile) OPENLOG(G.logfile, lvl, 1);
LOGMSG("hello"); LOGMSG("Started");
LOGERRADD("additional to err"); 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; return 0;
} }

View File

@ -43,7 +43,7 @@ int main(int argc, char **argv){
sl_init(); sl_init();
sl_parseargs(&argc, &argv, cmdlnopts); sl_parseargs(&argc, &argv, cmdlnopts);
if(G.help) sl_showhelp(-1, 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; if(!b) return 1;
printf("Created ring buffer of %d bytes\n", G.size); 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"); printf("Enter lines of text to fill it or type (get) to get one line from buffer\n");

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,31 +17,31 @@ msgstr ""
"Content-Type: text/plain; charset=koi8-r\n" "Content-Type: text/plain; charset=koi8-r\n"
"Content-Transfer-Encoding: 8bit\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" msgid "Unsupported option type"
msgstr "" 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 #, c-format
msgid "Wrong number format '%s'" msgid "Wrong number format '%s'"
msgstr "" 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 #, c-format
msgid "Can't open %s" msgid "Can't open %s"
msgstr "" 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 #, c-format
msgid "Wrong key: '%s'" msgid "Wrong key: '%s'"
msgstr "" 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 #, c-format
msgid "Key '%s' need value" msgid "Key '%s' need value"
msgstr "" 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 #, c-format
msgid "Key '%s' have no argptr!" msgid "Key '%s' have no argptr!"
msgstr "" msgstr ""
@ -105,6 +105,30 @@ msgstr ""
msgid "Wrong argument \"%s\" of parameter \"%s\"" msgid "Wrong argument \"%s\" of parameter \"%s\""
msgstr "" 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 #: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:87
#, c-format #, c-format
msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: 1/2" 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 "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416 #: /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 #, c-format
msgid "Wrong integer number format '%s'" msgid "Wrong integer number format '%s'"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:432
msgid "Number out of integer limits"
msgstr ""

View File

@ -7,7 +7,7 @@
msgid "" msgid ""
msgstr "Project-Id-Version: PACKAGE VERSION\n" msgstr "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -49,7 +49,7 @@ msgstr "
msgid "Can't munmap" msgid "Can't munmap"
msgstr "îÅ ÍÏÇÕ 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 #, c-format
msgid "Can't open %s" msgid "Can't open %s"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s" msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s"
@ -85,6 +85,10 @@ msgstr "
msgid "Can't setup console" msgid "Can't setup console"
msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ" 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 #: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:256
#, c-format #, c-format
msgid "Can't stat %s" msgid "Can't stat %s"
@ -103,16 +107,20 @@ msgstr "
msgid "Integer out of range" msgid "Integer out of range"
msgstr "ãÅÌÏÅ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ" 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 #, c-format
msgid "Key '%s' have no argptr!" msgid "Key '%s' have no argptr!"
msgstr "õ ËÌÀÞÁ '%s' ÎÅÔ ÕËÁÚÁÔÅÌÑ ÎÁ ÐÅÒÅÍÅÎÎÕÀ!" 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 #, c-format
msgid "Key '%s' need value" msgid "Key '%s' need value"
msgstr "ëÌÀÞ '%s' ÔÒÅÂÕÅÔ ÚÎÁÞÅÎÉÑ" 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 #: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:262
msgid "Mmap error for input" msgid "Mmap error for input"
msgstr "ïÛÉÂËÁ mmap" msgstr "ïÛÉÂËÁ mmap"
@ -130,14 +138,34 @@ msgstr "
msgid "No filename given!" msgid "No filename given!"
msgstr "îÅ ÚÁÄÁÎÏ ÉÍÑ ÆÁÊÌÁ!" 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 #: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162
msgid "Port name is missing" msgid "Port name is missing"
msgstr "ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ" 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" msgid "Unsupported option type"
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÏÐÃÉÉ" 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 #: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:87
#, c-format #, c-format
msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: " 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 "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÓÔÒÏËÉ ÐÏÍÏÝÉ" msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÓÔÒÏËÉ ÐÏÍÏÝÉ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416 #: /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 #, c-format
msgid "Wrong integer number format '%s'" msgid "Wrong integer number format '%s'"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÃÅÌÏÇÏ: %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 #, c-format
msgid "Wrong key: '%s'" msgid "Wrong key: '%s'"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ËÌÀÞ: %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 #, c-format
msgid "Wrong number format '%s'" msgid "Wrong number format '%s'"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÞÉÓÌÁ: %s" msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÞÉÓÌÁ: %s"
@ -189,4 +218,3 @@ msgstr "
#, c-format #, c-format
msgid "double short arguments: -%c" msgid "double short arguments: -%c"
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ËÏÒÏÔËÉÊ ÐÁÒÁÍÅÔÒ: -%c" msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ËÏÒÏÔËÉÊ ÐÁÒÁÍÅÔÒ: -%c"

View File

@ -25,8 +25,8 @@
* @param size - RB size * @param size - RB size
* @return RB * @return RB
*/ */
sl_ringbuffer *sl_RB_new(size_t size){ sl_ringbuffer_t *sl_RB_new(size_t size){
sl_ringbuffer *b = MALLOC(sl_ringbuffer, 1); sl_ringbuffer_t *b = MALLOC(sl_ringbuffer_t, 1);
b->data = MALLOC(uint8_t, size); b->data = MALLOC(uint8_t, size);
pthread_mutex_init(&b->busy, NULL); pthread_mutex_init(&b->busy, NULL);
b->head = b->tail = 0; b->head = b->tail = 0;
@ -38,13 +38,11 @@ sl_ringbuffer *sl_RB_new(size_t size){
* @brief sl_RB_delete - free ringbuffer * @brief sl_RB_delete - free ringbuffer
* @param b - buffer to free * @param b - buffer to free
*/ */
void sl_RB_delete(sl_ringbuffer **b){ void sl_RB_delete(sl_ringbuffer_t **b){
if(!b || !*b) return; if(!b || !*b) return;
sl_ringbuffer *bptr = *b; sl_ringbuffer_t *bptr = *b;
pthread_mutex_lock(&bptr->busy);
FREE(bptr->data); FREE(bptr->data);
*b = 0; *b = 0;
pthread_mutex_unlock(&bptr->busy);
FREE(bptr); FREE(bptr);
} }
@ -53,26 +51,34 @@ void sl_RB_delete(sl_ringbuffer **b){
* @param b - buffer * @param b - buffer
* @return N * @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); if(b->tail >= b->head) return (b->tail - b->head);
else return (b->length - b->head + b->tail); else return (b->length - b->head + b->tail);
} }
// datalen but with blocking of RB // 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); pthread_mutex_lock(&b->busy);
size_t l = datalen(b); size_t l = datalen(b);
pthread_mutex_unlock(&b->busy); pthread_mutex_unlock(&b->busy);
return l; 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 * @brief hasbyte - check if RB have given byte
* @param b - rb * @param b - rb
* @param byte - what to find * @param byte - what to find
* @return index of byte or -1 if not found or no data * @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 if(b->head == b->tail) return -1; // no data in buffer
size_t startidx = b->head; size_t startidx = b->head;
if(b->head > b->tail){ if(b->head > b->tail){
@ -86,19 +92,19 @@ static ssize_t hasbyte(sl_ringbuffer *b, uint8_t byte){
} }
// hasbyte with block // 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); pthread_mutex_lock(&b->busy);
size_t idx = hasbyte(b, byte); size_t idx = hasbyte(b, byte);
pthread_mutex_unlock(&b->busy); pthread_mutex_unlock(&b->busy);
return idx; return idx;
} }
// increment head or tail // 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; *what += n;
if(*what >= b->length) *what -= b->length; 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); size_t l = datalen(b);
if(!l) return 0; if(!l) return 0;
if(l > len) l = len; 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` * @param len - length of `s`
* @return amount of bytes read * @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); pthread_mutex_lock(&b->busy);
size_t got = rbread(b, s, len); size_t got = rbread(b, s, len);
pthread_mutex_unlock(&b->busy); 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` * @param len - length of `s`
* @return amount of bytes read or -1 if `s` have insufficient size * @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; ssize_t got = 0;
pthread_mutex_lock(&b->busy); pthread_mutex_lock(&b->busy);
ssize_t idx = hasbyte(b, byte); ssize_t idx = hasbyte(b, byte);
@ -160,7 +166,7 @@ ret:
* @return amount of characters read or -1 if buffer too small * @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 * !!! 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; ssize_t got = 0;
pthread_mutex_lock(&b->busy); pthread_mutex_lock(&b->busy);
ssize_t idx = hasbyte(b, '\n'); ssize_t idx = hasbyte(b, '\n');
@ -184,7 +190,7 @@ ret:
* @param byte - data byte * @param byte - data byte
* @return FALSE if there's no place for `byte` in `b` * @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; int rtn = FALSE;
pthread_mutex_lock(&b->busy); pthread_mutex_lock(&b->busy);
size_t s = datalen(b); size_t s = datalen(b);
@ -204,11 +210,11 @@ ret:
* @param len - data length * @param len - data length
* @return amount of bytes wrote (can be less than `len`) * @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); pthread_mutex_lock(&b->busy);
size_t r = b->length - 1 - datalen(b); // rest length size_t r = b->length - 1 - datalen(b); // rest length
if(len > r) len = r; if(len > r) len = r;
if(!len){ r = 0; goto ret; } if(!len) goto ret;
size_t _1st = b->length - b->tail; size_t _1st = b->length - b->tail;
if(_1st > len) _1st = len; if(_1st > len) _1st = len;
memcpy(b->data + b->tail, str, _1st); 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); incr(b, &b->tail, len);
ret: ret:
pthread_mutex_unlock(&b->busy); pthread_mutex_unlock(&b->busy);
return r; return len;
} }
/** /**
* @brief sl_RB_clearbuf - reset buffer * @brief sl_RB_clearbuf - reset buffer
* @param b - rb * @param b - rb
*/ */
void sl_RB_clearbuf(sl_ringbuffer *b){ void sl_RB_clearbuf(sl_ringbuffer_t *b){
pthread_mutex_lock(&b->busy); pthread_mutex_lock(&b->busy);
b->head = 0; b->head = 0;
b->tail = 0; b->tail = 0;
@ -238,7 +244,7 @@ void sl_RB_clearbuf(sl_ringbuffer *b){
* @param s - string * @param s - string
* @return amount of bytes written (strlen of s) or 0 * @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); size_t len = strlen(s);
pthread_mutex_lock(&b->busy); pthread_mutex_lock(&b->busy);
size_t r = b->length - 1 - datalen(b); // rest length size_t r = b->length - 1 - datalen(b); // rest length

582
socket.c
View File

@ -16,5 +16,587 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * 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" #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;
}

View File

@ -419,6 +419,81 @@ int sl_str2ll(long long *num, const char *str){
if(num) *num = res; if(num) *num = res;
return TRUE; 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 * Logging to file

View File

@ -28,6 +28,8 @@
#endif #endif
#include <errno.h> // errno #include <errno.h> // errno
#include <netdb.h> // struct addrinfo
#include <pthread.h>
#include <stdlib.h> // alloc, free #include <stdlib.h> // alloc, free
#include <sys/types.h> // pid_t #include <sys/types.h> // pid_t
#include <unistd.h> // pid_t #include <unistd.h> // pid_t
@ -152,9 +154,13 @@ char *sl_omitspaces(const char *str);
// omit trailing spaces // omit trailing spaces
char *sl_omitspacesr(const char *v); 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_str2d(double *num, const char *str);
int sl_str2ll(long long *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 The original term.h
@ -389,53 +395,114 @@ typedef struct{
size_t head; // head index size_t head; // head index
size_t tail; // tail index size_t tail; // tail index
pthread_mutex_t busy; // mutex of buffer activity pthread_mutex_t busy; // mutex of buffer activity
} sl_ringbuffer; } sl_ringbuffer_t;
sl_ringbuffer *sl_RB_new(size_t size); sl_ringbuffer_t *sl_RB_new(size_t size);
void sl_RB_delete(sl_ringbuffer **b); void sl_RB_delete(sl_ringbuffer_t **b);
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);
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 sl_RB_hasbyte(sl_ringbuffer *b, uint8_t byte); ssize_t sl_RB_hasbyte(sl_ringbuffer_t *b, uint8_t byte);
int sl_RB_putbyte(sl_ringbuffer *b, uint8_t byte); int sl_RB_putbyte(sl_ringbuffer_t *b, uint8_t byte);
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);
size_t sl_RB_datalen(sl_ringbuffer *b); size_t sl_RB_datalen(sl_ringbuffer_t *b);
void sl_RB_clearbuf(sl_ringbuffer *b); size_t sl_RB_freesize(sl_ringbuffer_t *b);
ssize_t sl_RB_readline(sl_ringbuffer *b, char *s, size_t len); void sl_RB_clearbuf(sl_ringbuffer_t *b);
size_t sl_RB_writestr(sl_ringbuffer *b, char *s); 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 The original socket.h
\******************************************************************************/ \******************************************************************************/
// handler result: what to send to client // handler result: what to send to client
typedef enum{ typedef enum{
RESULT_OK, // all OK RESULT_OK, // all OK
RESULT_FAIL, // failed running command RESULT_FAIL, // failed running command
RESULT_BADVAL, // bad value RESULT_BADVAL, // bad value
RESULT_BADKEY, // bad (non-existant) key RESULT_BADKEY, // bad (non-existant) key
RESULT_SILENCE, // send nothing to client (in case of handlers which sends data by themself) RESULT_SILENCE, // send nothing to client (in case of handlers which sends data by themself)
RESULT_NUM // total amount of enum fields RESULT_AMOUNT // total amount of enum fields
} sl_hresult; } 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{ typedef struct{
sl_msghandler handler; double timestamp; // time of last change
const char *key; int64_t val;
const char *help; } sl_sock_int_t;
} sl_handleritem;
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{ typedef enum{
SOCKT_UNIX, // UNIX socket SOCKT_UNIX, // UNIX socket
SOCKT_NETLOCAL, // INET socket but only for localhost SOCKT_NETLOCAL, // INET socket but only for localhost
SOCKT_NET // true INET socket SOCKT_NET, // true INET socket
} sl_socktype; SOCKT_AMOUNT // amount of types
} sl_socktype_e;
const char *sl_hresult2str(sl_hresult r); // socket itself
int sl_start_socket(int isserver, int isnet, const char *path); typedef struct sl_sock{
void sl_sendbinmessage(int fd, const uint8_t *msg, int l); int fd; // file descriptor
void sl_sendstrmessage(int fd, const char *msg); 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);