diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6ef60a6..b5b3461 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
-cmake_minimum_required(VERSION 3.9)
+cmake_minimum_required(VERSION 4.0)
set(PROJ usefull_macros)
-set(MINOR_VERSION "2")
+set(MINOR_VERSION "3")
set(MID_VERSION "3")
set(MAJOR_VERSION "0")
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
diff --git a/Changelog b/Changelog
index 7d851e9..1775ef8 100644
--- a/Changelog
+++ b/Changelog
@@ -1,10 +1,4 @@
-Wed Dec 23 17:22:39 MSK 2020
-
-VERSION 0.1.1:
-add tty_timeout()
-fix read_tty() for disconnect
-add logging
-add sl_libversion()
+Dec 16 2024
VERSION 0.2.1:
RENAME:
@@ -54,3 +48,11 @@ Remove "bool" type (change to int)
FIXED bug with several only long options with same shortopt mark (like 0)
Add simple config files, so you can move some of your parameters settings to it
+
+Wed Dec 23 17:22:39 MSK 2020
+
+VERSION 0.1.1:
+add tty_timeout()
+fix read_tty() for disconnect
+add logging
+add sl_libversion()
diff --git a/Readme.rus b/Readme.rus
new file mode 100644
index 0000000..a7d6d29
--- /dev/null
+++ b/Readme.rus
@@ -0,0 +1,37 @@
+Репозиторий с кодом.
+Сначала избавился от нескольких багов. Потом решил добавить сетевой функционал. Ну, а коль сеть — то и кольцевой буфер нужен. В общем, теперь там — не просто макросы и функционал, который в 99% случаев нужен (тот же разбор параметров командной строки), но и дополнительные облегчалки.
+
+В релизе 0.2.1 код подвергся рефакторингу, многие функции переименованы, чтобы было единообразие: почти все функции и т.п. имеют префикс sl_; типы-перечисления имеют суффикс _e, типы данных — суффикс _t. Без префикса я оставил лишь функции red и green для цветного вывода сообщений. И многие макросы лень было переименовывать.
+
+"Старое":
+Для использования функционала в самом начале main() нужно выполнить функцию sl_init. Для работы с терминалом ввода в режиме "без эха" — sl_setup_con() (и не забыть в конце и в функции signals() вызвать sl_restore_con(), иначе по завершению придется ручками вводить reset). Функции sl_read_con() и sl_getchar() дают, соответственно, неблокирующее и блокирующее чтение символа, введенного пользователем.
+Осталось и упрощение работы с mmap: sl_mmap() и sl_munmap().
+sl_random_seed() позволяет при помощи /dev/random сгенерировать случайную затравку для использования в ГСЧ. sl_libversion() позволяет получить строку с текущей версией библиотеки.
+Для работы с gettext теперь нужно определить макрос GETTEXT, в этом случае макрос _() будет вызывать gettext().
+Функции работы с блочными устройствами не просто переименовались: по умолчанию я перешел на termios2. Если нужно собрать библиотеку с поддержкой "классической" termios со всеми ее убогими косяками, нужно определить макрос SL_USE_OLD_TTY (в этом случае еще и будет доступна функция sl_tty_convspd() для преобразования скорости интерфейса из числа в макрос). А остальные функции — как и были: sl_tty_new() для выделения памяти и инициализации структур; sl_tty_open() — открыть порт; sl_tty_read() и sl_tty_write() для чтения/записи; sl_tty_tmout() позволяет задать таймаут для select() в микросекундах. sl_tty_close() — закрыть устройство и очистить структуры данных.
+Логгирование имеет "умолчальный" глобальный лог (sl_globlog), для которого нарисованы макросы вроде LOGERR(), LOGERRADD() и т.п. (все с суффиксом ADD добавляют текст без записи timestamp и уровня сообщения — если нужно вывести несколько строк текста; но лучше ими не пользоваться часто, особенно в многопоточных/многопроцессных приложениях). Чтобы логи писались вменяемо в многопоточных/многопроцессных, пришлось лог-файл каждый раз для записи открывать, блокировать, потом писать, закрывать и разблокировать. Чтобы избежать дедлоков или слишком долгих ожиданий, установлен "гвоздями прибитый" таймаут в 0.1с: если за это время заблокировать файл не получится, то отменяем запись и возвращаем пользователю 0; иначе возвращаем количество записанных символов. "Глобальный" лог-файл активируется при помощи макроса OPENLOG(nm, lvl, prefix) (имя, уровень, флаг добавления "префикса": строчки с расшифровкой уровня сообщения, например, [WARN] или [ERR]). Функции sl_createlog(), sl_deletelog() и sl_putlogt() позволяют активировать структуру-лог (и создать файл, если надо), удалить структуру (сам файл не удаляется) и что-нибудь туда записать/дописать.
+В работе с аргументами командной строки я исправил один баг, связанный с выводом справки по "только длинным" аргументам, у которых в качестве "короткой альтернативы" проставлены одинаковые числа. Ну и была проблема с опцией "-?", которая криво обрабатывалась (т.к. я пользуюсь getopt_long). В остальном все осталось, как было: поддерживаются целочисленные аргументы (причем, arg_none означает, что к заданной переменной будет добавляться 1 каждый раз, как встречается этот флаг — удобно для задания уровня отображения как, например, -vvvv), long long, double, float, string и "аргумент-функция" (т.е. вместо присвоения значения будет выполняться заданная функция — например, чтобы парсить свои вложенные аргументы). Все также для каждого флага можно указать, что он не имеет аргументов, имеет обязательный, необязательный или множественный (в этом случае переменной-целью должен быть указатель соответствующего типа: при разборе получится NULL-terminated массив со всеми введенными пользователем данными (удобно, например, когда тебе нужно указать несколько файлов для считывания из них однообразной информации и т.п.). Поддерживаются "подопции" (когда за флагом следует строка вида "so1=par1;so2=par2..."). sl_showhelp() позволяет вызвать справку по всем опциям или только одной. Она же вызывается автоматом, если пользователь что-то не то введет. Чтобы поменять форматную строку справки, используется sl_helpstring() (там должен быть один-единственный "%s", куда вставится имя запускаемого файла). sl_parseargs() нужно запускать сразу после инициализации, и дальше уже пользоваться своей структурой с параметрами.
+Для проверки, не запущен ли второй экземпляр, есть функция sl_check4running(). Если найден процесс, запускается слабая функция sl_iffound_deflt(), которую под свои нужды можно изменить. sl_getPSname() парсит /proc и выдает имя процесса с заданным PID.
+FIFO/LIFO также остались без изменений: sl_list_push() и sl_list_push_tail() позволяют воткнуть запись в начало или хвост списка, а sl_list_pop() — извлечь последнюю запись.
+
+"Новое":
+sl_mem_avail() позволяет определить доступный объем ОЗУ. sl_omitspaces() и sl_omitspacesr() — удалить начальные и конечные пробелы. Помимо sl_str2d() ("безопасное" преобразование строки в double) я добавил sl_str2ll() и sl_str2i() — long long и int, соответственно.
+Для проверки, можно ли в данный дескриптор писать или читать из него без блокировки, добавил функции sl_canread() и sl_canwrite().
+Поддержку конфигурационных файлов тоже добавил в библиотеку. В файле все данные должны быть в формате "параметр = значение" или просто "параметр". Допускается наличие пустых строк и комментариев (все, что за символом "#" в строке). "Гвоздями" прибил максимальную длину параметра (SL_KEY_LEN, 32 байта) и значения (SL_VAL_LEN, 128 байт). Работа с флагами похожа на работу с аргументами командной строки. sl_get_keyval() чистит лишние пробелы и комментарии, выделяя непосредственно строчку "параметр [= значение]" и помещая ее в отдельные аргументы (возвращает 0 если ничего в строке не найдено, 1 — если только параметр, 2 — если и параметр, и значение); эта функция используется и в обработке запросов по сети. sl_print_opts() печатает изменяемые параметры (или все, если задан флаг) с комментариями. sl_conf_readopts() читает конфигурационный файл. Опции задаются полностью аналогично опциям командной строки, что позволяет печатать сопровождающий текст помимо флага и его значения.
+Для работы с кольцевым буфером есть следующие функции: sl_RB_new() и sl_RB_delete() — инициализация и удаление данных структуры кольцевого буфера. sl_RB_hasbyte() позволяет определить, есть ли в буфере нужный символ (например, конец строки), а sl_RB_readto() — считать до него (включительно). sl_RB_putbyte(), sl_RB_write() и sl_RB_writestr() позволяют записать данные в буфер (возвращают количество записанных символов, чтобы юзер мог понять, что что-то не то, если место кончилось). sl_RB_datalen() и sl_RB_freesize() позволяют узнать, сколько места в буфере занимают данные, и сколько там свободно. Нужно помнить, что размер данных будет на 1 байт меньше длины буфера (по понятным причинам). sl_RB_clearbuf() сбрасывает содержимое буфера, как будто бы там ничего нет.
+
+Ну и последняя, самая жирная по количеству функций и структур — сетевая часть.
+Для активации соединения есть две функции: sl_sock_run_client() и sl_sock_run_server() — для клиента и сервера, соответственно. Они инициализируют структуры, открывают соединение и подключаются для клиента или вызывают bind для сервера. Клиенту заодно запускается поток, читающий приходящие данные и складывающий в кольцевой буфер. У сервера, если указать не-NULL аргумент handlers, тоже запускается отдельный поток: он обслуживает присоединенных клиентов, запускает accept для новых, закрывает отключившиеся; полученные от клиентов данные парсятся и, если среди переданной структуры handlers находится подобный ключ, то запускается соответствующий обработчик + есть три стандартных обработчика: sl_sock_inthandler(), sl_sock_dblhandler() и sl_sock_strhandler() — для int64_t, double и char* (для того, чтобы знать, когда было произведено изменение значения, я дополнительные типы данных ввел, где помимо данных есть timestamp). Предельное количество клиентов можно задать функцией sl_sock_changemaxclients(), а узнать текущее — sl_sock_getmaxclients(). sl_sock_delete() закрывает соединение и удаляет структуру.
+Помимо этого, есть еще обработчики событий: подключение клиента, отключение и подключение клиента сверх предельного количества. По умолчанию там NULL (т.е. ничего не делается), но можно сменить при помощи, соответственно, функций sl_sock_connhandler(), sl_sock_dischandler() и sl_sock_maxclhandler(). В них можно, например, в логи писать, мол клиент fd=xx ip=yyy подключился/отключился. А в последнем — писать клиенту "подожди, все соединения заняты".
+Отправлять данные можно непосредственно при помощи write/send (не забывая сначала заблокировать, а потом разблокировать соответствующий мьютекс), либо же при помощи оберток: sl_sock_sendbyte(), sl_sock_sendbinmessage() и sl_sock_sendstrmessage(). Читать тоже можно или при помощи соответствующих функций работы с кольцевым буфером, или же текстовые строковые сообщения можно считывать посредством sl_sock_readline().
+У сервера бывают ситуации, когда одно и то же сообщение нужно отправить сразу всем клиентам. Для этого есть функция sl_sock_sendall() (она тупо перебирает все открытые и подключенные дескрипторы, да отправляет туда данные).
+
+Примеры
+clientserver — пример организации простейшего сервера и клиента. 226 строк всего: аргументы командной строки, примеры обработчиков, пример неблокирующего чтения с клавиатуры одновременно с чтением из сокета. Здесь я не парился насчет типа "локальный сетевой": можно открыть либо UNIX-сокет, либо INET. Заодно логи можно писать.
+conffile — 101 строка. Дает пример ввода конфигурационных параметров как из командной строки, так и из конфигурационного файла. Также парсит строковый параметр вида "параметр=значение". Отображает состояние флагов после обработки аргументов командной строки, а затем — после считывания из конфигурационного файла. К сожалению, чтобы считать путь к конфигурационному файлу, надо сначала обработать аргументы командной строки, поэтому, если не постараться, а просто на тот же самый массив структур вызывать чтение конф-файла, то приоритет будет за ним. Хотя, логика подсказывает, что приоритет всегда должен быть за командной строкой. Поэтому в случае, когда необходимо именно иметь приоритет командной строки, нужно будет завести 2 раздельных структуры, инициализировать их сначала каким-то дефолтом (с данными, которые пользователь точно не соможет ввести), а после сравнить и все неинициализированное из командной строки инициализировать записями из конф-файла.
+fifo — 61 строка. Пример работы с FIFO/LIFO. Показывает реальный размер свободной ОЗУ и затем все параметры командной строки помещает в списки: LIFO и FIFO. А затем из каждого списка по одному извлекает, демонстрируя, как это работает.
+helloworld — 35 строк (из которых только 10 приходится на собственно код). Демонстрирует цветной вывод и ввод без эха.
+options — 134 строки. Демонстрирует работу с разным типом опций (кроме функции: я и сам уже забыл, зачем оно мне нужно было), в т.ч. массивами. Ну и по умолчанию демонстрирует простейший терминал для работы с символьными устройствами (если пользователь введет путь).
+ringbuffer — 80 строк, демонстрирующих работу кольцевого буфера. По умолчанию он имеет размер 1024 байт (т.е. вмещает 1023 символа). Дальше пользователю предлагается вводить построчно текст, который будет помещаться в буфер. Ввод заканчивается на переполнении буфера или же при нажатии ctrl+D. После чего весь буфер вычитывается и выводится на консоль.
+
+Теперь как минимум все, что пользуется этой библиотекой, надо обновить. Ну, а сетевые демоны роботелескопов вообще радикально так переделать: чтобы "шапкой" с метеоданными не демон монтировки занимался (что вообще нелогично), а демон погоды; чтобы демоны купола, монтировки и телескопа "слушались" демона погоды. Скажем, если стоит prohibited=1, то прекращать наблюдения, когда они идут, или не давать наблюдателю открыться. Вот на БТА такую функцию АСУшник выполняет (правда, некоторые из рук вон плохо работают, позволяя наблюдателям-вредителям открывать телескоп при влажности свыше 90%, облачности или даже тумане). А на Ц-1000 почему-то до сих пор нет такого "супердемона", который бы "давал по шапке" наблюдателям-вредителям. Иначе угробят телескоп, а за это даже никакого наказания не понесут (я бы отстранял наблюдателей минимум на полгода от наблюдений, если они открывают телескоп тогда, когда этого делать ни в коем случае нельзя).
diff --git a/examples/clientserver.c b/examples/clientserver.c
index 0fa77a5..9ac67a1 100644
--- a/examples/clientserver.c
+++ b/examples/clientserver.c
@@ -126,7 +126,7 @@ static sl_sock_string_t sflag = {0};
static sl_sock_hresult_e dtimeh(sl_sock_t _U_ *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
char buf[32];
snprintf(buf, 31, "UNIXT=%.2f\n", sl_dtime());
- sl_sock_sendall((uint8_t*)buf, strlen(buf));
+ sl_sock_sendall(s, (uint8_t*)buf, strlen(buf));
return RESULT_SILENCE;
}
@@ -158,16 +158,24 @@ static void toomuch(int fd){
DBG("Disc after %gs", sl_dtime() - t0);
LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
}
-// new connections handler
-static void connected(sl_sock_t *c){
+// new connections handler (return FALSE to reject client)
+static int connected(sl_sock_t *c){
if(c->type == SOCKT_UNIX) LOGMSG("New client fd=%d connected", c->fd);
else LOGMSG("New client fd=%d, IP=%s connected", c->fd, c->IP);
+ return TRUE;
}
// disconnected handler
static void disconnected(sl_sock_t *c){
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected client fd=%d", c->fd);
else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP);
}
+static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){
+ if(!s || !str) return RESULT_FAIL;
+ sl_sock_sendstrmessage(s, "You entered wrong command:\n```\n");
+ sl_sock_sendstrmessage(s, str);
+ sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n");
+ return RESULT_SILENCE;
+}
static sl_sock_hitem_t handlers[] = {
{sl_sock_inthandler, "int", "set/get integer flag", (void*)&iflag},
@@ -185,16 +193,19 @@ int main(int argc, char **argv){
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);
- sl_sock_connhandler(connected);
- sl_sock_dischandler(disconnected);
s = sl_sock_run_server(type, G.node, -1, handlers);
} else {
sl_setup_con();
s = sl_sock_run_client(type, G.node, -1);
}
if(!s) ERRX("Can't create socket and/or run threads");
+ if(G.isserver){
+ sl_sock_changemaxclients(s, G.maxclients);
+ sl_sock_maxclhandler(s, toomuch);
+ sl_sock_connhandler(s, connected);
+ sl_sock_dischandler(s, disconnected);
+ sl_sock_defmsghandler(s, defhandler);
+ }
sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR;
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
if(G.logfile) OPENLOG(G.logfile, lvl, 1);
diff --git a/examples/fifo.c b/examples/fifo.c
index 08464a2..b234f9e 100644
--- a/examples/fifo.c
+++ b/examples/fifo.c
@@ -25,7 +25,7 @@
int main(int argc, char *argv[argc]) {
sl_list_t *f = NULL;
printf("Available memory: %luMB\n", sl_mem_avail()/1024/1024);
- //initial_setup(); - there's no need for this function if you don't use locale & don't want to have
+ sl_init();
// specific output in non-tty
if(argc == 1){
green("Usage:\n\t%s args - fill fifo with arguments\n", __progname);
@@ -55,7 +55,6 @@ int main(int argc, char *argv[argc]) {
d = sl_list_pop(&f);
green("pull: ");
printf("%s\n", d);
- // after last usage we should FREE data, but here it is parameter of main()
}
return 0;
}
diff --git a/fifo_lifo.c b/fifo_lifo.c
index f57f3df..609eb4a 100644
--- a/fifo_lifo.c
+++ b/fifo_lifo.c
@@ -24,7 +24,7 @@
/**
* @brief sl_list_push_tail - push data into the tail of a stack (like FIFO)
* @param lst (io) - list
- * @param v (i) - data to push
+ * @param v (i) - data to push (DON'T FREE it after this function as it would be just a link to original!)
* @return pointer to just pused node or NULL in case of error
*/
sl_list_t *sl_list_push_tail(sl_list_t **lst, void *v){
diff --git a/locale/ru/LC_MESSAGES/usefull_macros.mo b/locale/ru/LC_MESSAGES/usefull_macros.mo
index 556123d..4ca8b88 100644
Binary files a/locale/ru/LC_MESSAGES/usefull_macros.mo and b/locale/ru/LC_MESSAGES/usefull_macros.mo differ
diff --git a/locale/ru/messages.po b/locale/ru/messages.po
index 5f7e43e..8246731 100644
--- a/locale/ru/messages.po
+++ b/locale/ru/messages.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-16 11:25+0300\n"
+"POT-Creation-Date: 2025-06-27 11:51+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -105,38 +105,39 @@ msgstr ""
msgid "Wrong argument \"%s\" of parameter \"%s\""
msgstr ""
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:148
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:147
msgid "Server disconnected"
msgstr ""
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:223
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:390
msgid "Can't start server handlers thread"
msgstr ""
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:285
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:459
msgid "Limit of connections reached"
msgstr ""
#. check for RB overflow
#. -1 - buffer empty (can't be), -2 - buffer overflow
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:325
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:503
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:504
#, c-format
msgid "Server thread: ring buffer overflow for fd=%d"
msgstr ""
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:339
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:518
#, c-format
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
msgstr ""
#. buffer overflow
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:351
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:530
#, c-format
msgid "Server thread: buffer overflow from fd=%d"
msgstr ""
#. never reached
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:416
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:620
#, c-format
msgid "Unsupported socket type %d"
msgstr ""
diff --git a/locale/ru/ru.po b/locale/ru/ru.po
index 64a3589..8565b7a 100644
--- a/locale/ru/ru.po
+++ b/locale/ru/ru.po
@@ -7,7 +7,7 @@
msgid ""
msgstr "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
- "POT-Creation-Date: 2024-12-13 09:10+0300\n"
+ "POT-Creation-Date: 2025-06-27 11:49+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -85,7 +85,7 @@ msgstr "
msgid "Can't setup console"
msgstr " "
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:223
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:390
msgid "Can't start server handlers thread"
msgstr " - "
@@ -117,7 +117,7 @@ msgstr "
msgid "Key '%s' need value"
msgstr " '%s' "
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:285
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:459
msgid "Limit of connections reached"
msgstr " "
@@ -146,20 +146,27 @@ msgstr "
msgid "Port name is missing"
msgstr " "
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:148
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:147
msgid "Server disconnected"
msgstr " "
#. buffer overflow
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:351
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:530
+#, c-format
msgid "Server thread: buffer overflow from fd=%d"
msgstr " : fd=%d"
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:339
-msgid "Server thread: can't write data to ringbuffer, overflow from fd=%d"
-msgstr " : , fd=%d"
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:518
+#, fuzzy, c-format
+msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
+msgstr " : , "
+ " fd=%d"
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:325
+#. check for RB overflow
+#. -1 - buffer empty (can't be), -2 - buffer overflow
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:503
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:504
+#, c-format
msgid "Server thread: ring buffer overflow for fd=%d"
msgstr " : fd=%d"
@@ -168,7 +175,7 @@ msgid "Unsupported option type"
msgstr " "
#. never reached
-#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:416
+#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:620
#, c-format
msgid "Unsupported socket type %d"
msgstr " %d"
diff --git a/ringbuffer.c b/ringbuffer.c
index fd5379a..3b93d6c 100644
--- a/ringbuffer.c
+++ b/ringbuffer.c
@@ -82,6 +82,9 @@ size_t sl_RB_freesize(sl_ringbuffer_t *b){
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;
+ /*DBG("head: %zd, tail: %zd (%c %c %c %c), search %02x",
+ b->head, b->tail, b->data[startidx], b->data[startidx+1],
+ b->data[startidx+2], b->data[startidx+3], byte);*/
if(b->head > b->tail){
for(size_t found = b->head; found < b->length; ++found)
if(b->data[found] == byte) return found;
@@ -222,6 +225,13 @@ size_t sl_RB_write(sl_ringbuffer_t *b, const uint8_t *str, size_t len){
DBG("rest: %zd, need: %zd", r, len);
if(len > r) len = r;
if(!len) goto ret;
+ /*green("buf:\n");
+ for(size_t i = 0; i < len; ++i){
+ char c = (char)str[i];
+ if(c > 31) printf("%c", c);
+ else printf("\\x%02x", c);
+ }
+ green("(end)\n");*/
size_t _1st = b->length - b->tail;
if(_1st > len) _1st = len;
memcpy(b->data + b->tail, str, _1st);
diff --git a/socket.c b/socket.c
index 4ab3861..e9e9f10 100644
--- a/socket.c
+++ b/socket.c
@@ -16,6 +16,7 @@
* along with this program. If not, see .
*/
+#include
#include
#include
#include
@@ -29,37 +30,35 @@
#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 toomuch_handler = NULL;
-// new client connected handler; it will be run each new connection
-static sl_sock_connh_t newconnect_handler = NULL;
-// client disconnected handler
-static sl_sock_disch_t disconnect_handler = 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;
+void sl_sock_changemaxclients(sl_sock_t *sock, int val){
+ if(sock) sock->maxclients = val;
+}
+int sl_sock_getmaxclients(sl_sock_t *sock){
+ if(sock) return sock->maxclients;
+ else return -1;
}
-int sl_sock_getmaxclients(){ return maxclients; }
// in each next function changed default handlers you can set h to NULL to remove your handler
// setter of "too much clients" handler
-void sl_sock_maxclhandler(sl_sock_maxclh_t h){
- toomuch_handler = h;
+void sl_sock_maxclhandler(sl_sock_t *sock, void (*h)(int)){
+ if(sock) sock->toomuch_handler = h;
}
// setter of "new client connected" handler
-void sl_sock_connhandler(sl_sock_connh_t h){
- newconnect_handler = h;
+void sl_sock_connhandler(sl_sock_t *sock, int(*h)(struct sl_sock*)){
+ if(sock) sock->newconnect_handler = h;
}
// setter of "client disconnected" handler
-void sl_sock_dischandler(sl_sock_disch_t h){
- disconnect_handler = h;
+void sl_sock_dischandler(sl_sock_t *sock, void(*h)(struct sl_sock*)){
+ if(sock) sock->disconnect_handler = h;
+}
+// setter of default client message handler
+void sl_sock_defmsghandler(struct sl_sock *sock, sl_sock_hresult_e(*h)(struct sl_sock *s, const char *str)){
+ if(sock) sock->defmsg_handler = h;
}
// text messages for `hresult`
@@ -163,33 +162,174 @@ errex:
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 sl_sock_sendall(sl_sock_t *sock, uint8_t *data, size_t len){
+ FNAME();
+ if(!sock || !sock->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;
+ for(int i = sock->maxclients; i > 0; --i){
+ if(!sock->clients[i]) continue;
+ if(sock->clients[i]->fd < 0 || !sock->clients[i]->connected) continue;
+ if((ssize_t)len == sl_sock_sendbinmessage(sock->clients[i], data, len)) ++nsent;
}
return nsent;
}
+static sl_sock_hresult_e parse_post_data(sl_sock_t *c, char *str);
+
+// return TRUE if this is header without data (also modify c->sockmethod)
+static int iswebheader(sl_sock_t *client, char *str){
+ DBG("check header: _%s_", str);
+ const char *methods[] = {
+ [SOCKM_GET] = "GET",
+ [SOCKM_PUT] = "PUT",
+ [SOCKM_POST] = "POST",
+ [SOCKM_PATCH] = "PATCH",
+ [SOCKM_DELETE] = "DELETE"
+ };
+ const int metlengths[] = {
+ [SOCKM_GET] = 3,
+ [SOCKM_PUT] = 3,
+ [SOCKM_POST] = 4,
+ [SOCKM_PATCH] = 5,
+ [SOCKM_DELETE] = 6
+ };
+ for(sl_sockmethod_e m = SOCKM_GET; m < SOCKM_AMOUNT; ++m){
+ if(0 == strncmp(str, methods[m], metlengths[m])){
+ client->sockmethod = m;
+ DBG("SOCK method: %s", methods[m]);
+ if(m == SOCKM_GET){ // modify `str` by GET
+ char *slash = strchr(str, '/');
+ if(slash){
+ char *eol = strstr(slash, "HTTP");
+ if(eol){ // move to `str` head
+ size_t l = eol - slash - 2;
+ memmove(str, slash+1, l);
+ str[l] = 0;
+ DBG("User asks GET '%s' -> run parser", str);
+ //parse_post_data(client, str);
+ return TRUE;
+ }
+ }
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static char *stringscan(char *str, char *needle){
+ char *a, *end = str + strlen(str);
+ a = strstr(str, needle);
+ if(!a) return NULL;
+ a += strlen(needle);
+ while (a < end && isspace(*a)) a++;
+ if(a >= end) return NULL;
+ DBG("Found a needle \"%s\"", needle);
+ return a;
+}
+
+// just check for header words - if so, return TRUE
+static int chkwebreq(sl_sock_t *client, char *str){
+ if(client->contlen == 0){
+ char *cl = stringscan(str, "Content-Length:");
+ if(cl){
+ DBG("Contlen: _%s_", cl);
+ sl_str2i(&client->contlen, cl);
+ return TRUE;
+ }
+ }
+ DBG("retval: %d", !client->gotemptyline);
+ return !client->gotemptyline;
+}
+
+// In-place URL parser
+void url_decode(char *str) {
+ if (!str) return;
+ char *src = str;
+ char *dst = str;
+ char hex[3] = {0};
+ DBG("STR ori: _%s_", str);
+ while(*src){
+ if(*src == '+'){
+ *dst++ = ' ';
+ src++;
+ }else if(*src == '%' && isxdigit((unsigned char)src[1]) && isxdigit((unsigned char)src[2])){
+ hex[0] = src[1];
+ hex[1] = src[2];
+ *dst++ = (char)strtol(hex, NULL, 16);
+ src += 3;
+ }else{
+ *dst++ = *src++;
+ }
+ }
+ *dst = 0;
+ DBG("STR DECODED to _%s_", str);
+}
+
+static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str);
+
+// parser of web-encoded data by POST/GET:
+static sl_sock_hresult_e parse_post_data(sl_sock_t *c, char *str){
+ if (!c || !str) return RESULT_BADKEY;
+ if(0 == strcmp("favicon.ico", str)){
+ DBG("icon -> omit");
+ return RESULT_SILENCE;
+ }
+ char *start = str;
+ char *current = str;
+ DBG("\n\n\nSTART parser");
+ while(*current){
+ if(*current == '&'){
+ *current = '\0';
+ DBG("\n\n`start` = _%s_", start);
+ url_decode(start);
+ sl_sock_hresult_e r = msgparser(c, start);
+ if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
+ start = current + 1;
+ }
+ current++;
+ }
+ if(*start){
+ url_decode(start);
+ sl_sock_hresult_e r = msgparser(c, start);
+ if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
+ }
+ DBG("\n\n\nEND parser");
+ return RESULT_SILENCE;
+}
+
// parser of client's message
+// "only-server's" fields of `client` are copies of server's
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;
+ // check web headers and fields
+ DBG("\n\nLINENO: %lu", client->lineno);
+ if(client->lineno == 0){ // first data line from client -> check if it's a header
+ if(iswebheader(client, str)){
+ if(client->sockmethod == SOCKM_GET)
+ return parse_post_data(client, str);
+ return RESULT_SILENCE;
+ }
+ }else if(client->sockmethod != SOCKM_RAW){
+ if(chkwebreq(client, str)) return RESULT_SILENCE;
+ }
+ if(!client->handlers){ // have only default handler
+ if(!client->defmsg_handler) return RESULT_BADKEY;
+ return client->defmsg_handler(client, str);
+ }
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 == 0){
+ if(client->defmsg_handler) return client->defmsg_handler(client, str);
+ return RESULT_BADKEY;
+ }
if(N == 1) valptr = NULL;
else valptr = val;
if(0 == strcmp(key, "help")){
@@ -209,9 +349,31 @@ static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
if(strcmp(h->key, key)) continue;
return h->handler(client, h, valptr);
}
+ if(client->defmsg_handler) return client->defmsg_handler(client, str);
return RESULT_BADKEY;
}
+// send http responce with data in c->outbuffer
+static void send_http_response(sl_sock_t* c){
+ if(c->sockmethod == SOCKM_RAW) return;
+ DBG("Send to client HTTP response");
+ c->sockmethod = SOCKM_RAW; // dirty hack to send data by sl_sock_sendbinmessage
+ char tbuf[BUFSIZ];
+ ssize_t L = snprintf(tbuf, BUFSIZ-1,
+ "HTTP/2.0 200 OK\r\n"
+ "Access-Control-Allow-Origin: *\r\n"
+ "Access-Control-Allow-Methods: GET, POST\r\n"
+ "Access-Control-Allow-Credentials: true\r\n"
+ "Content-type: text/plain\r\nContent-Length: %zd\r\n\r\n", c->outplen);
+ if(L < 0){
+ WARN("sprintf()");
+ LOGWARN("sprintf()");
+ return;
+ }
+ if(L != sl_sock_sendbinmessage(c, (uint8_t*)tbuf, L)) return;
+ if(c->outplen != (size_t)sl_sock_sendbinmessage(c, (uint8_t*)c->outbuffer, c->outplen)) return;
+}
+
/**
* @brief serverrbthread - thread for standard server procedure (when user give non-NULL `handlers`)
* @param d - socket descriptor
@@ -219,43 +381,50 @@ static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
*/
static void *serverthread(void _U_ *d){
sl_sock_t *s = (sl_sock_t*) d;
- if(!s || !s->handlers){
+ if(!s || (!s->handlers && !s->defmsg_handler)){
WARNX(_("Can't start server handlers thread"));
goto errex;
}
int sockfd = s->fd;
- if(listen(sockfd, maxclients) == -1){
+ if(listen(sockfd, s->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);
+ struct pollfd *poll_set = MALLOC(struct pollfd, s->maxclients+1);
+ sl_sock_t **clients = MALLOC(sl_sock_t*, s->maxclients+1);
+ s->clients = clients;
// init default clients records
- for(int i = maxclients; i > 0; --i){
+ for(int i = s->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);
+ // copy server data: we have no `self`, so use so
+ c->handlers = s->handlers;
+ c->defmsg_handler = s->defmsg_handler;
}
// ZERO - listening server socket
poll_set[0].fd = sockfd;
poll_set[0].events = POLLIN;
// disconnect client
void disconnect_(sl_sock_t *c, int N){
- DBG("client \"%s\" (fd=%d) disconnected", c->IP, c->fd);
- if(disconnect_handler) disconnect_handler(c);
+ DBG("Disconnect client \"%s\" (fd=%d)", c->IP, c->fd);
+ if(s->disconnect_handler) s->disconnect_handler(c);
+ if(c->sockmethod != SOCKM_RAW) send_http_response(c); // we are closing HTTP request - send headers and answer
pthread_mutex_lock(&c->mutex);
DBG("close fd %d", c->fd);
c->connected = 0;
close(c->fd);
+ c->outplen = 0;
+ c->lineno = 0;
+ c->gotemptyline = 0;
sl_RB_clearbuf(c->buffer);
// now move all data of last client to disconnected
if(nfd > 2 && N != nfd - 1){ // don't move the only or the last
@@ -281,9 +450,9 @@ static void *serverthread(void _U_ *d){
socklen_t len = sizeof(struct sockaddr);
int client = accept(sockfd, &a, &len);
DBG("New connection, nfd=%d, len=%d", nfd, len);
- if(nfd == maxclients + 1){
+ if(nfd == s->maxclients + 1){
WARNX(_("Limit of connections reached"));
- if(toomuch_handler) toomuch_handler(client);
+ if(s->toomuch_handler) s->toomuch_handler(client);
close(client);
}else{
memset(&poll_set[nfd], 0, sizeof(struct pollfd));
@@ -302,12 +471,16 @@ static void *serverthread(void _U_ *d){
*c->IP = 0;
}
DBG("got IP:%s", c->IP);
- if(newconnect_handler) newconnect_handler(c);
- 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;
+ if(s->newconnect_handler && s->newconnect_handler(c) == FALSE){
+ DBG("Client %s rejected", c->IP);
+ disconnect_(c, nfd);
+ }else{
+ 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
+ }
+ }
}
}
// scan connections
@@ -323,6 +496,7 @@ static void *serverthread(void _U_ *d){
// check for RB overflow
if(sl_RB_hasbyte(c->buffer, '\n') < 0){ // -1 - buffer empty (can't be), -2 - buffer overflow
WARNX(_("Server thread: ring buffer overflow for fd=%d"), fd);
+ LOGERR(_("Server thread: ring buffer overflow for fd=%d"), fd);
disconnect_(c, fdidx);
--fdidx;
}
@@ -352,15 +526,39 @@ static void *serverthread(void _U_ *d){
disconnect_(c, fdidx);
--fdidx;
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));
+ }else if(got == 0){ // check last data in POST/GET methods
+ if(c->sockmethod == SOCKM_RAW) continue;
+ size_t l = sl_RB_datalen(c->buffer);
+ if(c->sockmethod == SOCKM_POST){
+ if(l != (size_t)c->contlen) continue; // wait for last data
+ if(l < bufsize - 1){
+ sl_RB_read(c->buffer, buf, l);
+ buf[l] = 0;
+ parse_post_data(c, (char*)buf);
+ }
+ }
+ disconnect_(c, fdidx);
+ --fdidx;
+ continue;
+ }
+ if(got > 1 && *buf && *buf != '\r'){ // not empty line
+ if(buf[got-2] == '\r'){
+ buf[got-2] = 0; // omit '\r' for "\r\n"
+ DBG("delete \\r: _%s_", buf);
+ }
+ sl_sock_hresult_e r = msgparser(c, (char*)buf);
+ if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
+ }else{
+ DBG("EMPTY line");
+ if(c->sockmethod != SOCKM_RAW) c->gotemptyline = TRUE;
+ }
+ ++c->lineno;
}
}
// clear memory
FREE(buf);
FREE(poll_set);
- for(int i = maxclients; i > 0; --i){
+ for(int i = s->maxclients; i > 0; --i){
DBG("Clear %dth client data", i);
sl_sock_t *c = clients[i];
if(c->fd > -1) close(c->fd);
@@ -372,6 +570,7 @@ static void *serverthread(void _U_ *d){
FREE(c);
}
FREE(clients);
+ s->clients = NULL;
errex:
s->rthread = 0;
return NULL;
@@ -420,11 +619,14 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
sl_sock_t *s = MALLOC(sl_sock_t, 1);
s->type = type;
s->fd = -1;
+ s->maxclients = SL_DEF_MAXCLIENTS;
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
+ if(!s->buffer){
+ sl_sock_delete(&s);
+ return NULL;
+ }else{ // fill node/service
+ if(type == SOCKT_UNIX) s->node = strdup(str); // str now is converted path
else{
char *delim = strchr(path, ':');
if(!delim) s->service = strdup(path); // only port
@@ -444,13 +646,19 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
// 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
+ if(!s->node){
+ DBG("Socket type now is SOCKT_NET");
+ s->type = SOCKT_NET;
+ }
+ if(s->type == SOCKT_NETLOCAL){
+ DBG("SOCKT_NETLOCAL: change `node` to localhost");
+ FREE(s->node);
+ s->node = strdup("127.0.0.1"); // localhost
+ }
}
- DBG("---> node '%s', service '%s'", node, s->service);
- int e = getaddrinfo(node, s->service, &ai, &res);
+ DBG("---> node '%s', service '%s'", s->node, s->service);
+ int e = getaddrinfo(s->node, s->service, &ai, &res);
if(e){
WARNX("getaddrinfo(): %s", gai_strerror(e));
sl_sock_delete(&s);
@@ -491,7 +699,7 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
DBG("s->fd=%d, node=%s, service=%s", s->fd, s->node, s->service);
int r = -1;
if(isserver){
- if(s->handlers)
+ if(s->handlers || s->defmsg_handler)
r = pthread_create(&s->rthread, NULL, serverthread, (void*)s);
else r = 0;
}else{
@@ -543,6 +751,15 @@ sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path, int bufsiz,
*/
ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l){
if(!msg || l < 1) return -1;
+ if(socket->sockmethod != SOCKM_RAW){ // just fill buffer while socket isn't marked as "RAW"
+ DBG("Put to buffer: _%s_", (char*)msg);
+ size_t L = BUFSIZ - socket->outplen;
+ if(l > L) l = L;
+ memcpy(socket->outbuffer + socket->outplen, msg, l);
+ socket->outplen += l;
+ DBG("Now buflen=%zd, buf: ```%s```", socket->outplen, socket->outbuffer);
+ return l;
+ }
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;
@@ -578,6 +795,13 @@ ssize_t sl_sock_sendstrmessage(sl_sock_t *socket, const char *msg){
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;
+ if(socket->sockmethod != SOCKM_RAW){ // just fill buffer while socket isn't marked as "RAW"
+ DBG("Put to buffer: _%c_", (char)byte);
+ if(socket->outplen == BUFSIZ) return 0;
+ socket->outbuffer[socket->outplen++] = (char)byte;
+ DBG("Now buflen=%zd, buf: ```%s```", socket->outplen, socket->outbuffer);
+ return 1;
+ }
DBG("lock");
pthread_mutex_lock(&socket->mutex);
ssize_t r = send(socket->fd, &byte, 1, MSG_NOSIGNAL);
diff --git a/usefull_macros.h b/usefull_macros.h
index 15d89e4..15c578a 100644
--- a/usefull_macros.h
+++ b/usefull_macros.h
@@ -21,6 +21,7 @@
#pragma once
+#include
#ifdef SL_USE_OLD_TTY
#include // termios
#else
@@ -445,7 +446,7 @@ 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);
+typedef sl_sock_hresult_e (*sl_sock_msghandler)(struct sl_sock *s, struct sl_sock_hitem *item, const char *val);
// handler item
typedef struct sl_sock_hitem{
sl_sock_msghandler handler; // function-handler
@@ -462,6 +463,30 @@ typedef enum{
SOCKT_AMOUNT // amount of types
} sl_socktype_e;
+struct sl_sock;
+
+// default max clients amount
+#define SL_DEF_MAXCLIENTS (32)
+// custom socket handlers: connect/disconnect/etc
+// max clients handler
+void sl_sock_maxclhandler(struct sl_sock *s, void (*h)(int));
+// client connected handler
+void sl_sock_connhandler(struct sl_sock *s, int(*h)(struct sl_sock*));
+// client disconnected handler
+void sl_sock_dischandler(struct sl_sock *s, void(*h)(struct sl_sock*));
+// unknown message handler (instead of "BADKEY" default message)
+void sl_sock_defmsghandler(struct sl_sock *s, sl_sock_hresult_e(*h)(struct sl_sock *s, const char *str));
+
+typedef enum{
+ SOCKM_RAW = 0, // default sockets
+ SOCKM_GET, // http methods - client should be closed after data processing
+ SOCKM_PUT,
+ SOCKM_POST,
+ SOCKM_PATCH,
+ SOCKM_DELETE,
+ SOCKM_AMOUNT
+} sl_sockmethod_e;
+
// socket itself
typedef struct sl_sock{
int fd; // file descriptor
@@ -476,29 +501,33 @@ typedef struct sl_sock{
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_sockmethod_e sockmethod; // method
+ uint64_t lineno; // number of line read
+ int contlen; // content length for POST method
+ int gotemptyline; // == TRUE when found empty line in web request (to know that header is over)
+ char outbuffer[BUFSIZ]; // buffer for output data (if client is WEB)
+ size_t outplen; // amount of bytes in `outbuffer`
+ // server-only items
+ int maxclients; // max clients amount
+ void (*toomuch_handler)(int); // too much clients handler; it is running for client connected with number>maxclients (before closing its fd)
+ int (*newconnect_handler)(struct sl_sock*); // new client connected handler; it will be run each new connection
+ void (*disconnect_handler)(struct sl_sock*); // client disconnected handler
+ sl_sock_hresult_e(*defmsg_handler)(struct sl_sock *s, const char *str); // default message handler (the only without `handlers` array or instead of "BADKEY" answer
+ struct sl_sock **clients; // pointer to clients array for `sendall`
} sl_sock_t;
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();
-// max clients handler
-typedef void (*sl_sock_maxclh_t)(int fd);
-void sl_sock_maxclhandler(sl_sock_maxclh_t h);
-// new client connected handler
-typedef void (*sl_sock_connh_t)(sl_sock_t *s);
-void sl_sock_connhandler(sl_sock_connh_t h);
-// client disconnected handler
-typedef void (*sl_sock_disch_t)(sl_sock_t *s);
-void sl_sock_dischandler(sl_sock_disch_t h);
+void sl_sock_changemaxclients(sl_sock_t *sock, int val);
+int sl_sock_getmaxclients(sl_sock_t *sock);
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);
+int sl_sock_sendall(sl_sock_t *sock, 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);
diff --git a/usefull_macros.pc.in b/usefull_macros.pc.in
index 441b10e..0b39006 100644
--- a/usefull_macros.pc.in
+++ b/usefull_macros.pc.in
@@ -1,7 +1,6 @@
prefix=@CMAKE_INSTALL_PREFIX@
-exec_prefix=${prefix}
-libdir=${exec_prefix}/lib
-includedir=${prefix}/include
+libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: @PROJ@
Description: Library with a lot of usefull snippets