mirror of
https://github.com/eddyem/snippets_library.git
synced 2026-06-21 18:56:23 +03:00
Compare commits
8 Commits
v0.3.3-r1
...
52970bfc04
| Author | SHA1 | Date | |
|---|---|---|---|
| 52970bfc04 | |||
| 179e0631ff | |||
| a2b7977866 | |||
|
|
3b6d262583 | ||
| 3903250fbe | |||
| b5d5396167 | |||
| b20e669d93 | |||
| 56ee64dd78 |
@@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 4.0)
|
cmake_minimum_required(VERSION 4.0)
|
||||||
set(PROJ usefull_macros)
|
set(PROJ usefull_macros)
|
||||||
set(MINOR_VERSION "3")
|
set(MINOR_VERSION "5")
|
||||||
set(MID_VERSION "3")
|
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}")
|
||||||
@@ -8,7 +8,7 @@ set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
|||||||
project(${PROJ} VERSION ${VERSION} LANGUAGES C)
|
project(${PROJ} VERSION ${VERSION} LANGUAGES C)
|
||||||
|
|
||||||
# default flags
|
# default flags
|
||||||
set(CMAKE_C_FLAGS "${CFLAGS} -O2")
|
set(CMAKE_C_FLAGS "$ENV{CFLAGS} -O2 -pedantic-errors")
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS}")
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS}")
|
||||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W")
|
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W")
|
||||||
set(CMAKE_COLOR_MAKEFILE ON)
|
set(CMAKE_COLOR_MAKEFILE ON)
|
||||||
@@ -35,15 +35,15 @@ if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
add_definitions(-DEBUG)
|
add_definitions(-DEBUG)
|
||||||
set(CMAKE_VERBOSE_MAKEFILE true)
|
set(CMAKE_VERBOSE_MAKEFILE true)
|
||||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
message("install to ${CMAKE_CURRENT_SOURCE_DIR}/install ")
|
message("install to ${CMAKE_CURRENT_SOURCE_DIR}/install ")
|
||||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/install)
|
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/install)
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_DEBUG})
|
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_DEBUG})
|
||||||
else()
|
else()
|
||||||
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_RELEASE})
|
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_RELEASE})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message("Build type: ${CMAKE_BUILD_TYPE}")
|
message("Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
@@ -51,16 +51,6 @@ message("Build type: ${CMAKE_BUILD_TYPE}")
|
|||||||
# here is one of two variants: all .c in directory or .c files in list
|
# here is one of two variants: all .c in directory or .c files in list
|
||||||
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
||||||
|
|
||||||
get_filename_component(term_c ${CMAKE_CURRENT_SOURCE_DIR}/term.c ABSOLUTE)
|
|
||||||
if(DEFINED USE_OLD_TTY OR DEFINED SL_USE_OLD_TTY)
|
|
||||||
get_filename_component(term_c ${CMAKE_CURRENT_SOURCE_DIR}/term2.c ABSOLUTE)
|
|
||||||
add_definitions(-DSL_USE_OLD_TTY)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message("remove ${term_c}")
|
|
||||||
list(REMOVE_ITEM SOURCES ${term_c})
|
|
||||||
|
|
||||||
|
|
||||||
# directory should contain dir locale/ru for gettext translations
|
# directory should contain dir locale/ru for gettext translations
|
||||||
set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru)
|
set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru)
|
||||||
if(NOT DEFINED LOCALEDIR)
|
if(NOT DEFINED LOCALEDIR)
|
||||||
@@ -107,7 +97,7 @@ link_directories(${${PROJ}_LIBRARY_DIRS})
|
|||||||
add_definitions(-DLOCALEDIR=\"${LOCALEDIR}\"
|
add_definitions(-DLOCALEDIR=\"${LOCALEDIR}\"
|
||||||
-DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\"
|
-DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\"
|
||||||
-DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\"
|
-DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\"
|
||||||
-DMAJOR_VERSION=\"${MAJOR_VESION}\")
|
-DMAJOR_VERSION=\"${MAJOR_VERSION}\")
|
||||||
|
|
||||||
# -l
|
# -l
|
||||||
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} -lm)
|
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} -lm)
|
||||||
|
|||||||
29
Changelog
29
Changelog
@@ -1,3 +1,32 @@
|
|||||||
|
Thu May 7 14:39:35 MSK 2026
|
||||||
|
VERSION 0.3.5
|
||||||
|
- added Readme.md
|
||||||
|
- some minor bugs fixed
|
||||||
|
|
||||||
|
Wed Mar 4 14:18:15 MSK 2026
|
||||||
|
VERSION 0.3.4:
|
||||||
|
- fixed wrong UNIX-sockets names
|
||||||
|
- code changed for `-pedantic-errors`
|
||||||
|
VERSION 0.3.5
|
||||||
|
- add int sl_sock_open(sl_socktype_e type, const char *path, int isserver, int ai_socktype);
|
||||||
|
open socket, bind or connect it and return descriptor
|
||||||
|
- deprecation of SL_USE_OLD_TTY
|
||||||
|
- add int sl_tty_fdescr(const char *comdev, const char *format, int speed, int exclusive)
|
||||||
|
open and configure serial port and return descriptor
|
||||||
|
- remove both fields `struct termios2` from `sl_tty_t`
|
||||||
|
- add int sl_tty_setformat(sl_tty_t *d, const char *format)
|
||||||
|
change default format "8N1" to given
|
||||||
|
|
||||||
|
|
||||||
|
Thu Nov 6 11:25:07 MSK 2025
|
||||||
|
VERSION 0.3.4:
|
||||||
|
add data type:
|
||||||
|
- sl_sock_keyno_t - number of key (like key[keyno] = val) using in sl_sock_hitem_t.data
|
||||||
|
add functions:
|
||||||
|
- sl_sock_keyno_init - init keyno (or use macro SL_SOCK_KEYNO_DEFAUL in assignment)
|
||||||
|
- sl_sock_keyno_check - return -1 if there's no keyno, else return its value
|
||||||
|
New functional listed in examples/clientserver.c introducing key `flags` that allows to work with bit flags as a whole or by bits like flags[1]=1, flags21=0, flags{31} or flags(14)...
|
||||||
|
|
||||||
Wed Sep 10 14:19:24 MSK 2025
|
Wed Sep 10 14:19:24 MSK 2025
|
||||||
(still version 0.3.3: I forgot to add changelog last commits)
|
(still version 0.3.3: I forgot to add changelog last commits)
|
||||||
- fixed minor bugs and memory leaks
|
- fixed minor bugs and memory leaks
|
||||||
|
|||||||
750
Readme.md
Normal file
750
Readme.md
Normal file
@@ -0,0 +1,750 @@
|
|||||||
|
# `libusefull_macros` A collection of useful C snippets for Linux
|
||||||
|
|
||||||
|
**Version:** 0.3.5
|
||||||
|
**Author:** Edward V. Emelianov (<edward.emelianoff@gmail.com>)
|
||||||
|
**License:** GPLv3+
|
||||||
|
**Repository:** [github.com/eddyem/snippets_library](https://github.com/eddyem/snippets_library)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Overview](#overview)
|
||||||
|
2. [Installation](#installation)
|
||||||
|
- [Building from source](#building-from-source)
|
||||||
|
- [CMake options](#cmake-options)
|
||||||
|
- [Linking](#linking)
|
||||||
|
3. [Quick Start](#quick-start)
|
||||||
|
4. [Module Reference](#module-reference)
|
||||||
|
- [Initialization & Locale](#initialization--locale)
|
||||||
|
- [Colored Terminal Output](#colored-terminal-output)
|
||||||
|
- [Error & Warning Macros](#error--warning-macros)
|
||||||
|
- [Memory Management](#memory-management)
|
||||||
|
- [String Utilities](#string-utilities)
|
||||||
|
- [Number Conversion](#number-conversion)
|
||||||
|
- [Console / Terminal I/O](#console--terminal-io)
|
||||||
|
- [Logging](#logging)
|
||||||
|
- [Command-line Argument Parsing](#command-line-argument-parsing)
|
||||||
|
- [Configuration Files](#configuration-files)
|
||||||
|
- [Daemon Support](#daemon-support)
|
||||||
|
- [FIFO / LIFO Linked List](#fifo--lifo-linked-list)
|
||||||
|
- [Ring Buffer](#ring-buffer)
|
||||||
|
- [TCP / UNIX Socket Server & Client](#tcp--unix-socket-server--client)
|
||||||
|
- [Serial Port (TTY)](#serial-port-tty)
|
||||||
|
- [Sub-options Parsing](#sub-options-parsing)
|
||||||
|
- [Miscellaneous Utilities](#miscellaneous-utilities)
|
||||||
|
5. [Data Structures](#data-structures)
|
||||||
|
6. [Examples](#examples)
|
||||||
|
7. [Internationalization (i18n)](#internationalization-i18n)
|
||||||
|
8. [Thread Safety](#thread-safety)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`libusefull_macros` is a C shared library that bundles many frequently needed utility routines for
|
||||||
|
Linux application development. It covers:
|
||||||
|
|
||||||
|
- Colored, locale-aware terminal output
|
||||||
|
- Safe memory allocation and memory-mapped file I/O
|
||||||
|
- GNU `getopt_long`-style command-line argument parsing with typesafe callbacks
|
||||||
|
- INIlike configuration file reading/writing
|
||||||
|
- Daemonization with PIDfile management
|
||||||
|
- Threadsafe ring buffer for producerconsumer patterns
|
||||||
|
- FIFO/LIFO linked list
|
||||||
|
- TCP and UNIX socket server/client framework with builtin HTTP parsing and keyvalue handler dispatch
|
||||||
|
- Serial port (TTY) management with nonstandard baud rates
|
||||||
|
- Filebased logging with multiple severity levels
|
||||||
|
- `gettext` integration for internationalization
|
||||||
|
|
||||||
|
All public identifiers are prefixed with `sl_` (for "snippets library") to avoid naming collisions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Building from source
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/eddyem/snippets_library.git
|
||||||
|
cd snippets_library
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake ..
|
||||||
|
make -j$(nproc)
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
The build produces a shared library `libusefull_macros.so` and a pkg-config file.
|
||||||
|
|
||||||
|
### CMake options
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `DEBUG=1` | off | Build with `-Wextra -Wall -Werror -W` and enable debug output |
|
||||||
|
| `EXAMPLES=1` | off | Build example programs in the `examples/` subdirectory |
|
||||||
|
| `NOGETTEXT` | not set | Disable gettext integration |
|
||||||
|
| `PROCESSOR_COUNT` | auto | Number of threads for parallel operations (default detects from `/proc/cpuinfo`) |
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake .. -DDEBUG=1 -DEXAMPLES=1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linking
|
||||||
|
|
||||||
|
A pkg-config file is installed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pkg-config --cflags --libs usefull_macros
|
||||||
|
```
|
||||||
|
|
||||||
|
Or manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gcc -o myapp myapp.c -I/usr/local/include -L/usr/local/lib -lusefull_macros -lm -lpthread
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
sl_init(); // locale, gettext, colored output
|
||||||
|
green("Hello, world!\n"); // green text on tty
|
||||||
|
red("An error occurred\n"); // red text on tty
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Compile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gcc -o hello hello.c $(pkg-config --cflags --libs usefull_macros)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Module Reference
|
||||||
|
|
||||||
|
### Initialization & Locale
|
||||||
|
|
||||||
|
```c
|
||||||
|
void sl_init(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
Must be called once at the beginning of `main()`. It:
|
||||||
|
|
||||||
|
- Detects whether `stdout`/`stderr` are terminals and sets up colored output functions accordingly.
|
||||||
|
- Calls `setlocale(LC_ALL, "")` and `setlocale(LC_NUMERIC, "C")` (decimal point is always a dot).
|
||||||
|
- If compiled with `GETTEXT` defined, binds the message domain.
|
||||||
|
|
||||||
|
**Important:** `sl_init()` must be called before any other library function that generates output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Colored Terminal Output
|
||||||
|
|
||||||
|
When output is a terminal (not redirected to a file or pipe), text can be printed in color:
|
||||||
|
|
||||||
|
```c
|
||||||
|
extern int (*red)(const char *fmt, ...);
|
||||||
|
extern int (*green)(const char *fmt, ...);
|
||||||
|
```
|
||||||
|
|
||||||
|
These function pointers are set by `sl_init()`. Use them like `printf`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
red("Error code: %d\n", err);
|
||||||
|
green("Operation successful\n");
|
||||||
|
```
|
||||||
|
|
||||||
|
When output is not a tty, `red` wraps the message between lines of asterisks, and `green` falls
|
||||||
|
back to plain `printf`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Error & Warning Macros
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define ERR(...) // print errno + message, then exit(9)
|
||||||
|
#define ERRX(...) // print message (no errno), then exit(9)
|
||||||
|
#define WARN(...) // print errno + message, continue
|
||||||
|
#define WARNX(...) // print message (no errno), continue
|
||||||
|
```
|
||||||
|
|
||||||
|
These use the `_WARN` function pointer (respects colored output). They automatically include the
|
||||||
|
current `errno` value when using `ERR`/`WARN`.
|
||||||
|
|
||||||
|
The default signal handler for `ERR`/`ERRX` is `signals(9)`, which simply calls `exit(9)`. You can
|
||||||
|
override the `signals` function since it is declared `__attribute__((weak))`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
void signals(int sig) {
|
||||||
|
// custom cleanup
|
||||||
|
exit(sig);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Debug macros** (active only when `-DEBUG` is defined):
|
||||||
|
|
||||||
|
```c
|
||||||
|
FNAME() // print current function name, file, line
|
||||||
|
DBG(...) // printf-like debug message
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
|
||||||
|
```c
|
||||||
|
void *sl_alloc(size_t N, size_t S);
|
||||||
|
```
|
||||||
|
|
||||||
|
Safe `calloc` wrapper. Exits with error message if allocation fails.
|
||||||
|
|
||||||
|
Convenience macros:
|
||||||
|
|
||||||
|
```c
|
||||||
|
ALLOC(type, var, size) // declare + allocate: type *var = calloc(size, sizeof(type))
|
||||||
|
MALLOC(type, size) // allocate without declaration
|
||||||
|
FREE(ptr) // free and set to NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
**Memorymapped files:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct { char *data; size_t len; } sl_mmapbuf_t;
|
||||||
|
sl_mmapbuf_t *sl_mmap(char *filename);
|
||||||
|
void sl_munmap(sl_mmapbuf_t *b);
|
||||||
|
```
|
||||||
|
|
||||||
|
Maps a file readonly into memory; `sl_munmap` unmaps and frees the structure.
|
||||||
|
|
||||||
|
**System memory query:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
uint64_t sl_mem_avail(void); // available physical memory in bytes
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### String Utilities
|
||||||
|
|
||||||
|
```c
|
||||||
|
char *sl_omitspaces(const char *str); // skip leading whitespace
|
||||||
|
char *sl_omitspacesr(const char *str); // pointer to (last non-space char + 1)
|
||||||
|
int sl_remove_quotes(char *string); // remove outer matching quotes (' or ")
|
||||||
|
int sl_get_keyval(const char *pair, char key[32], char value[128]); // parse "key = value"
|
||||||
|
```
|
||||||
|
|
||||||
|
`sl_remove_quotes` strips matched pairs of single or double quotes from both ends. Returns the
|
||||||
|
number of pairs removed (0 if none).
|
||||||
|
|
||||||
|
`sl_get_keyval` parses a line into key and value:
|
||||||
|
- Returns `0` if the line is empty or a comment (starts with `#`).
|
||||||
|
- Returns `1` if only a key is present.
|
||||||
|
- Returns `2` if both key and value are found.
|
||||||
|
- Ignores inline comments, strips surrounding whitespace and quotes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Number Conversion
|
||||||
|
|
||||||
|
```c
|
||||||
|
int sl_str2i(int *num, const char *str);
|
||||||
|
int sl_str2ll(long long *num, const char *str);
|
||||||
|
int sl_str2d(double *num, const char *str);
|
||||||
|
```
|
||||||
|
|
||||||
|
Safe `strtol`/`strtod` wrappers. Return `TRUE` (1) on success, `FALSE` (0) on failure. The output
|
||||||
|
pointer may be `NULL` to only check validity.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Console / Terminal I/O
|
||||||
|
|
||||||
|
For noncanonical, noecho terminal input:
|
||||||
|
|
||||||
|
```c
|
||||||
|
void sl_setup_con(void); // switch terminal to raw mode
|
||||||
|
void sl_restore_con(void); // restore original terminal settings
|
||||||
|
int sl_read_con(void); // nonblocking read (0 if no key)
|
||||||
|
int sl_getchar(void); // blocking read of one character
|
||||||
|
```
|
||||||
|
|
||||||
|
Typical usage:
|
||||||
|
|
||||||
|
```c
|
||||||
|
sl_setup_con();
|
||||||
|
int ch = sl_getchar();
|
||||||
|
sl_restore_con();
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important:** These functions are **not threadsafe** they use a global `struct termios2`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef enum {
|
||||||
|
LOGLEVEL_NONE, // no logging
|
||||||
|
LOGLEVEL_ERR, // only errors
|
||||||
|
LOGLEVEL_WARN, // warnings + errors
|
||||||
|
LOGLEVEL_MSG, // all except debug
|
||||||
|
LOGLEVEL_DBG, // all messages
|
||||||
|
LOGLEVEL_ANY // everything
|
||||||
|
} sl_loglevel_e;
|
||||||
|
|
||||||
|
sl_log_t *sl_createlog(const char *logpath, sl_loglevel_e level, int prefix);
|
||||||
|
void sl_deletelog(sl_log_t **log);
|
||||||
|
int sl_putlogt(int timest, sl_log_t *log, sl_loglevel_e lvl, const char *fmt, ...);
|
||||||
|
```
|
||||||
|
|
||||||
|
A "global" log is managed through the pointer `sl_globlog`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
extern sl_log_t *sl_globlog;
|
||||||
|
```
|
||||||
|
|
||||||
|
Convenience macros (write to `sl_globlog`):
|
||||||
|
|
||||||
|
| Macro | Meaning |
|
||||||
|
|-------|---------|
|
||||||
|
| `OPENLOG(path, level, prefix)` | Open global log |
|
||||||
|
| `LOGERR(...)` | Error with timestamp |
|
||||||
|
| `LOGERRADD(...)` | Error without timestamp |
|
||||||
|
| `LOGWARN(...)` / `LOGWARNADD(...)` | Warning |
|
||||||
|
| `LOGMSG(...)` / `LOGMSGADD(...)` | Message |
|
||||||
|
| `LOGDBG(...)` / `LOGDBGADD(...)` | Debug |
|
||||||
|
|
||||||
|
Timestamps use format `YYYY/MM/DD-HH:MM:SS`. Each log call locks the file with `flock` for
|
||||||
|
concurrent access.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Command-line Argument Parsing
|
||||||
|
|
||||||
|
Built on top of `getopt_long`. Supports:
|
||||||
|
|
||||||
|
- Short and long options
|
||||||
|
- Required, optional, and no arguments
|
||||||
|
- Multiple occurrences of the same option (multiparameters)
|
||||||
|
- Six data types: `int`, `long long`, `double`, `float`, `char*`, and function callback
|
||||||
|
- Automatic help generation
|
||||||
|
|
||||||
|
**Option descriptor:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
const char *name; // long option (NULL for short-only)
|
||||||
|
sl_hasarg_e has_arg; // NO_ARGS, NEED_ARG, OPT_ARG, or MULT_PAR
|
||||||
|
int *flag; // NULL return val; else set *flag = val
|
||||||
|
int val; // short option character or flag value
|
||||||
|
sl_argtype_e type; // arg_int, arg_longlong, arg_double, arg_float,
|
||||||
|
// arg_string, arg_function
|
||||||
|
void *argptr; // pointer to variable or callback function
|
||||||
|
const char *help; // help text (mandatory; end_option marks end)
|
||||||
|
} sl_option_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Helper macro:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define APTR(x) ((void*)x)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Functions:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
void sl_parseargs(int *argc, char ***argv, sl_option_t *options);
|
||||||
|
void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options,
|
||||||
|
void (*helpfun)(int, sl_option_t*));
|
||||||
|
void sl_showhelp(int oindex, sl_option_t *options);
|
||||||
|
void sl_helpstring(char *s); // customize help header
|
||||||
|
```
|
||||||
|
|
||||||
|
After calling `sl_parseargs`, `argc` and `argv` are updated to point to remaining nonoption
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
int verbose = 0;
|
||||||
|
char *output = NULL;
|
||||||
|
sl_option_t opts[] = {
|
||||||
|
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&verbose), "increase verbosity"},
|
||||||
|
{"output", NEED_ARG, NULL, 'o', arg_string, APTR(&output), "output file"},
|
||||||
|
end_option
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
sl_init();
|
||||||
|
sl_parseargs(&argc, &argv, opts);
|
||||||
|
// argc, argv now contain nonoption arguments
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Multiparameters** (`MULT_PAR`): Options that may appear multiple times. The library allocates a
|
||||||
|
`NULL`terminated array of pointers, each pointing to a newly allocated value. For `arg_string`,
|
||||||
|
each element is a pointer to a `strdup`'d string; for numeric types, each is a pointer to a
|
||||||
|
heapallocated number.
|
||||||
|
|
||||||
|
**Function callback** (`arg_function`): The callback must have signature `int (*fn)(void *arg)` and
|
||||||
|
receives a `strdup`'d argument string.
|
||||||
|
|
||||||
|
**Custom help function:** Pass a function pointer to `sl_parseargs_hf` to handle errors differently
|
||||||
|
than the default `sl_showhelp` (which calls `exit(-1)`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Configuration Files
|
||||||
|
|
||||||
|
Reads keyvalue pairs from a file and treats them as commandline options.
|
||||||
|
|
||||||
|
```c
|
||||||
|
int sl_conf_readopts(const char *filename, sl_option_t *options);
|
||||||
|
char *sl_print_opts(sl_option_t *opt, int showall);
|
||||||
|
void sl_conf_showhelp(int idx, sl_option_t *options);
|
||||||
|
```
|
||||||
|
|
||||||
|
`sl_conf_readopts` reads a file with lines like:
|
||||||
|
|
||||||
|
```
|
||||||
|
# comment
|
||||||
|
key1 = value1
|
||||||
|
key2
|
||||||
|
key3 = "quoted value"
|
||||||
|
```
|
||||||
|
|
||||||
|
Each noncomment line is converted to `--key=value` (or `--key` if no value) and passed to
|
||||||
|
`sl_parseargs`. Returns the number of recognized options.
|
||||||
|
|
||||||
|
`sl_print_opts` generates a string representation of current option values (useful for debugging or
|
||||||
|
saving state). The returned string must be freed with `free()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Daemon Support
|
||||||
|
|
||||||
|
```c
|
||||||
|
int sl_daemonize(void);
|
||||||
|
void sl_check4running(char *selfname, char *pidfilename);
|
||||||
|
char *sl_getPSname(pid_t pid);
|
||||||
|
void sl_iffound_deflt(pid_t pid); // WEAK overridable
|
||||||
|
```
|
||||||
|
|
||||||
|
`sl_daemonize()`:
|
||||||
|
- `chdir("/")`
|
||||||
|
- `umask(0)`
|
||||||
|
- Closes stdin/stdout/stderr, reopens to `/dev/null`
|
||||||
|
- Ignores `SIGHUP`
|
||||||
|
- Returns 0 on success, -1 on failure
|
||||||
|
|
||||||
|
`sl_check4running()`:
|
||||||
|
- Checks a PID file and `/proc` for a running process with the same name.
|
||||||
|
- If found, calls `sl_iffound_deflt` (by default prints a message and exits).
|
||||||
|
- Otherwise writes its own PID to the PID file.
|
||||||
|
|
||||||
|
Override `sl_iffound_deflt` in your application (it is `__attribute__((weak))`):
|
||||||
|
|
||||||
|
```c
|
||||||
|
void sl_iffound_deflt(pid_t pid) {
|
||||||
|
fprintf(stderr, "Already running (pid %d)\n", pid);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### FIFO / LIFO Linked List
|
||||||
|
|
||||||
|
A simple singlylinked list with both head and tail pointers.
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct sl_buff_node {
|
||||||
|
void *data;
|
||||||
|
struct sl_buff_node *next, *last;
|
||||||
|
} sl_list_t;
|
||||||
|
|
||||||
|
sl_list_t *sl_list_push(sl_list_t **lst, void *v); // LIFO (push to head)
|
||||||
|
sl_list_t *sl_list_push_tail(sl_list_t **lst, void *v); // FIFO (push to tail)
|
||||||
|
void *sl_list_pop(sl_list_t **lst); // pop from head
|
||||||
|
```
|
||||||
|
|
||||||
|
`sl_list_pop` returns the data pointer and frees the node. The caller is responsible for freeing
|
||||||
|
the data if needed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ring Buffer
|
||||||
|
|
||||||
|
A threadsafe, fixedsize ring buffer for byte streams, protected by `pthread_mutex_t`.
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *data;
|
||||||
|
size_t length, head, tail;
|
||||||
|
pthread_mutex_t busy;
|
||||||
|
} sl_ringbuffer_t;
|
||||||
|
|
||||||
|
sl_ringbuffer_t *sl_RB_new(size_t size);
|
||||||
|
void sl_RB_delete(sl_ringbuffer_t **b);
|
||||||
|
size_t sl_RB_read(sl_ringbuffer_t *b, uint8_t *s, size_t len);
|
||||||
|
ssize_t sl_RB_readto(sl_ringbuffer_t *b, uint8_t byte, uint8_t *s, size_t len);
|
||||||
|
ssize_t sl_RB_readline(sl_ringbuffer_t *b, char *s, size_t len);
|
||||||
|
int sl_RB_putbyte(sl_ringbuffer_t *b, uint8_t byte);
|
||||||
|
size_t sl_RB_write(sl_ringbuffer_t *b, const uint8_t *str, size_t len);
|
||||||
|
size_t sl_RB_writestr(sl_ringbuffer_t *b, char *s);
|
||||||
|
size_t sl_RB_datalen(sl_ringbuffer_t *b);
|
||||||
|
size_t sl_RB_freesize(sl_ringbuffer_t *b);
|
||||||
|
void sl_RB_clearbuf(sl_ringbuffer_t *b);
|
||||||
|
ssize_t sl_RB_hasbyte(sl_ringbuffer_t *b, uint8_t byte);
|
||||||
|
```
|
||||||
|
|
||||||
|
Key behaviors:
|
||||||
|
|
||||||
|
- `sl_RB_readline` reads up to and including a newline (`\n`), replaces `\n` with `\0`.
|
||||||
|
- `sl_RB_readto` reads until (and including) a specified byte.
|
||||||
|
- `sl_RB_writestr` ensures the string ends with `\n` before writing.
|
||||||
|
- All read/write operations are atomic with respect to the mutex.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TCP / UNIX Socket Server & Client
|
||||||
|
|
||||||
|
A highlevel socket framework supporting TCP and UNIX domain sockets, with builtin HTTP method
|
||||||
|
detection.
|
||||||
|
|
||||||
|
**Socket types:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef enum { SOCKT_UNIX, SOCKT_NETLOCAL, SOCKT_NET } sl_socktype_e;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Creating and destroying:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path,
|
||||||
|
int bufsiz, sl_sock_hitem_t *handlers);
|
||||||
|
sl_sock_t *sl_sock_run_client(sl_socktype_e type, const char *path, int bufsiz);
|
||||||
|
void sl_sock_delete(sl_sock_t **sock);
|
||||||
|
```
|
||||||
|
|
||||||
|
- `path` for UNIX sockets: file path; prefix with `\0` or `@` for abstract namespace.
|
||||||
|
- `path` for INET sockets: `"host:port"` (client) or `":port"` (server).
|
||||||
|
- `handlers`: `NULL`terminated array of keyvalue handlers (see below).
|
||||||
|
- `bufsiz`: internal ring buffer size (minimum 256).
|
||||||
|
|
||||||
|
**Sending data:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l);
|
||||||
|
ssize_t sl_sock_sendstrmessage(sl_sock_t *socket, const char *msg);
|
||||||
|
ssize_t sl_sock_sendbyte(sl_sock_t *socket, uint8_t byte);
|
||||||
|
int sl_sock_sendall(sl_sock_t *sock, uint8_t *data, size_t len); // server only
|
||||||
|
```
|
||||||
|
|
||||||
|
**Reading (client):**
|
||||||
|
|
||||||
|
```c
|
||||||
|
ssize_t sl_sock_readline(sl_sock_t *sock, char *str, size_t len);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Handler dispatch (server):**
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef sl_sock_hresult_e (*sl_sock_msghandler)(struct sl_sock *s,
|
||||||
|
struct sl_sock_hitem *item, const char *val);
|
||||||
|
|
||||||
|
typedef struct sl_sock_hitem {
|
||||||
|
sl_sock_msghandler handler;
|
||||||
|
const char *key;
|
||||||
|
const char *help;
|
||||||
|
void *data; // user data (e.g., &variable)
|
||||||
|
} sl_sock_hitem_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
Handler results:
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef enum {
|
||||||
|
RESULT_OK, RESULT_FAIL, RESULT_BADVAL,
|
||||||
|
RESULT_BADKEY, RESULT_SILENCE
|
||||||
|
} sl_sock_hresult_e;
|
||||||
|
```
|
||||||
|
|
||||||
|
Builtin handlers for common types:
|
||||||
|
|
||||||
|
```c
|
||||||
|
sl_sock_hresult_e sl_sock_inthandler(...); // int64_t
|
||||||
|
sl_sock_hresult_e sl_sock_dblhandler(...); // double
|
||||||
|
sl_sock_hresult_e sl_sock_strhandler(...); // string
|
||||||
|
```
|
||||||
|
|
||||||
|
**Optional key numbering** (`key[0]`, `key(1)`, `key{2}`, `key3`):
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct { double magick; int n; } sl_sock_keyno_t;
|
||||||
|
#define SL_SOCK_KEYNO_DEFAULT { .magick = -INFINITY, .n = -1 }
|
||||||
|
void sl_sock_keyno_init(sl_sock_keyno_t *k);
|
||||||
|
int sl_sock_keyno_check(sl_sock_keyno_t *k);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Server hooks:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
void sl_sock_changemaxclients(sl_sock_t *sock, int val);
|
||||||
|
void sl_sock_maxclhandler(sl_sock_t *sock, void (*h)(int));
|
||||||
|
void sl_sock_connhandler(sl_sock_t *sock, int (*h)(struct sl_sock*));
|
||||||
|
void sl_sock_dischandler(sl_sock_t *sock, void (*h)(struct sl_sock*));
|
||||||
|
void sl_sock_defmsghandler(sl_sock_t *sock, sl_sock_hresult_e (*h)(struct sl_sock*, const char*));
|
||||||
|
```
|
||||||
|
|
||||||
|
The server thread automatically handles `POLLIN` events, parses messages using `sl_get_keyval`, and
|
||||||
|
dispatches them to matching handlers. HTTP `GET`/`POST` requests are partially parsed: `GET`
|
||||||
|
parameters are URLdecoded and dispatched; `POST` data is accumulated and then parsed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Serial Port (TTY)
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
char *portname;
|
||||||
|
int speed;
|
||||||
|
char *format; // e.g., "8N1"
|
||||||
|
int comfd;
|
||||||
|
char *buf;
|
||||||
|
size_t bufsz, buflen;
|
||||||
|
int exclusive;
|
||||||
|
} sl_tty_t;
|
||||||
|
|
||||||
|
int sl_tty_fdescr(const char *comdev, const char *format, int speed, int exclusive);
|
||||||
|
sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz);
|
||||||
|
int sl_tty_setformat(sl_tty_t *d, const char *format);
|
||||||
|
sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive);
|
||||||
|
int sl_tty_read(sl_tty_t *d);
|
||||||
|
int sl_tty_write(int comfd, const char *buff, size_t length);
|
||||||
|
void sl_tty_close(sl_tty_t **descr);
|
||||||
|
int sl_tty_tmout(double usec);
|
||||||
|
```
|
||||||
|
|
||||||
|
Format string: three characters data bits (58), parity (N/E/O/0/1), stop bits (1/2). Example:
|
||||||
|
`"8N1"`.
|
||||||
|
|
||||||
|
Uses `struct termios2` via `ioctl(TCGETS2/TCSETS2)` to support arbitrary baud rates (not limited to
|
||||||
|
the standard `Bxxx` constants).
|
||||||
|
|
||||||
|
`sl_tty_read` uses `select()` with a configurable timeout (default 5 ms, change with
|
||||||
|
`sl_tty_tmout`). Returns the number of bytes read; data is placed in `d->buf` with length
|
||||||
|
`d->buflen`.
|
||||||
|
|
||||||
|
`sl_tty_fdescr` allows to use library functions for opening serial device with given path, format string,
|
||||||
|
non-standard speed, marking it as exclusive (not share with other processes) or not. It doesn't allocates
|
||||||
|
memory and just returns opened tty file descriptor or `-1` in case of error.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Sub-options Parsing
|
||||||
|
|
||||||
|
Parses strings like `key1=val1:key2=val2,key3`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
sl_hasarg_e has_arg;
|
||||||
|
sl_argtype_e type;
|
||||||
|
void *argptr;
|
||||||
|
} sl_suboption_t;
|
||||||
|
|
||||||
|
int sl_get_suboption(char *str, sl_suboption_t *opt);
|
||||||
|
```
|
||||||
|
|
||||||
|
The input string is tokenized on `:` and `,`; each token is matched against option names
|
||||||
|
(caseinsensitive).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Miscellaneous Utilities
|
||||||
|
|
||||||
|
```c
|
||||||
|
const char *sl_libversion(void); // returns PACKAGE_VERSION string
|
||||||
|
double sl_dtime(void); // UNIX time as double (seconds)
|
||||||
|
long sl_random_seed(void); // seed from /dev/random or time
|
||||||
|
int sl_canread(int fd); // nonblocking select() for read
|
||||||
|
int sl_canwrite(int fd); // nonblocking select() for write
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Structures
|
||||||
|
|
||||||
|
| Structure | Purpose |
|
||||||
|
|-----------|---------|
|
||||||
|
| `sl_option_t` | Commandline option descriptor |
|
||||||
|
| `sl_suboption_t` | Suboption descriptor |
|
||||||
|
| `sl_tty_t` | Serial port state |
|
||||||
|
| `sl_log_t` | Log file descriptor |
|
||||||
|
| `sl_mmapbuf_t` | Memorymapped file |
|
||||||
|
| `sl_list_t` | Linked list node |
|
||||||
|
| `sl_ringbuffer_t` | Threadsafe ring buffer |
|
||||||
|
| `sl_sock_t` | Socket state (client or server) |
|
||||||
|
| `sl_sock_hitem_t` | Socket handler item |
|
||||||
|
| `sl_sock_int_t` | Timestamped `int64_t` |
|
||||||
|
| `sl_sock_double_t` | Timestamped `double` |
|
||||||
|
| `sl_sock_string_t` | Timestamped string |
|
||||||
|
| `sl_sock_keyno_t` | Optional key number |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
The repository includes several example programs in the `examples/` directory:
|
||||||
|
|
||||||
|
| Example | Demonstrates |
|
||||||
|
|---------|-------------|
|
||||||
|
| `helloworld` | Minimal usage: `sl_init`, colored output, `sl_setup_con`/`sl_getchar`/`sl_restore_con` |
|
||||||
|
| `options` + `cmdlnopts` | Full commandline parsing with all types, logging, serial port, signals |
|
||||||
|
| `conffile` | Configuration file reading, `sl_print_opts`, multiparameters |
|
||||||
|
| `fifo` | LIFO and FIFO list operations |
|
||||||
|
| `ringbuffer` | Ring buffer creation, line reading, overflow handling |
|
||||||
|
| `clientserver` | Socket server/client with custom handlers, bit flags, logging |
|
||||||
|
| `daemon` | Daemonization, PID file, child process monitoring |
|
||||||
|
|
||||||
|
Build examples with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake .. -DEXAMPLES=1
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Internationalization (i18n)
|
||||||
|
|
||||||
|
If compiled with `GETTEXT` defined, the `_()` macro wraps `gettext()`. Translation files are
|
||||||
|
expected in the `locale/` directory. The library generates `.po` and `.mo` files during the build
|
||||||
|
(in Debug mode). To disable, define `NOGETTEXT`.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define _(String) gettext(String) // when GETTEXT is defined
|
||||||
|
#define _(String) (String) // otherwise
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Thread Safety
|
||||||
|
|
||||||
|
- **Ring buffer:** all operations are protected by a `pthread_mutex_t`.
|
||||||
|
- **Logging:** file writes are guarded with `flock(LOCK_EX)`.
|
||||||
|
- **Sockets:** server thread uses `poll()`; client read thread is separate; send operations lock the socket mutex.
|
||||||
|
- **Console I/O:** `sl_setup_con`/`sl_read_con`/`sl_getchar`/`sl_restore_con` are **not** threadsafe (global terminal state).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
67
daemon.c
67
daemon.c
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <stdio.h> // printf, fopen, ...
|
#include <stdio.h> // printf, fopen, ...
|
||||||
#include <unistd.h> // getpid
|
#include <unistd.h> // getpid
|
||||||
|
#include <signal.h>
|
||||||
#include <stdio.h> // perror
|
#include <stdio.h> // perror
|
||||||
#include <sys/types.h> // opendir
|
#include <sys/types.h> // opendir
|
||||||
#include <dirent.h> // opendir
|
#include <dirent.h> // opendir
|
||||||
@@ -73,42 +74,19 @@ void WEAK sl_iffound_deflt(pid_t pid){
|
|||||||
/**
|
/**
|
||||||
* check wether there is a same running process
|
* check wether there is a same running process
|
||||||
* exit if there is a running process or error
|
* exit if there is a running process or error
|
||||||
* Checking have 3 steps:
|
* Checking have 2 steps:
|
||||||
* 1) lock executable file
|
* 1) check pidfile and its owner (if you run a copy?)
|
||||||
* 2) check pidfile (if you run a copy?)
|
* 2) check /proc for executables with the same name (no/wrong pidfile)
|
||||||
* 3) check /proc for executables with the same name (no/wrong pidfile)
|
* @param selfname - deprecated, maybe remove in next versions
|
||||||
* @param selfname - argv[0] or NULL for non-locking
|
|
||||||
* @param pidfilename - name of pidfile or NULL if none
|
* @param pidfilename - name of pidfile or NULL if none
|
||||||
*/
|
*/
|
||||||
void sl_check4running(char *selfname, char *pidfilename){
|
void sl_check4running(char _U_ *selfname, char *pidfilename){
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
FILE *pidfile, *fself;
|
FILE *pidfile;
|
||||||
struct dirent *de;
|
struct dirent *de;
|
||||||
struct stat s_buf;
|
struct stat s_buf;
|
||||||
pid_t pid = 0, self;
|
pid_t pid = 0, self;
|
||||||
struct flock fl;
|
|
||||||
char *name, *myname;
|
char *name, *myname;
|
||||||
if(selfname){ // block self
|
|
||||||
fself = fopen(selfname, "r"); // open self binary to lock
|
|
||||||
if(!fself){
|
|
||||||
WARN("fopen()");
|
|
||||||
goto selfpid;
|
|
||||||
}
|
|
||||||
memset(&fl, 0, sizeof(struct flock));
|
|
||||||
fl.l_type = F_WRLCK;
|
|
||||||
if(fcntl(fileno(fself), F_GETLK, &fl) == -1){ // check locking
|
|
||||||
WARN("fcntl()");
|
|
||||||
goto selfpid;
|
|
||||||
}
|
|
||||||
if(fl.l_type != F_UNLCK){ // file is locking - exit
|
|
||||||
sl_iffound_deflt(fl.l_pid);
|
|
||||||
}
|
|
||||||
fl.l_type = F_RDLCK;
|
|
||||||
if(fcntl(fileno(fself), F_SETLKW, &fl) == -1){
|
|
||||||
WARN("fcntl()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
selfpid:
|
|
||||||
self = getpid(); // get self PID
|
self = getpid(); // get self PID
|
||||||
if(!(dir = opendir(PROC_BASE))){ // open /proc directory
|
if(!(dir = opendir(PROC_BASE))){ // open /proc directory
|
||||||
ERR(PROC_BASE);
|
ERR(PROC_BASE);
|
||||||
@@ -120,9 +98,11 @@ void sl_check4running(char *selfname, char *pidfilename){
|
|||||||
if(pidfilename && stat(pidfilename, &s_buf) == 0){ // pidfile exists
|
if(pidfilename && stat(pidfilename, &s_buf) == 0){ // pidfile exists
|
||||||
pidfile = fopen(pidfilename, "r");
|
pidfile = fopen(pidfilename, "r");
|
||||||
if(pidfile){
|
if(pidfile){
|
||||||
if(fscanf(pidfile, "%d", &pid) > 0){ // read PID of (possibly) running process
|
if(fscanf(pidfile, "%d", &pid) == 1){ // read PID of (possibly) running process
|
||||||
if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0)
|
if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0){
|
||||||
sl_iffound_deflt(pid);
|
sl_iffound_deflt(pid);
|
||||||
|
exit(1); // run `exit` if user forgot to do it himself
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fclose(pidfile);
|
fclose(pidfile);
|
||||||
}
|
}
|
||||||
@@ -131,8 +111,10 @@ void sl_check4running(char *selfname, char *pidfilename){
|
|||||||
while((de = readdir(dir))){ // scan /proc
|
while((de = readdir(dir))){ // scan /proc
|
||||||
if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self
|
if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self
|
||||||
continue;
|
continue;
|
||||||
if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0)
|
if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0){
|
||||||
sl_iffound_deflt(pid);
|
sl_iffound_deflt(pid);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
free(myname);
|
free(myname);
|
||||||
@@ -143,3 +125,24 @@ void sl_check4running(char *selfname, char *pidfilename){
|
|||||||
fclose(pidfile);
|
fclose(pidfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief sl_daemonize - prepare for daemonize:
|
||||||
|
* - close stdin/out/err and reopen to /dev/null
|
||||||
|
* - croot /
|
||||||
|
* - umask(0)
|
||||||
|
* - ignore SIGHUP
|
||||||
|
* @return non-zero if failed
|
||||||
|
*/
|
||||||
|
int sl_daemonize(){
|
||||||
|
if(chdir("/")) return -1;
|
||||||
|
umask(0);
|
||||||
|
close(STDIN_FILENO);
|
||||||
|
close(STDOUT_FILENO);
|
||||||
|
close(STDERR_FILENO);
|
||||||
|
if(open("/dev/null", O_RDWR) < 0) return -1;
|
||||||
|
if(dup(0) < 0) return -1;
|
||||||
|
if(dup(0) < 0) return -1;
|
||||||
|
if(SIG_ERR == signal(SIGHUP, SIG_IGN)) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ cmake_minimum_required(VERSION 3.9)
|
|||||||
project(examples)
|
project(examples)
|
||||||
|
|
||||||
# common includes & library
|
# common includes & library
|
||||||
include_directories(../)
|
#include_directories(../)
|
||||||
link_libraries(usefull_macros)
|
#link_libraries(usefull_macros)
|
||||||
|
link_libraries(${CMAKE_BINARY_DIR}/libusefull_macros.so)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR})
|
||||||
|
|
||||||
# exe list
|
# exe list
|
||||||
add_executable(helloworld helloworld.c)
|
add_executable(helloworld helloworld.c)
|
||||||
@@ -12,3 +14,4 @@ add_executable(fifo fifo.c)
|
|||||||
add_executable(conffile conffile.c)
|
add_executable(conffile conffile.c)
|
||||||
add_executable(clientserver clientserver.c)
|
add_executable(clientserver clientserver.c)
|
||||||
add_executable(ringbuffer ringbuffer.c)
|
add_executable(ringbuffer ringbuffer.c)
|
||||||
|
add_executable(daemon daemon.c)
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ static void runclient(sl_sock_t *s){
|
|||||||
static sl_sock_int_t iflag = {0};
|
static sl_sock_int_t iflag = {0};
|
||||||
static sl_sock_double_t dflag = {0};
|
static sl_sock_double_t dflag = {0};
|
||||||
static sl_sock_string_t sflag = {0};
|
static sl_sock_string_t sflag = {0};
|
||||||
|
static uint32_t bitflags = 0;
|
||||||
|
|
||||||
static sl_sock_hresult_e dtimeh(sl_sock_t _U_ *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
static sl_sock_hresult_e dtimeh(sl_sock_t _U_ *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||||
char buf[32];
|
char buf[32];
|
||||||
@@ -169,6 +170,7 @@ static void disconnected(sl_sock_t *c){
|
|||||||
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected client fd=%d", c->fd);
|
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected client fd=%d", c->fd);
|
||||||
else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP);
|
else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP);
|
||||||
}
|
}
|
||||||
|
// default (unknown key) handler
|
||||||
static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){
|
static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){
|
||||||
if(!s || !str) return RESULT_FAIL;
|
if(!s || !str) return RESULT_FAIL;
|
||||||
sl_sock_sendstrmessage(s, "You entered wrong command:\n```\n");
|
sl_sock_sendstrmessage(s, "You entered wrong command:\n```\n");
|
||||||
@@ -176,6 +178,33 @@ static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){
|
|||||||
sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n");
|
sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n");
|
||||||
return RESULT_SILENCE;
|
return RESULT_SILENCE;
|
||||||
}
|
}
|
||||||
|
// if we use this macro, there's no need to run `sl_sock_keyno_init` later
|
||||||
|
static sl_sock_keyno_t kph_number = SL_SOCK_KEYNO_DEFAULT;
|
||||||
|
// handler for key with optional parameter number
|
||||||
|
static sl_sock_hresult_e keyparhandler(struct sl_sock *s, sl_sock_hitem_t *item, const char *req){
|
||||||
|
if(!item->data) return RESULT_FAIL;
|
||||||
|
char buf[1024];
|
||||||
|
int no = sl_sock_keyno_check((sl_sock_keyno_t*)item->data);
|
||||||
|
long long newval = -1;
|
||||||
|
if(req){
|
||||||
|
if(!sl_str2ll(&newval, req) || newval < 0 || newval > 0xffffffff) return RESULT_BADVAL;
|
||||||
|
}
|
||||||
|
printf("no = %d\n", no);
|
||||||
|
if(no < 0){ // flags as a whole
|
||||||
|
if(req) bitflags = (uint32_t)newval;
|
||||||
|
snprintf(buf, 1023, "flags = 0x%08X\n", bitflags);
|
||||||
|
sl_sock_sendstrmessage(s, buf);
|
||||||
|
}else if(no < 32){ // bit access
|
||||||
|
int bitmask = 1 << no;
|
||||||
|
if(req){
|
||||||
|
if(newval) bitflags |= bitmask;
|
||||||
|
else bitflags &= ~bitmask;
|
||||||
|
}
|
||||||
|
snprintf(buf, 1023, "flags[%d] = %d\n", no, bitflags & bitmask ? 1 : 0);
|
||||||
|
sl_sock_sendstrmessage(s, buf);
|
||||||
|
}else return RESULT_BADKEY;
|
||||||
|
return RESULT_SILENCE;
|
||||||
|
}
|
||||||
|
|
||||||
static sl_sock_hitem_t handlers[] = {
|
static sl_sock_hitem_t handlers[] = {
|
||||||
{sl_sock_inthandler, "int", "set/get integer flag", (void*)&iflag},
|
{sl_sock_inthandler, "int", "set/get integer flag", (void*)&iflag},
|
||||||
@@ -183,6 +212,7 @@ static sl_sock_hitem_t handlers[] = {
|
|||||||
{sl_sock_strhandler, "str", "set/get string variable", (void*)&sflag},
|
{sl_sock_strhandler, "str", "set/get string variable", (void*)&sflag},
|
||||||
{show, "show", "show current flags @ server console", NULL},
|
{show, "show", "show current flags @ server console", NULL},
|
||||||
{dtimeh, "dtime", "get server's UNIX time for all clients connected", NULL},
|
{dtimeh, "dtime", "get server's UNIX time for all clients connected", NULL},
|
||||||
|
{keyparhandler, "flags", "set/get bit flags as whole (flags=val) or by bits (flags[bit]=val)", (void*)&kph_number},
|
||||||
{NULL, NULL, NULL, NULL}
|
{NULL, NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -193,10 +223,13 @@ int main(int argc, char **argv){
|
|||||||
if(!G.node) ERRX("Point node");
|
if(!G.node) ERRX("Point node");
|
||||||
sl_socktype_e type = (G.isunix) ? SOCKT_UNIX : SOCKT_NET;
|
sl_socktype_e type = (G.isunix) ? SOCKT_UNIX : SOCKT_NET;
|
||||||
if(G.isserver){
|
if(G.isserver){
|
||||||
|
//sl_sock_keyno_init(&kph_number); // don't forget to init first or use macro in initialisation
|
||||||
s = sl_sock_run_server(type, G.node, -1, handlers);
|
s = sl_sock_run_server(type, G.node, -1, handlers);
|
||||||
|
DBG("Server started");
|
||||||
} else {
|
} else {
|
||||||
sl_setup_con();
|
sl_setup_con();
|
||||||
s = sl_sock_run_client(type, G.node, -1);
|
s = sl_sock_run_client(type, G.node, -1);
|
||||||
|
DBG("Client started");
|
||||||
}
|
}
|
||||||
if(!s) ERRX("Can't create socket and/or run threads");
|
if(!s) ERRX("Can't create socket and/or run threads");
|
||||||
if(G.isserver){
|
if(G.isserver){
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ glob_pars *parse_args(int argc, char **argv){
|
|||||||
void *ptr;
|
void *ptr;
|
||||||
ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
||||||
size_t hlen = 1024;
|
size_t hlen = 1024;
|
||||||
char helpstring[1024], *hptr = helpstring;
|
static char helpstring[1024], *hptr = helpstring;
|
||||||
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
||||||
// format of help: "Usage: progname [args]\n"
|
// format of help: "Usage: progname [args]\n"
|
||||||
sl_helpstring(helpstring);
|
sl_helpstring(helpstring);
|
||||||
|
|||||||
105
examples/daemon.c
Normal file
105
examples/daemon.c
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Snippets project.
|
||||||
|
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// example of simplest daemon: search for running process, daemonize if all OK and herd its child
|
||||||
|
|
||||||
|
#include <linux/prctl.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
static pid_t childpid = 0;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
int help;
|
||||||
|
int verbose;
|
||||||
|
int nodaemon;
|
||||||
|
char *logfile;
|
||||||
|
char *pidfile;
|
||||||
|
} parameters;
|
||||||
|
|
||||||
|
static parameters G = {0};
|
||||||
|
|
||||||
|
static sl_option_t cmdlnopts[] = {
|
||||||
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
|
||||||
|
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose), "verbose level (each -v adds 1)"},
|
||||||
|
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), "log file name (FULL path!!!)"},
|
||||||
|
{"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), "PID-file name (FULL path!!!)"},
|
||||||
|
{"nodaemon", NO_ARGS, NULL, 0, arg_int, APTR(&G.nodaemon), "don't daemonize"},
|
||||||
|
end_option
|
||||||
|
};
|
||||||
|
|
||||||
|
void sl_iffound_deflt(pid_t pid){
|
||||||
|
WARNX("Another copy of this process found, pid=%d. Exit.", pid);
|
||||||
|
exit(1); // don't run `signals` to protect foreign PID-file from removal
|
||||||
|
}
|
||||||
|
|
||||||
|
void signals(int signo){
|
||||||
|
if(childpid){ // this is a main process!
|
||||||
|
LOGERR("Main process exits with status %d", signo);
|
||||||
|
// main process have nothing to cleanup, just remove PID-file
|
||||||
|
if(G.pidfile) unlink(G.pidfile);
|
||||||
|
}else{ // this is child
|
||||||
|
LOGERR("Killed with status %d, clearing", signo);
|
||||||
|
// here we can close everything and make cleanup
|
||||||
|
}
|
||||||
|
usleep(1000);
|
||||||
|
LOGERR("Exited");
|
||||||
|
exit(signo);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
sl_init();
|
||||||
|
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||||
|
if(G.help) sl_showhelp(-1, cmdlnopts);
|
||||||
|
sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR;
|
||||||
|
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
|
||||||
|
sl_check4running(NULL, G.pidfile);
|
||||||
|
LOGMSG("Hello, I'm started");
|
||||||
|
if(G.logfile) OPENLOG(G.logfile, lvl, 1);
|
||||||
|
if(!G.nodaemon){
|
||||||
|
green("Daemonize..\n");
|
||||||
|
LOGMSG("Daemonize");
|
||||||
|
// and ignore SIGHUP
|
||||||
|
if(sl_daemonize()){
|
||||||
|
WARN("sl_daemonize() failed");
|
||||||
|
LOGERR("Can't daemonize");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signal(SIGTERM, signals); // kill (-15) - quit
|
||||||
|
signal(SIGINT, signals); // ctrl+C - quit
|
||||||
|
signal(SIGQUIT, signals); // ctrl+\ - quit
|
||||||
|
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
||||||
|
while(1){ // guard for dead processes
|
||||||
|
childpid = fork();
|
||||||
|
if(childpid){
|
||||||
|
LOGMSG("create child with PID %d\n", childpid);
|
||||||
|
DBG("Created child with PID %d\n", childpid);
|
||||||
|
wait(NULL);
|
||||||
|
WARNX("Child %d died\n", childpid);
|
||||||
|
LOGWARN("Child %d died\n", childpid);
|
||||||
|
sleep(1);
|
||||||
|
}else{
|
||||||
|
prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies
|
||||||
|
break; // go out to normal functional
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGMSG("Here is the child process; sleep for 1 second and die");
|
||||||
|
sleep(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -48,8 +48,13 @@ void signals(int sig){
|
|||||||
LOGERR("Exit with status %d", sig);
|
LOGERR("Exit with status %d", sig);
|
||||||
if(GP && GP->pidfile) // remove unnesessary PID file
|
if(GP && GP->pidfile) // remove unnesessary PID file
|
||||||
unlink(GP->pidfile);
|
unlink(GP->pidfile);
|
||||||
|
DBG("restore console");
|
||||||
sl_restore_con();
|
sl_restore_con();
|
||||||
if(dev) sl_tty_close(&dev);
|
if(dev){
|
||||||
|
DBG("Close serial device");
|
||||||
|
sl_tty_close(&dev);
|
||||||
|
}
|
||||||
|
DBG("OK, exit");
|
||||||
exit(sig);
|
exit(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,13 +105,17 @@ int main(int argc, char *argv[]){
|
|||||||
printf("String so2=%s\n", GP->so2);
|
printf("String so2=%s\n", GP->so2);
|
||||||
}
|
}
|
||||||
if(GP->device){
|
if(GP->device){
|
||||||
LOGDBG("Try to open serial %s", GP->device);
|
LOGMSG("Try to open serial %s at speed %d", GP->device, GP->speed);
|
||||||
dev = sl_tty_new(GP->device, GP->speed, 4096);
|
dev = sl_tty_new(GP->device, GP->speed, 4096);
|
||||||
if(dev) dev = sl_tty_open(dev, GP->exclusive);
|
if(dev) dev = sl_tty_open(dev, GP->exclusive);
|
||||||
if(!dev){
|
if(!dev){
|
||||||
LOGERR("Can't open %s with speed %d. Exit.", GP->device, GP->speed);
|
LOGERR("Can't open %s with speed %d. Exit.", GP->device, GP->speed);
|
||||||
signals(0);
|
signals(0);
|
||||||
}
|
}
|
||||||
|
if(GP->speed != dev->speed){
|
||||||
|
LOGERR("Can't set exact speed! Opened %s at speed %d", dev->portname, dev->speed);
|
||||||
|
ERRX("Can't set speed %d (try %d)", GP->speed, dev->speed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(!dev) return 0;
|
if(!dev) return 0;
|
||||||
sl_setup_con();
|
sl_setup_con();
|
||||||
|
|||||||
Binary file not shown.
@@ -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: 2025-09-10 12:08+0300\n"
|
"POT-Creation-Date: 2026-03-04 17:48+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"
|
||||||
@@ -77,79 +77,79 @@ msgstr ""
|
|||||||
msgid "Can't use multiple args with arg_none!"
|
msgid "Can't use multiple args with arg_none!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:267
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:268
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "double long arguments: --%s"
|
msgid "double long arguments: --%s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:274
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "double short arguments: -%c"
|
msgid "double short arguments: -%c"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:343
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:347
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Need argument with %s type\n"
|
msgid "Need argument with %s type\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. print only one message
|
#. print only one message
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:435
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:439
|
||||||
msgid "sl_showhelp(): option index out of range"
|
msgid "sl_showhelp(): option index out of range"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:441
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:445
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Usage: %s [arguments]\n"
|
msgid "Usage: %s [arguments]\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:528
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:535
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Wrong parameter: %s"
|
msgid "Wrong parameter: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:532
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:539
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s: need argument!"
|
msgid "%s: need argument!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:536
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:543
|
||||||
#, c-format
|
#, c-format
|
||||||
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:147
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:132
|
||||||
msgid "Server disconnected"
|
msgid "Server disconnected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:385
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:419
|
||||||
msgid "Can't start server handlers thread"
|
msgid "Can't start server handlers thread"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:454
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:491
|
||||||
msgid "Limit of connections reached"
|
msgid "Limit of connections reached"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. check for RB overflow
|
#. check for RB overflow
|
||||||
#. -1 - buffer empty (can't be), -2 - buffer overflow
|
#. -1 - buffer empty (can't be), -2 - buffer overflow
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:498
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:535
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:499
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:536
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Server thread: ring buffer overflow for fd=%d"
|
msgid "Server thread: ring buffer overflow for fd=%d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:513
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:550
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
|
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. buffer overflow
|
#. buffer overflow
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:525
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:562
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Server thread: buffer overflow from fd=%d"
|
msgid "Server thread: buffer overflow from fd=%d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. never reached
|
#. never reached
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:615
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:714
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Unsupported socket type %d"
|
msgid "Unsupported socket type %d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -159,37 +159,43 @@ msgstr ""
|
|||||||
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"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:105
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Can't use port %s"
|
msgid "Can't use port %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Get settings
|
#. Get settings
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:106
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:111
|
||||||
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130
|
||||||
msgid "Can't get old TTY settings"
|
msgid "Can't get old TTY settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:119
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124
|
||||||
msgid "Can't apply new TTY settings"
|
msgid "Can't apply new TTY settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:136
|
||||||
#, c-format
|
|
||||||
msgid "Can't set speed %d, got ispeed=%d, ospeed=%d"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130
|
|
||||||
msgid "Can't do exclusive open"
|
msgid "Can't do exclusive open"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:175
|
||||||
msgid "Port name is missing"
|
msgid "Port name is missing"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:168
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:183
|
||||||
msgid "Need non-zero buffer for TTY device"
|
msgid "Need non-zero buffer for TTY device"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Get settings
|
||||||
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:219
|
||||||
|
msgid "Can't get current TTY settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:222
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't set exact speed %d"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194
|
||||||
msgid "Can't open /dev/random"
|
msgid "Can't open /dev/random"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -224,6 +230,6 @@ msgstr ""
|
|||||||
msgid "Can't munmap"
|
msgid "Can't munmap"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:331
|
||||||
msgid "Can't setup console"
|
msgid "Can't setup console"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -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: 2025-09-10 12:08+0300\n"
|
"POT-Creation-Date: 2026-03-04 17:30+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"
|
||||||
@@ -33,7 +33,7 @@ msgstr "sl_conf_showhelp():
|
|||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:322
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:322
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Configuration file options (format: key=value):\n"
|
msgid "Configuration file options (format: key=value):\n"
|
||||||
msgstr ""
|
msgstr "ïÐÃÉÉ ËÏÎÆÉÇÕÒÁÃÉÏÎÎÏÇÏ ÆÁÊÌÁ (ÆÏÒÍÁÔ: ËÌÀÞ=ÚÎÁÞÅÎÉÅ):\n"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69
|
||||||
#, c-format
|
#, c-format
|
||||||
@@ -76,80 +76,80 @@ msgstr "
|
|||||||
msgid "Can't use multiple args with arg_none!"
|
msgid "Can't use multiple args with arg_none!"
|
||||||
msgstr "íÁÓÓÉ× ÐÁÒÁÍÅÔÒÏ× ÎÅ ÍÏÖÅÔ ÉÍÅÔØ ÔÉÐ arg_none!"
|
msgstr "íÁÓÓÉ× ÐÁÒÁÍÅÔÒÏ× ÎÅ ÍÏÖÅÔ ÉÍÅÔØ ÔÉÐ arg_none!"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:267
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:268
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "double long arguments: --%s"
|
msgid "double long arguments: --%s"
|
||||||
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ÄÌÉÎÎÙÊ ÐÁÒÁÍÅÔÒ: --%s"
|
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ÄÌÉÎÎÙÊ ÐÁÒÁÍÅÔÒ: --%s"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:274
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "double short arguments: -%c"
|
msgid "double short arguments: -%c"
|
||||||
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ËÏÒÏÔËÉÊ ÐÁÒÁÍÅÔÒ: -%c"
|
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ËÏÒÏÔËÉÊ ÐÁÒÁÍÅÔÒ: -%c"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:343
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:347
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Need argument with %s type\n"
|
msgid "Need argument with %s type\n"
|
||||||
msgstr "îÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ Ó ÔÉÐÏÍ %s\n"
|
msgstr "îÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ Ó ÔÉÐÏÍ %s\n"
|
||||||
|
|
||||||
#. print only one message
|
#. print only one message
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:435
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:439
|
||||||
msgid "sl_showhelp(): option index out of range"
|
msgid "sl_showhelp(): option index out of range"
|
||||||
msgstr "sl_showhelp(): ÉÎÄÅËÓ ÏÐÃÉÉ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ"
|
msgstr "sl_showhelp(): ÉÎÄÅËÓ ÏÐÃÉÉ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:441
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:445
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Usage: %s [arguments]\n"
|
msgid "Usage: %s [arguments]\n"
|
||||||
msgstr "éÓÐÏÌØÚÏ×ÁÎÉÅ: %s [ÁÒÇÕÍÅÎÔÙ]\n"
|
msgstr "éÓÐÏÌØÚÏ×ÁÎÉÅ: %s [ÁÒÇÕÍÅÎÔÙ]\n"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:528
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:535
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Wrong parameter: %s"
|
msgid "Wrong parameter: %s"
|
||||||
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ: %s"
|
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ: %s"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:532
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:539
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%s: need argument!"
|
msgid "%s: need argument!"
|
||||||
msgstr "%s: ÎÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ!"
|
msgstr "%s: ÎÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ!"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:536
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:543
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Wrong argument \"%s\" of parameter \"%s\""
|
msgid "Wrong argument \"%s\" of parameter \"%s\""
|
||||||
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÁÒÇÕÍÅÎÔ \"%s\" ÐÁÒÁÍÅÔÒÁ \"%s\""
|
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÁÒÇÕÍÅÎÔ \"%s\" ÐÁÒÁÍÅÔÒÁ \"%s\""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:147
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:132
|
||||||
msgid "Server disconnected"
|
msgid "Server disconnected"
|
||||||
msgstr "óÅÒ×ÅÒ ÏÔËÌÀÞÅÎ"
|
msgstr "óÅÒ×ÅÒ ÏÔËÌÀÞÅÎ"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:385
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:419
|
||||||
msgid "Can't start server handlers thread"
|
msgid "Can't start server handlers thread"
|
||||||
msgstr "îÅ ÍÏÇÕ ÚÁÐÕÓÔÉÔØ ÐÏÔÏË-ÏÂÒÁÂÏÔÞÉË ÓÅÒ×ÅÒÁ"
|
msgstr "îÅ ÍÏÇÕ ÚÁÐÕÓÔÉÔØ ÐÏÔÏË-ÏÂÒÁÂÏÔÞÉË ÓÅÒ×ÅÒÁ"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:454
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:491
|
||||||
msgid "Limit of connections reached"
|
msgid "Limit of connections reached"
|
||||||
msgstr "äÏÓÔÉÇÎÕÔ ÐÒÅÄÅÌ ÓÏÅÄÉÎÅÎÉÊ"
|
msgstr "äÏÓÔÉÇÎÕÔ ÐÒÅÄÅÌ ÓÏÅÄÉÎÅÎÉÊ"
|
||||||
|
|
||||||
#. check for RB overflow
|
#. check for RB overflow
|
||||||
#. -1 - buffer empty (can't be), -2 - buffer overflow
|
#. -1 - buffer empty (can't be), -2 - buffer overflow
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:498
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:535
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:499
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:536
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Server thread: ring buffer overflow for fd=%d"
|
msgid "Server thread: ring buffer overflow for fd=%d"
|
||||||
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
|
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:513
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:550
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
|
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
|
||||||
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÎÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÄÁÎÎÙÅ × ËÏÌØÃÅ×ÏÍ ÂÕÆÅÒÅ, "
|
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÎÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÄÁÎÎÙÅ × ËÏÌØÃÅ×ÏÍ ÂÕÆÅÒÅ, "
|
||||||
"ÐÅÒÅÐÏÌÎÅÎÉÅ ÏÔ fd=%d"
|
"ÐÅÒÅÐÏÌÎÅÎÉÅ ÏÔ fd=%d"
|
||||||
|
|
||||||
#. buffer overflow
|
#. buffer overflow
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:525
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:562
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Server thread: buffer overflow from fd=%d"
|
msgid "Server thread: buffer overflow from fd=%d"
|
||||||
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
|
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
|
||||||
|
|
||||||
#. never reached
|
#. never reached
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:615
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:714
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Unsupported socket type %d"
|
msgid "Unsupported socket type %d"
|
||||||
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÓÏËÅÔÁ %d"
|
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÓÏËÅÔÁ %d"
|
||||||
@@ -161,37 +161,44 @@ msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: "
|
|||||||
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ USART \"%s\"; ÎÕÖÅÎ NPS, ÇÄÅ N: 5..8; P: N/E/O/"
|
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ USART \"%s\"; ÎÕÖÅÎ NPS, ÇÄÅ N: 5..8; P: N/E/O/"
|
||||||
"1/0, S: 1/2"
|
"1/0, S: 1/2"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:105
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Can't use port %s"
|
msgid "Can't use port %s"
|
||||||
msgstr "îÅ ÍÏÇÕ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÏÒÔ %s"
|
msgstr "îÅ ÍÏÇÕ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÏÒÔ %s"
|
||||||
|
|
||||||
#. Get settings
|
#. Get settings
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:106
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:111
|
||||||
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130
|
||||||
msgid "Can't get old TTY settings"
|
msgid "Can't get old TTY settings"
|
||||||
msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÊÓÔ×ÕÀÝÉÅ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
|
msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÊÓÔ×ÕÀÝÉÅ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:119
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124
|
||||||
msgid "Can't apply new TTY settings"
|
msgid "Can't apply new TTY settings"
|
||||||
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
|
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:136
|
||||||
#, c-format
|
|
||||||
msgid "Can't set speed %d, got ispeed=%d, ospeed=%d"
|
|
||||||
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÓËÏÒÏÓÔØ ÎÁ %d; ispeed=%d, ospeed=%d"
|
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130
|
|
||||||
msgid "Can't do exclusive open"
|
msgid "Can't do exclusive open"
|
||||||
msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ ÐÏÒÔ × ÜËÓËÌÀÚÉ×ÎÙÊ ÒÅÖÉÍ"
|
msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ ÐÏÒÔ × ÜËÓËÌÀÚÉ×ÎÙÊ ÒÅÖÉÍ"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:175
|
||||||
msgid "Port name is missing"
|
msgid "Port name is missing"
|
||||||
msgstr "ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ"
|
msgstr "ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:168
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:183
|
||||||
msgid "Need non-zero buffer for TTY device"
|
msgid "Need non-zero buffer for TTY device"
|
||||||
msgstr "äÌÑ ÐÏÓÌÅÄÏ×ÁÔÅÌØÎÏÇÏ ÕÓÔÒÏÊÓÔ×Á ÔÒÅÂÕÅÔÓÑ ÂÕÆÅÒ ÎÅÎÕÌÅ×ÏÇÏ ÒÁÚÍÅÒÁ"
|
msgstr "äÌÑ ÐÏÓÌÅÄÏ×ÁÔÅÌØÎÏÇÏ ÕÓÔÒÏÊÓÔ×Á ÔÒÅÂÕÅÔÓÑ ÂÕÆÅÒ ÎÅÎÕÌÅ×ÏÇÏ ÒÁÚÍÅÒÁ"
|
||||||
|
|
||||||
|
#. Get settings
|
||||||
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:219
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Can't get current TTY settings"
|
||||||
|
msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÊÓÔ×ÕÀÝÉÅ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
|
||||||
|
|
||||||
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:222
|
||||||
|
#, fuzzy, c-format
|
||||||
|
msgid "Can't set exact speed %d"
|
||||||
|
msgstr "îÅ ÍÏÇÕ ×ÙÐÏÌÎÉÔØ stat %s"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194
|
||||||
msgid "Can't open /dev/random"
|
msgid "Can't open /dev/random"
|
||||||
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ /dev/random"
|
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ /dev/random"
|
||||||
@@ -226,6 +233,8 @@ msgstr "
|
|||||||
msgid "Can't munmap"
|
msgid "Can't munmap"
|
||||||
msgstr "îÅ ÍÏÇÕ munmap"
|
msgstr "îÅ ÍÏÇÕ munmap"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:331
|
||||||
msgid "Can't setup console"
|
msgid "Can't setup console"
|
||||||
msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"
|
msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"
|
||||||
|
|
||||||
|
#, c-format
|
||||||
|
|||||||
110
parseargs.c
110
parseargs.c
@@ -182,6 +182,7 @@ void *get_aptr(void *paptr, sl_argtype_e type){
|
|||||||
break;*/
|
break;*/
|
||||||
}
|
}
|
||||||
aptr = realloc(aptr, (i + 1) * sizeof(void*));
|
aptr = realloc(aptr, (i + 1) * sizeof(void*));
|
||||||
|
if(!aptr) ERR("realloc()");
|
||||||
*((void***)paptr) = aptr;
|
*((void***)paptr) = aptr;
|
||||||
aptr[i] = NULL;
|
aptr[i] = NULL;
|
||||||
if(sz){
|
if(sz){
|
||||||
@@ -191,6 +192,18 @@ void *get_aptr(void *paptr, sl_argtype_e type){
|
|||||||
return aptr[i - 1];
|
return aptr[i - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cmpstringp(const void *p1, const void *p2){
|
||||||
|
if(!p1 || !p2) return 0;
|
||||||
|
const char *str1 = * (char * const *) p1, *str2 = * (char * const *) p2;
|
||||||
|
if(!str1 && !str2) return 0;
|
||||||
|
else if(!str1) return 1;
|
||||||
|
else if(!str2) return -1;
|
||||||
|
return strcmp(str1, str2);
|
||||||
|
}
|
||||||
|
static int cmpcharp(const void *p1, const void *p2){
|
||||||
|
return (int)(*(char * const)p1 - *(char *const)p2);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief sl_parseargs_hf - parse arguments with user help funtion
|
* @brief sl_parseargs_hf - parse arguments with user help funtion
|
||||||
* @param argc - amount of arguments
|
* @param argc - amount of arguments
|
||||||
@@ -246,17 +259,6 @@ void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options, void (*helpf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// sort all lists & check for repeating
|
// sort all lists & check for repeating
|
||||||
int cmpstringp(const void *p1, const void *p2){
|
|
||||||
if(!p1 || !p2) return 0;
|
|
||||||
const char *str1 = * (char * const *) p1, *str2 = * (char * const *) p2;
|
|
||||||
if(!str1 && !str2) return 0;
|
|
||||||
else if(!str1) return 1;
|
|
||||||
else if(!str2) return -1;
|
|
||||||
return strcmp(str1, str2);
|
|
||||||
}
|
|
||||||
int cmpcharp(const void *p1, const void *p2){
|
|
||||||
return (int)(*(char * const)p1 - *(char *const)p2);
|
|
||||||
}
|
|
||||||
qsort(longlist, optsize, sizeof(char *), cmpstringp);
|
qsort(longlist, optsize, sizeof(char *), cmpstringp);
|
||||||
qsort(shortlist,optsize, sizeof(char), cmpcharp);
|
qsort(shortlist,optsize, sizeof(char), cmpcharp);
|
||||||
char *prevl = longlist[0], prevshrt = shortlist[0];
|
char *prevl = longlist[0], prevshrt = shortlist[0];
|
||||||
@@ -336,7 +338,10 @@ void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options, void (*helpf
|
|||||||
type = "string";
|
type = "string";
|
||||||
break;
|
break;
|
||||||
case arg_function:
|
case arg_function:
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
result = ((sl_argfn_t)aptr)(optarg);
|
result = ((sl_argfn_t)aptr)(optarg);
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(!result){
|
if(!result){
|
||||||
@@ -460,6 +465,49 @@ void sl_showhelp(int oindex, sl_option_t *options){
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int findsubopt(char *par, sl_suboption_t *so){
|
||||||
|
int idx = 0;
|
||||||
|
if(!par) return -1;
|
||||||
|
while(so[idx].name){
|
||||||
|
if(strcasecmp(par, so[idx].name) == 0) return idx;
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
return -1; // badarg
|
||||||
|
}
|
||||||
|
static int opt_setarg(sl_suboption_t *so, int idx, char *val){
|
||||||
|
sl_suboption_t *soptr = &so[idx];
|
||||||
|
int result = FALSE;
|
||||||
|
void *aptr = soptr->argptr;
|
||||||
|
switch(soptr->type){
|
||||||
|
default:
|
||||||
|
case arg_none:
|
||||||
|
if(soptr->argptr) *((int*)aptr) += 1; // increment value
|
||||||
|
result = TRUE;
|
||||||
|
break;
|
||||||
|
case arg_int:
|
||||||
|
result = myatoll(aptr, val, arg_int);
|
||||||
|
break;
|
||||||
|
case arg_longlong:
|
||||||
|
result = myatoll(aptr, val, arg_longlong);
|
||||||
|
break;
|
||||||
|
case arg_double:
|
||||||
|
result = myatod(aptr, val, arg_double);
|
||||||
|
break;
|
||||||
|
case arg_float:
|
||||||
|
result = myatod(aptr, val, arg_float);
|
||||||
|
break;
|
||||||
|
case arg_string:
|
||||||
|
result = (*((void**)aptr) = (void*)strdup(val)) != NULL;
|
||||||
|
break;
|
||||||
|
case arg_function:
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
|
result = ((sl_argfn_t)aptr)(val);
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @brief sl_get_suboption - get suboptions from parameter string
|
* @brief sl_get_suboption - get suboptions from parameter string
|
||||||
* @param str - parameter string
|
* @param str - parameter string
|
||||||
@@ -467,46 +515,6 @@ void sl_showhelp(int oindex, sl_option_t *options){
|
|||||||
* @return TRUE if all OK
|
* @return TRUE if all OK
|
||||||
*/
|
*/
|
||||||
int sl_get_suboption(char *str, sl_suboption_t *opt){
|
int sl_get_suboption(char *str, sl_suboption_t *opt){
|
||||||
int findsubopt(char *par, sl_suboption_t *so){
|
|
||||||
int idx = 0;
|
|
||||||
if(!par) return -1;
|
|
||||||
while(so[idx].name){
|
|
||||||
if(strcasecmp(par, so[idx].name) == 0) return idx;
|
|
||||||
++idx;
|
|
||||||
}
|
|
||||||
return -1; // badarg
|
|
||||||
}
|
|
||||||
int opt_setarg(sl_suboption_t *so, int idx, char *val){
|
|
||||||
sl_suboption_t *soptr = &so[idx];
|
|
||||||
int result = FALSE;
|
|
||||||
void *aptr = soptr->argptr;
|
|
||||||
switch(soptr->type){
|
|
||||||
default:
|
|
||||||
case arg_none:
|
|
||||||
if(soptr->argptr) *((int*)aptr) += 1; // increment value
|
|
||||||
result = TRUE;
|
|
||||||
break;
|
|
||||||
case arg_int:
|
|
||||||
result = myatoll(aptr, val, arg_int);
|
|
||||||
break;
|
|
||||||
case arg_longlong:
|
|
||||||
result = myatoll(aptr, val, arg_longlong);
|
|
||||||
break;
|
|
||||||
case arg_double:
|
|
||||||
result = myatod(aptr, val, arg_double);
|
|
||||||
break;
|
|
||||||
case arg_float:
|
|
||||||
result = myatod(aptr, val, arg_float);
|
|
||||||
break;
|
|
||||||
case arg_string:
|
|
||||||
result = (*((void**)aptr) = (void*)strdup(val)) != NULL;
|
|
||||||
break;
|
|
||||||
case arg_function:
|
|
||||||
result = ((sl_argfn_t)aptr)(val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
char *tok;
|
char *tok;
|
||||||
int ret = FALSE;
|
int ret = FALSE;
|
||||||
char *tmpbuf;
|
char *tmpbuf;
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ void sl_RB_clearbuf(sl_ringbuffer_t *b){
|
|||||||
/**
|
/**
|
||||||
* @brief sl_RB_writestr - write FULL string `s` to buffer (without trailing zero!)
|
* @brief sl_RB_writestr - write FULL string `s` to buffer (without trailing zero!)
|
||||||
* @param b - rb
|
* @param b - rb
|
||||||
* @param s - string
|
* @param s - string !!! can be modified if have no '\n' on end !!!
|
||||||
* @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_t *b, char *s){
|
size_t sl_RB_writestr(sl_ringbuffer_t *b, char *s){
|
||||||
|
|||||||
370
socket.c
370
socket.c
@@ -16,8 +16,9 @@
|
|||||||
* 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 <ctype.h>
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <inttypes.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -25,7 +26,6 @@
|
|||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/un.h> // unix socket
|
#include <sys/un.h> // unix socket
|
||||||
#include <inttypes.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "usefull_macros.h"
|
#include "usefull_macros.h"
|
||||||
@@ -48,7 +48,7 @@ int sl_sock_getmaxclients(sl_sock_t *sock){
|
|||||||
void sl_sock_maxclhandler(sl_sock_t *sock, void (*h)(int)){
|
void sl_sock_maxclhandler(sl_sock_t *sock, void (*h)(int)){
|
||||||
if(sock) sock->toomuch_handler = h;
|
if(sock) sock->toomuch_handler = h;
|
||||||
}
|
}
|
||||||
// setter of "new client connected" handler
|
// setter of "new client connected" han;dler
|
||||||
void sl_sock_connhandler(sl_sock_t *sock, int(*h)(struct sl_sock*)){
|
void sl_sock_connhandler(sl_sock_t *sock, int(*h)(struct sl_sock*)){
|
||||||
if(sock) sock->newconnect_handler = h;
|
if(sock) sock->newconnect_handler = h;
|
||||||
}
|
}
|
||||||
@@ -80,21 +80,6 @@ const char *sl_sock_hresult2str(sl_sock_hresult_e r){
|
|||||||
return resmessages[r];
|
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
|
* @brief sl_sock_delete - close socket and delete descriptor
|
||||||
* @param sock - pointer to socket descriptor
|
* @param sock - pointer to socket descriptor
|
||||||
@@ -104,8 +89,8 @@ void sl_sock_delete(sl_sock_t **sock){
|
|||||||
sl_sock_t *ptr = *sock;
|
sl_sock_t *ptr = *sock;
|
||||||
ptr->connected = 0;
|
ptr->connected = 0;
|
||||||
if(ptr->rthread){
|
if(ptr->rthread){
|
||||||
DBG("Cancel thread");
|
DBG("Join thread");
|
||||||
pthread_cancel(ptr->rthread);
|
pthread_join(ptr->rthread, NULL);
|
||||||
}
|
}
|
||||||
DBG("close fd=%d", ptr->fd);
|
DBG("close fd=%d", ptr->fd);
|
||||||
if(ptr->fd > -1) close(ptr->fd);
|
if(ptr->fd > -1) close(ptr->fd);
|
||||||
@@ -151,12 +136,13 @@ static void *clientrbthread(void *d){
|
|||||||
do{
|
do{
|
||||||
ssize_t written = sl_RB_write(s->buffer, (uint8_t*)buf + got, n-got);
|
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);
|
//DBG("Put %zd to buffer, got=%zd, n=%zd", written, got, n);
|
||||||
if(got > n) return NULL;
|
if(got > n) goto errex;
|
||||||
if(written > 0) got += written;
|
if(written > 0) got += written;
|
||||||
}while(got != n);
|
}while(got != n);
|
||||||
//DBG("All messsages done");
|
//DBG("All messsages done");
|
||||||
}
|
}
|
||||||
errex:
|
errex:
|
||||||
|
FREE(buf);
|
||||||
s->rthread = 0;
|
s->rthread = 0;
|
||||||
s->connected = FALSE;
|
s->connected = FALSE;
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -274,6 +260,7 @@ void url_decode(char *str) {
|
|||||||
|
|
||||||
static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str);
|
static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str);
|
||||||
|
|
||||||
|
// TODO: handle Content-Length correctly
|
||||||
// parser of web-encoded data by POST/GET:
|
// parser of web-encoded data by POST/GET:
|
||||||
static sl_sock_hresult_e parse_post_data(sl_sock_t *c, char *str){
|
static sl_sock_hresult_e parse_post_data(sl_sock_t *c, char *str){
|
||||||
if (!c || !str) return RESULT_BADKEY;
|
if (!c || !str) return RESULT_BADKEY;
|
||||||
@@ -345,10 +332,59 @@ static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
|
|||||||
sl_sock_sendbyte(client, '\n');
|
sl_sock_sendbyte(client, '\n');
|
||||||
return RESULT_SILENCE;
|
return RESULT_SILENCE;
|
||||||
}
|
}
|
||||||
|
// check for strict params like `key=val`
|
||||||
for(sl_sock_hitem_t *h = client->handlers; h->handler; ++h){
|
for(sl_sock_hitem_t *h = client->handlers; h->handler; ++h){
|
||||||
if(strcmp(h->key, key)) continue;
|
if(strcmp(h->key, key)) continue;
|
||||||
|
if(h->data){
|
||||||
|
sl_sock_keyno_t *kn = (sl_sock_keyno_t*)h->data;
|
||||||
|
if(-1 == isinf(kn->magick)) kn->n = -1; // no value number
|
||||||
|
}
|
||||||
return h->handler(client, h, valptr);
|
return h->handler(client, h, valptr);
|
||||||
}
|
}
|
||||||
|
// now check for optional key's number like key0=val, key[1]=val, key(2)=val or key{3}=val
|
||||||
|
int keylen = strlen(key);
|
||||||
|
char *numstart = NULL;
|
||||||
|
const char *bra = "([{", *ket = ")]}";
|
||||||
|
char *found = strchr((char*)ket, key[keylen - 1]);
|
||||||
|
DBG("found=%s", found);
|
||||||
|
if(found){
|
||||||
|
char *keyend = strchr(key, bra[found - ket]); // find starting bracket
|
||||||
|
DBG("keyend=%s", keyend);
|
||||||
|
if(keyend){
|
||||||
|
numstart = keyend + 1;
|
||||||
|
keylen = keyend - key;
|
||||||
|
}
|
||||||
|
}else{ // maybe this is key123=val ?
|
||||||
|
numstart = &key[keylen-1];
|
||||||
|
while(numstart > key && isdigit(*numstart)) --numstart;
|
||||||
|
if(numstart == &key[keylen-1]) numstart = NULL;
|
||||||
|
else{
|
||||||
|
keylen = (++numstart) - key;
|
||||||
|
DBG("numstart=%s", numstart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(numstart){
|
||||||
|
char *eptr;
|
||||||
|
long long LL = strtoll(numstart, &eptr, 0);
|
||||||
|
DBG("LL=%lld, len=%d", LL, keylen);
|
||||||
|
if(eptr != numstart && LL >= 0 && LL <= INT_MAX){
|
||||||
|
int parno = (int)LL;
|
||||||
|
for(sl_sock_hitem_t *h = client->handlers; h->handler; ++h){
|
||||||
|
if(strncmp(h->key, key, keylen)) continue; // now search only in first `keylen` bytes
|
||||||
|
DBG("found %s", h->key);
|
||||||
|
if(h->data){
|
||||||
|
sl_sock_keyno_t *kn = (sl_sock_keyno_t*)h->data;
|
||||||
|
if(-1 == isinf(kn->magick)){
|
||||||
|
kn->n = parno;
|
||||||
|
DBG("run handler, parno=%d", parno);
|
||||||
|
return h->handler(client, h, valptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBG("NO data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if(client->defmsg_handler) return client->defmsg_handler(client, str);
|
if(client->defmsg_handler) return client->defmsg_handler(client, str);
|
||||||
return RESULT_BADKEY;
|
return RESULT_BADKEY;
|
||||||
}
|
}
|
||||||
@@ -413,7 +449,9 @@ static void *serverthread(void _U_ *d){
|
|||||||
// ZERO - listening server socket
|
// ZERO - listening server socket
|
||||||
poll_set[0].fd = sockfd;
|
poll_set[0].fd = sockfd;
|
||||||
poll_set[0].events = POLLIN;
|
poll_set[0].events = POLLIN;
|
||||||
// disconnect client
|
// disconnect client (no way to make this function non-nested)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
void disconnect_(sl_sock_t *c, int N){
|
void disconnect_(sl_sock_t *c, int N){
|
||||||
DBG("Disconnect client \"%s\" (fd=%d)", c->IP, c->fd);
|
DBG("Disconnect client \"%s\" (fd=%d)", c->IP, c->fd);
|
||||||
if(s->disconnect_handler) s->disconnect_handler(c);
|
if(s->disconnect_handler) s->disconnect_handler(c);
|
||||||
@@ -426,6 +464,8 @@ static void *serverthread(void _U_ *d){
|
|||||||
c->lineno = 0;
|
c->lineno = 0;
|
||||||
c->gotemptyline = 0;
|
c->gotemptyline = 0;
|
||||||
sl_RB_clearbuf(c->buffer);
|
sl_RB_clearbuf(c->buffer);
|
||||||
|
DBG("unlock fd=%d", c->fd);
|
||||||
|
pthread_mutex_unlock(&c->mutex);
|
||||||
// now move all data of last client to disconnected
|
// now move all data of last client to disconnected
|
||||||
if(nfd > 2 && N != nfd - 1){ // don't move the only or the last
|
if(nfd > 2 && N != nfd - 1){ // don't move the only or the last
|
||||||
DBG("lock fd=%d", clients[nfd-1]->fd);
|
DBG("lock fd=%d", clients[nfd-1]->fd);
|
||||||
@@ -436,10 +476,9 @@ static void *serverthread(void _U_ *d){
|
|||||||
pthread_mutex_unlock(&clients[N]->mutex); // now N is nfd-1
|
pthread_mutex_unlock(&clients[N]->mutex); // now N is nfd-1
|
||||||
poll_set[N] = poll_set[nfd - 1];
|
poll_set[N] = poll_set[nfd - 1];
|
||||||
}
|
}
|
||||||
DBG("unlock fd=%d", c->fd);
|
|
||||||
pthread_mutex_unlock(&c->mutex);
|
|
||||||
--nfd;
|
--nfd;
|
||||||
}
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
// allocate buffer with size not less than RB size
|
// allocate buffer with size not less than RB size
|
||||||
size_t bufsize = s->buffer->length; // as RB should be 1 byte less, this is OK
|
size_t bufsize = s->buffer->length; // as RB should be 1 byte less, this is OK
|
||||||
uint8_t *buf = MALLOC(uint8_t, bufsize);
|
uint8_t *buf = MALLOC(uint8_t, bufsize);
|
||||||
@@ -576,98 +615,111 @@ errex:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert UNIX socket name for unaddr; result should be free'd
|
||||||
|
static char *convunsname(const char *path, socklen_t *nbytes){
|
||||||
|
char *apath = MALLOC(char, UNIX_SOCK_PATH_MAX);
|
||||||
|
socklen_t len = 0;
|
||||||
|
if(*path == 0 || *path == '@'){
|
||||||
|
DBG("convert name starting from %s", (*path == 0) ? "\\0" : "@");
|
||||||
|
apath[0] = 0;
|
||||||
|
strncpy(apath+1, path+1, UNIX_SOCK_PATH_MAX-2);
|
||||||
|
len = 1 + strlen(path+1);
|
||||||
|
}else if(strncmp("\\0", path, 2) == 0){
|
||||||
|
DBG("convert name starting from \\0");
|
||||||
|
apath[0] = 0;
|
||||||
|
strncpy(apath+1, path+2, UNIX_SOCK_PATH_MAX-2);
|
||||||
|
len = 1 + strlen(path+2);
|
||||||
|
}else{
|
||||||
|
strncpy(apath, path, UNIX_SOCK_PATH_MAX-1);
|
||||||
|
len = strlen(path);
|
||||||
|
}
|
||||||
|
if(nbytes) *nbytes = len + sizeof(sa_family_t); // amount of bytes for `bind()` (parameter `len`)
|
||||||
|
return apath;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief sl_sock_open - open socket (client or server)
|
* @brief mknodeservice - break `path` into node name and port for INET socket
|
||||||
* @param type - socket type
|
* @param path (i) - path like "node:service"
|
||||||
* @param path - path (for UNIX socket); for NET: ":port" for server, "server:port" for client
|
* @param node (o) - pointer to `char *` - node name
|
||||||
* @param handlers - standard handlers when read data (or NULL)
|
* @param service (o) - pointer to `char *` - port ("service")
|
||||||
* @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){
|
static void mknodeservice(const char *path, char **node, char **service){
|
||||||
if(!path || type >= SOCKT_AMOUNT) return NULL;
|
if(!path || !node || !service) return;
|
||||||
if(bufsiz < 256) bufsiz = 256;
|
char *delim = strchr((char*)path, ':');
|
||||||
|
if(!delim) *service = strdup(path); // only port
|
||||||
|
else{
|
||||||
|
if(delim == path) *service = strdup(path+1);
|
||||||
|
else{
|
||||||
|
size_t l = delim - path;
|
||||||
|
*node = MALLOC(char, l + 1);
|
||||||
|
strncpy(*node, path, l);
|
||||||
|
*service = strdup(delim + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief sl_sock_open - open socket, run bind or connect and return its file descriptor
|
||||||
|
* @param type - socket type
|
||||||
|
* @param path - path to UNIX-socket, port or "host:port" for INET-socket
|
||||||
|
* @param isserver - 1 for server, 0 for client
|
||||||
|
* @param socktype - custom socket type or "<1" for default (SOCK_STREAM)
|
||||||
|
* @return file descriptor or -1 if can't open
|
||||||
|
*/
|
||||||
|
int sl_sock_open(sl_socktype_e type, const char *path, int isserver, int ai_socktype){
|
||||||
|
FNAME();
|
||||||
|
if(!path || type >= SOCKT_AMOUNT) return -1;
|
||||||
|
if(ai_socktype < 1) ai_socktype = SOCK_STREAM;
|
||||||
int sock = -1;
|
int sock = -1;
|
||||||
struct addrinfo ai = {0}, *res = &ai;
|
struct addrinfo ai = {0}, *res = &ai;
|
||||||
struct sockaddr_un unaddr = {0};
|
struct sockaddr_un unaddr = {0};
|
||||||
char *str = NULL;
|
ai.ai_socktype = ai_socktype;
|
||||||
ai.ai_socktype = SOCK_STREAM;
|
|
||||||
switch(type){
|
switch(type){
|
||||||
case SOCKT_UNIX:
|
case SOCKT_UNIX:
|
||||||
str = convunsname(path);
|
{
|
||||||
if(!str) return NULL;
|
DBG("SOCKT_UNIX");
|
||||||
unaddr.sun_family = AF_UNIX;
|
char *str = convunsname(path, &ai.ai_addrlen);
|
||||||
ai.ai_addr = (struct sockaddr*) &unaddr;
|
DBG("path+1: %s", str+1);
|
||||||
ai.ai_addrlen = sizeof(unaddr);
|
if(!str) return -1;
|
||||||
memcpy(unaddr.sun_path, str, 106);
|
unaddr.sun_family = AF_UNIX;
|
||||||
ai.ai_family = AF_UNIX;
|
ai.ai_addr = (struct sockaddr*) &unaddr;
|
||||||
//ai.ai_socktype = SOCK_SEQPACKET;
|
memcpy(unaddr.sun_path, str, UNIX_SOCK_PATH_MAX);
|
||||||
break;
|
FREE(str); // don't forget!
|
||||||
case SOCKT_NET:
|
ai.ai_family = AF_UNIX;
|
||||||
case SOCKT_NETLOCAL:
|
//ai.ai_socktype = SOCK_SEQPACKET;
|
||||||
//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);
|
break;
|
||||||
s->type = type;
|
case SOCKT_NET:
|
||||||
s->fd = -1;
|
case SOCKT_NETLOCAL:
|
||||||
s->maxclients = SL_DEF_MAXCLIENTS;
|
{
|
||||||
s->handlers = handlers;
|
DBG("SOCKT_%s", (type == SOCKT_NET) ? "NET" : "NETLOCAL");
|
||||||
s->buffer = sl_RB_new(bufsiz);
|
char *node = NULL, *service = NULL;
|
||||||
if(!s->buffer){
|
mknodeservice(path, &node, &service);
|
||||||
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
|
|
||||||
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);
|
|
||||||
if(isserver){
|
if(isserver){
|
||||||
if(!s->node){
|
ai.ai_flags = AI_PASSIVE;
|
||||||
DBG("Socket type now is SOCKT_NET");
|
if(type == SOCKT_NETLOCAL){
|
||||||
s->type = SOCKT_NET;
|
FREE(node);
|
||||||
}
|
node = strdup("127.0.0.1");
|
||||||
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'", s->node, s->service);
|
ai.ai_family = AF_UNSPEC; // not AF_INET for client as there maybe problems with IPv6
|
||||||
int e = getaddrinfo(s->node, s->service, &ai, &res);
|
int e = getaddrinfo(node, service, &ai, &res);
|
||||||
if(e){
|
if(e){
|
||||||
WARNX("getaddrinfo(): %s", gai_strerror(e));
|
WARNX("getaddrinfo(): %s", gai_strerror(e));
|
||||||
sl_sock_delete(&s);
|
return -1;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
FREE(node);
|
||||||
|
FREE(service);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: // never reached
|
||||||
|
WARNX(_("Unsupported socket type %d"), type);
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
for(struct addrinfo *p = res; p; p = p->ai_next){
|
for(struct addrinfo *p = res; p; p = p->ai_next){
|
||||||
if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) continue;
|
if((sock = socket(p->ai_family, p->ai_socktype/*|SOCK_NONBLOCK*/, p->ai_protocol)) < 0) continue;
|
||||||
DBG("Try proto %d, type %d", p->ai_protocol, p->ai_socktype);
|
DBG("Try proto %d, type %d, socktype %d", p->ai_protocol, p->ai_socktype, p->ai_socktype);
|
||||||
if(isserver){
|
if(isserver){
|
||||||
int reuseaddr = 1;
|
int reuseaddr = 1;
|
||||||
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){
|
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){
|
||||||
@@ -688,30 +740,80 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
|
|||||||
if(connect(sock, p->ai_addr, p->ai_addrlen) == -1){
|
if(connect(sock, p->ai_addr, p->ai_addrlen) == -1){
|
||||||
WARN("connect()");
|
WARN("connect()");
|
||||||
close(sock); sock = -1;
|
close(sock); sock = -1;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(sock < 0) sl_sock_delete(&s);
|
if(type != SOCKT_UNIX) freeaddrinfo(res); // don't forget to free memory allocated with getaddrinfo
|
||||||
else{
|
return sock;
|
||||||
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;
|
* @brief sl_sock_run - open socket and run thread (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_run(sl_socktype_e type, const char *path, sl_sock_hitem_t *handlers, int bufsiz, int isserver){
|
||||||
|
FNAME();
|
||||||
|
if(bufsiz < 256) bufsiz = 256;
|
||||||
|
int sock = sl_sock_open(type, path, isserver, 0);
|
||||||
|
if(sock < 0) return NULL;
|
||||||
|
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);
|
||||||
|
return NULL;
|
||||||
|
}else{ // fill node/service
|
||||||
|
if(type == SOCKT_UNIX) s->node = strdup(path); // str now is path copy
|
||||||
|
else{
|
||||||
|
mknodeservice(path, &s->node, &s->service);
|
||||||
|
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);
|
||||||
if(isserver){
|
if(isserver){
|
||||||
if(s->handlers || s->defmsg_handler)
|
if(!s->node){
|
||||||
r = pthread_create(&s->rthread, NULL, serverthread, (void*)s);
|
DBG("Socket type now is SOCKT_NET");
|
||||||
else r = 0;
|
s->type = SOCKT_NET;
|
||||||
}else{
|
}
|
||||||
r = pthread_create(&s->rthread, NULL, clientrbthread, (void*)s);
|
if(s->type == SOCKT_NETLOCAL){
|
||||||
}
|
DBG("SOCKT_NETLOCAL: change `node` to localhost");
|
||||||
if(r){
|
FREE(s->node);
|
||||||
WARN("pthread_create()");
|
s->node = strdup("127.0.0.1"); // localhost
|
||||||
sl_sock_delete(&s);
|
}
|
||||||
}else{
|
|
||||||
s->connected = TRUE;
|
|
||||||
DBG("fd=%d CONNECTED", s->fd);
|
|
||||||
}
|
}
|
||||||
|
DBG("---> node '%s', service '%s'", s->node, s->service);
|
||||||
|
}
|
||||||
|
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 || s->defmsg_handler)
|
||||||
|
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;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -725,7 +827,7 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
|
|||||||
* @return socket descriptor or NULL if failed
|
* @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 *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);
|
sl_sock_t *s = sl_sock_run(type, path, NULL, bufsiz, 0);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,7 +840,7 @@ sl_sock_t *sl_sock_run_client(sl_socktype_e type, const char *path, int bufsiz){
|
|||||||
* @return socket descriptor or NULL if failed
|
* @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 *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);
|
sl_sock_t *s = sl_sock_run(type, path, handlers, bufsiz, 1);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -761,7 +863,7 @@ ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l){
|
|||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
DBG("send to fd=%d message with len=%zd (%s)", socket->fd, l, msg);
|
DBG("send to fd=%d message with len=%zd (%s)", socket->fd, l, msg);
|
||||||
while(socket && socket->connected && !sl_canwrite(socket->fd));
|
while(socket && socket->connected && 1 != sl_canwrite(socket->fd));
|
||||||
if(!socket || !socket->connected) return -1;
|
if(!socket || !socket->connected) return -1;
|
||||||
DBG("lock");
|
DBG("lock");
|
||||||
pthread_mutex_lock(&socket->mutex);
|
pthread_mutex_lock(&socket->mutex);
|
||||||
@@ -826,6 +928,7 @@ ssize_t sl_sock_readline(sl_sock_t *sock, char *str, 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_inthandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str){
|
||||||
char buf[128];
|
char buf[128];
|
||||||
sl_sock_int_t *i = (sl_sock_int_t *)hitem->data;
|
sl_sock_int_t *i = (sl_sock_int_t *)hitem->data;
|
||||||
|
if(!i) return RESULT_FAIL;
|
||||||
if(!str){ // getter
|
if(!str){ // getter
|
||||||
snprintf(buf, 127, "%s=%" PRId64 "\n", hitem->key, i->val);
|
snprintf(buf, 127, "%s=%" PRId64 "\n", hitem->key, i->val);
|
||||||
sl_sock_sendstrmessage(client, buf);
|
sl_sock_sendstrmessage(client, buf);
|
||||||
@@ -841,6 +944,7 @@ sl_sock_hresult_e sl_sock_inthandler(sl_sock_t *client, sl_sock_hitem_t *hitem,
|
|||||||
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_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str){
|
||||||
char buf[128];
|
char buf[128];
|
||||||
sl_sock_double_t *d = (sl_sock_double_t *)hitem->data;
|
sl_sock_double_t *d = (sl_sock_double_t *)hitem->data;
|
||||||
|
if(!d) return RESULT_FAIL;
|
||||||
if(!str){ // getter
|
if(!str){ // getter
|
||||||
snprintf(buf, 127, "%s=%g\n", hitem->key, d->val);
|
snprintf(buf, 127, "%s=%g\n", hitem->key, d->val);
|
||||||
sl_sock_sendstrmessage(client, buf);
|
sl_sock_sendstrmessage(client, buf);
|
||||||
@@ -855,6 +959,7 @@ sl_sock_hresult_e sl_sock_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem,
|
|||||||
sl_sock_hresult_e sl_sock_strhandler(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){
|
||||||
char buf[SL_VAL_LEN + SL_KEY_LEN + 3];
|
char buf[SL_VAL_LEN + SL_KEY_LEN + 3];
|
||||||
sl_sock_string_t *s = (sl_sock_string_t*) hitem->data;
|
sl_sock_string_t *s = (sl_sock_string_t*) hitem->data;
|
||||||
|
if(!s) return RESULT_FAIL;
|
||||||
if(!str){ // getter
|
if(!str){ // getter
|
||||||
snprintf(buf, SL_VAL_LEN + SL_KEY_LEN + 2, "%s=%s\n", hitem->key, s->val);
|
snprintf(buf, SL_VAL_LEN + SL_KEY_LEN + 2, "%s=%s\n", hitem->key, s->val);
|
||||||
sl_sock_sendstrmessage(client, buf);
|
sl_sock_sendstrmessage(client, buf);
|
||||||
@@ -868,3 +973,24 @@ sl_sock_hresult_e sl_sock_strhandler(sl_sock_t *client, sl_sock_hitem_t *hitem,
|
|||||||
s->val[l] = 0;
|
s->val[l] = 0;
|
||||||
return RESULT_OK;
|
return RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief sl_sock_keyno_init - init k->magick and k-> to default values
|
||||||
|
* @param k - key's number value
|
||||||
|
*/
|
||||||
|
void sl_sock_keyno_init(sl_sock_keyno_t* k){
|
||||||
|
if(!k) return;
|
||||||
|
k->magick = -INFINITY;
|
||||||
|
k->n = -1;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief sl_sock_keyno_check - check if this is a really `sl_sock_keyno_t`
|
||||||
|
* @param k - pointer to check
|
||||||
|
* @return k.n or -1 if failed
|
||||||
|
*/
|
||||||
|
int sl_sock_keyno_check(sl_sock_keyno_t* k){
|
||||||
|
DBG("magick=%g (%d)", k->magick, isinf(k->magick));
|
||||||
|
if(!k || -1 != isinf(k->magick)) return -1;
|
||||||
|
DBG("k->n=%d", k->n);
|
||||||
|
return k->n;
|
||||||
|
}
|
||||||
|
|||||||
254
term.c
254
term.c
@@ -1,254 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Snippets project.
|
|
||||||
* Copyright 2013 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <unistd.h> // tcsetattr, close, read, write
|
|
||||||
#include <sys/ioctl.h> // ioctl
|
|
||||||
#include <stdio.h> // printf, getchar, fopen, perror
|
|
||||||
#include <stdlib.h> // exit, realloc
|
|
||||||
#include <sys/stat.h> // read
|
|
||||||
#include <fcntl.h> // read
|
|
||||||
#include <signal.h> // signal
|
|
||||||
#include <time.h> // time
|
|
||||||
#include <string.h> // memcpy
|
|
||||||
#include <stdint.h> // int types
|
|
||||||
#include <sys/time.h> // gettimeofday
|
|
||||||
#include <unistd.h> // usleep
|
|
||||||
#include "usefull_macros.h"
|
|
||||||
|
|
||||||
#define LOGBUFSZ (1024)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int speed; // communication speed in bauds/s
|
|
||||||
tcflag_t bspeed; // baudrate from termios.h
|
|
||||||
} spdtbl;
|
|
||||||
|
|
||||||
static int tty_init(sl_tty_t *descr);
|
|
||||||
|
|
||||||
static spdtbl speeds[] = {
|
|
||||||
{50, B50},
|
|
||||||
{75, B75},
|
|
||||||
{110, B110},
|
|
||||||
{134, B134},
|
|
||||||
{150, B150},
|
|
||||||
{200, B200},
|
|
||||||
{300, B300},
|
|
||||||
{600, B600},
|
|
||||||
{1200, B1200},
|
|
||||||
{1800, B1800},
|
|
||||||
{2400, B2400},
|
|
||||||
{4800, B4800},
|
|
||||||
{9600, B9600},
|
|
||||||
{19200, B19200},
|
|
||||||
{38400, B38400},
|
|
||||||
{57600, B57600},
|
|
||||||
{115200, B115200},
|
|
||||||
{230400, B230400},
|
|
||||||
{460800, B460800},
|
|
||||||
{500000, B500000},
|
|
||||||
{576000, B576000},
|
|
||||||
{921600, B921600},
|
|
||||||
{1000000, B1000000},
|
|
||||||
{1152000, B1152000},
|
|
||||||
{1500000, B1500000},
|
|
||||||
{2000000, B2000000},
|
|
||||||
{2500000, B2500000},
|
|
||||||
{3000000, B3000000},
|
|
||||||
{3500000, B3500000},
|
|
||||||
{4000000, B4000000},
|
|
||||||
{0,0}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief sl_tty_convspd - test if `speed` is in .speed of `speeds` array
|
|
||||||
* @param speed - integer speed (bps)
|
|
||||||
* @return 0 if error, Bxxx if all OK
|
|
||||||
*/
|
|
||||||
tcflag_t sl_tty_convspd(int speed){
|
|
||||||
spdtbl *spd = speeds;
|
|
||||||
int curspeed = 0;
|
|
||||||
do{
|
|
||||||
curspeed = spd->speed;
|
|
||||||
if(curspeed == speed)
|
|
||||||
return spd->bspeed;
|
|
||||||
++spd;
|
|
||||||
}while(curspeed);
|
|
||||||
WARNX(_("Wrong speed value: %d!"), speed);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief tty_init - open & setup terminal
|
|
||||||
* @param descr (io) - port descriptor
|
|
||||||
* @return 0 if all OK or error code
|
|
||||||
*/
|
|
||||||
static int tty_init(sl_tty_t *descr){
|
|
||||||
// |O_NONBLOCK ?
|
|
||||||
if ((descr->comfd = open(descr->portname, O_RDWR|O_NOCTTY)) < 0){
|
|
||||||
WARN(_("Can't use port %s"), descr->portname);
|
|
||||||
return globErr ? globErr : 1;
|
|
||||||
}
|
|
||||||
if(tcgetattr(descr->comfd, &descr->oldtty) < 0){ // Get settings
|
|
||||||
WARN(_("Can't get old TTY settings"));
|
|
||||||
return globErr ? globErr : 1;
|
|
||||||
}
|
|
||||||
descr->tty = descr->oldtty;
|
|
||||||
descr->tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
|
||||||
descr->tty.c_iflag = 0;
|
|
||||||
descr->tty.c_oflag = 0;
|
|
||||||
descr->tty.c_cflag = descr->baudrate|CS8|CREAD|CLOCAL; // 9.6k, 8N1, RW, ignore line ctrl
|
|
||||||
descr->tty.c_cc[VMIN] = 0; // non-canonical mode
|
|
||||||
descr->tty.c_cc[VTIME] = 5;
|
|
||||||
if(tcsetattr(descr->comfd, TCSANOW, &descr->tty) < 0){
|
|
||||||
WARN(_("Can't apply new TTY settings"));
|
|
||||||
return globErr ? globErr : 1;
|
|
||||||
}
|
|
||||||
// make exclusive open
|
|
||||||
if(descr->exclusive){
|
|
||||||
if(ioctl(descr->comfd, TIOCEXCL)){
|
|
||||||
WARN(_("Can't do exclusive open"));
|
|
||||||
}}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief restore_tty - restore opened TTY to previous state and close it
|
|
||||||
*/
|
|
||||||
void sl_tty_close(sl_tty_t **descr){
|
|
||||||
if(descr == NULL || *descr == NULL) return;
|
|
||||||
sl_tty_t *d = *descr;
|
|
||||||
if(d->comfd){
|
|
||||||
ioctl(d->comfd, TCSANOW, &d->oldtty); // return TTY to previous state
|
|
||||||
close(d->comfd);
|
|
||||||
}
|
|
||||||
FREE(d->portname);
|
|
||||||
FREE(d->buf);
|
|
||||||
FREE(*descr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief sl_tty_new - create new TTY structure with partially filled fields
|
|
||||||
* @param comdev - TTY device filename
|
|
||||||
* @param speed - speed (number)
|
|
||||||
* @param bufsz - size of buffer for input data (or 0 if opened only to write)
|
|
||||||
* @return pointer to TTY structure if all OK
|
|
||||||
*/
|
|
||||||
sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz){
|
|
||||||
tcflag_t spd = sl_tty_convspd(speed);
|
|
||||||
if(!spd) return NULL;
|
|
||||||
sl_tty_t *descr = MALLOC(sl_tty_t, 1);
|
|
||||||
descr->portname = strdup(comdev);
|
|
||||||
descr->baudrate = spd;
|
|
||||||
descr->speed = speed;
|
|
||||||
if(!descr->portname){
|
|
||||||
WARNX(_("Port name is missing"));
|
|
||||||
}else{
|
|
||||||
if(bufsz){
|
|
||||||
descr->buf = MALLOC(char, bufsz+1);
|
|
||||||
descr->bufsz = bufsz;
|
|
||||||
return descr;
|
|
||||||
}else WARNX(_("Need non-zero buffer for TTY device"));
|
|
||||||
}
|
|
||||||
FREE(descr->portname);
|
|
||||||
FREE(descr);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief sl_tty_open - init & open tty device
|
|
||||||
* @param d - already filled structure (with new_tty or by hands)
|
|
||||||
* @param exclusive - == 1 to make exclusive open
|
|
||||||
* @return pointer to TTY structure if all OK
|
|
||||||
*/
|
|
||||||
sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive){
|
|
||||||
if(!d || !d->portname || !d->baudrate) return NULL;
|
|
||||||
if(exclusive) d->exclusive = TRUE;
|
|
||||||
else d->exclusive = FALSE;
|
|
||||||
if(tty_init(d)) return NULL;
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct timeval tvdefault = {.tv_sec = 0, .tv_usec = 5000};
|
|
||||||
/**
|
|
||||||
* @brief sl_tty_tmout - set timeout for select() on reading
|
|
||||||
* @param usec - microseconds of timeout
|
|
||||||
* @return -1 if usec < 0, 0 if all OK
|
|
||||||
*/
|
|
||||||
int sl_tty_tmout(double usec){
|
|
||||||
if(usec < 0.) return -1;
|
|
||||||
tvdefault.tv_sec = 0;
|
|
||||||
if(usec > 999999){
|
|
||||||
tvdefault.tv_sec = (__time_t)(usec / 1e6);
|
|
||||||
usec -= tvdefault.tv_sec * 1e6;
|
|
||||||
}
|
|
||||||
tvdefault.tv_usec = (__suseconds_t) usec;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief sl_tty_read - read data from TTY with 10ms timeout
|
|
||||||
* @param buff (o) - buffer for data read
|
|
||||||
* @param length - buffer len
|
|
||||||
* @return amount of bytes read or -1 if disconnected
|
|
||||||
*/
|
|
||||||
int sl_tty_read(sl_tty_t *d){
|
|
||||||
if(!d || d->comfd < 0) return 0;
|
|
||||||
size_t L = 0;
|
|
||||||
ssize_t l;
|
|
||||||
size_t length = d->bufsz;
|
|
||||||
char *ptr = d->buf;
|
|
||||||
fd_set rfds;
|
|
||||||
struct timeval tv;
|
|
||||||
int retval;
|
|
||||||
do{
|
|
||||||
l = 0;
|
|
||||||
FD_ZERO(&rfds);
|
|
||||||
FD_SET(d->comfd, &rfds);
|
|
||||||
//memcpy(&tv, &tvdefault, sizeof(struct timeval));
|
|
||||||
tv = tvdefault;
|
|
||||||
retval = select(d->comfd + 1, &rfds, NULL, NULL, &tv);
|
|
||||||
if(!retval) break;
|
|
||||||
if(retval < 0){
|
|
||||||
if(errno == EINTR) continue;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(FD_ISSET(d->comfd, &rfds)){
|
|
||||||
l = read(d->comfd, ptr, length);
|
|
||||||
if(l < 1) return -1; // disconnected
|
|
||||||
ptr += l; L += l;
|
|
||||||
length -= l;
|
|
||||||
}
|
|
||||||
}while(l && length);
|
|
||||||
d->buflen = L;
|
|
||||||
d->buf[L] = 0;
|
|
||||||
return (size_t)L;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief sl_tty_write - write data to serial port
|
|
||||||
* @param buff (i) - data to write
|
|
||||||
* @param length - its length
|
|
||||||
* @return 0 if all OK
|
|
||||||
*/
|
|
||||||
int sl_tty_write(int comfd, const char *buff, size_t length){
|
|
||||||
ssize_t L = write(comfd, buff, length);
|
|
||||||
if((size_t)L != length){
|
|
||||||
WARN("Write error");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
34
term.h
34
term.h
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* term.h
|
|
||||||
*
|
|
||||||
* Copyright 2016 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#ifndef __TERM_H__
|
|
||||||
#define __TERM_H__
|
|
||||||
|
|
||||||
#include <termios.h> // tcsetattr, baudrates
|
|
||||||
|
|
||||||
void term_quit(int ex_stat);
|
|
||||||
int conv_spd(int speed);
|
|
||||||
void ttys_open(char **ports, int **speeds, int globspeed);
|
|
||||||
|
|
||||||
void set_comlogname(char* nm);
|
|
||||||
void set_charmode();
|
|
||||||
|
|
||||||
#endif // __TERM_H__
|
|
||||||
113
term2.c
113
term2.c
@@ -89,47 +89,57 @@ someerr:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief tty_init - open & setup terminal
|
* @brief sl_tty_fdescr - open terminal device and give it's file descriptor
|
||||||
* @param descr (io) - port descriptor
|
* @param comdev - path to device
|
||||||
* !!! default settings are "8N1".
|
* @param speed - baudrate
|
||||||
* !!! If you want other, set it like `descr->format = "7O2"` between `sl_tty_new` and `sl_tty_open`
|
* @param exclusive - ==1 for exclusive open
|
||||||
* @return 0 if all OK, last error code or 1
|
* @return fd or -1 in case of error
|
||||||
*/
|
*/
|
||||||
static int tty_init(sl_tty_t *descr){
|
int sl_tty_fdescr(const char *comdev, const char *format, int speed, int exclusive){
|
||||||
|
if(!comdev || speed < 1) return -1;
|
||||||
tcflag_t flags;
|
tcflag_t flags;
|
||||||
if(!parse_format(descr->format, &flags)) return 1;
|
if(!parse_format(format, &flags)) return -1;
|
||||||
if((descr->comfd = open(descr->portname, O_RDWR|O_NOCTTY)) < 0){
|
DBG("open");
|
||||||
WARN(_("Can't use port %s"), descr->portname);
|
int comfd = open(comdev, O_RDWR|O_NOCTTY);
|
||||||
return globErr ? globErr : 1;
|
if(comfd < 0){
|
||||||
|
WARN(_("Can't use port %s"), comdev);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
if(ioctl(descr->comfd, TCGETS2, &descr->oldtty)){ // Get settings
|
DBG("fd=%d", comfd);
|
||||||
|
struct termios2 newtty;
|
||||||
|
if(ioctl(comfd, TCGETS2, &newtty)){ // Get settings
|
||||||
WARN(_("Can't get old TTY settings"));
|
WARN(_("Can't get old TTY settings"));
|
||||||
return globErr ? globErr : 1;
|
return -1;
|
||||||
}
|
}
|
||||||
descr->tty = descr->oldtty;
|
DBG("change termios flags");
|
||||||
descr->tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
newtty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
||||||
descr->tty.c_iflag = 0; // don't do any changes in input stream
|
newtty.c_iflag = 0; // don't do any changes in input stream
|
||||||
descr->tty.c_oflag = 0; // don't do any changes in output stream
|
newtty.c_oflag = 0; // don't do any changes in output stream
|
||||||
descr->tty.c_cflag = BOTHER | flags | CREAD | CLOCAL; // other speed, user format, RW, ignore line ctrl
|
newtty.c_cflag = BOTHER | flags | CREAD | CLOCAL; // other speed, user format, RW, ignore line ctrl
|
||||||
descr->tty.c_ispeed = descr->speed;
|
newtty.c_ispeed = speed;
|
||||||
descr->tty.c_ospeed = descr->speed;
|
newtty.c_ospeed = speed;
|
||||||
descr->tty.c_cc[VMIN] = 0; // non-canonical mode
|
newtty.c_cc[VMIN] = 0; // non-canonical mode
|
||||||
descr->tty.c_cc[VTIME] = 5;
|
newtty.c_cc[VTIME] = 1;
|
||||||
if(ioctl(descr->comfd, TCSETS2, &descr->tty)){
|
if(ioctl(comfd, TCSETS2, &newtty)){
|
||||||
WARN(_("Can't apply new TTY settings"));
|
WARN(_("Can't apply new TTY settings"));
|
||||||
return globErr ? globErr : 1;
|
close(comfd);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
ioctl(descr->comfd, TCGETS2, &descr->tty);
|
DBG("OK, check");
|
||||||
if(descr->tty.c_ispeed != (speed_t)descr->speed || descr->tty.c_ospeed != (speed_t)descr->speed){
|
if(ioctl(comfd, TCGETS2, &newtty)){ // Get settings
|
||||||
WARN(_("Can't set speed %d, got ispeed=%d, ospeed=%d"), descr->speed, descr->tty.c_ispeed, descr->tty.c_ospeed);
|
WARN(_("Can't get old TTY settings"));
|
||||||
descr->speed = descr->tty.c_ispeed;
|
return -1;
|
||||||
}
|
}
|
||||||
// make exclusive open
|
if(exclusive){
|
||||||
if(descr->exclusive){
|
DBG("make exclusive");
|
||||||
if(ioctl(descr->comfd, TIOCEXCL)){
|
if(ioctl(comfd, TIOCEXCL)){
|
||||||
WARN(_("Can't do exclusive open"));
|
WARN(_("Can't do exclusive open"));
|
||||||
}}
|
close(comfd);
|
||||||
return 0;
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBG("device %s opened", comdev);
|
||||||
|
return comfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,13 +148,16 @@ static int tty_init(sl_tty_t *descr){
|
|||||||
void sl_tty_close(sl_tty_t **descr){
|
void sl_tty_close(sl_tty_t **descr){
|
||||||
if(descr == NULL || *descr == NULL) return;
|
if(descr == NULL || *descr == NULL) return;
|
||||||
sl_tty_t *d = *descr;
|
sl_tty_t *d = *descr;
|
||||||
if(d->comfd){
|
if(d->comfd > -1){
|
||||||
ioctl(d->comfd, TCSETS2, &d->oldtty); // return TTY to previous state
|
DBG("close");
|
||||||
close(d->comfd);
|
close(d->comfd);
|
||||||
}
|
}
|
||||||
|
DBG("Free mem");
|
||||||
FREE(d->portname);
|
FREE(d->portname);
|
||||||
FREE(d->buf);
|
FREE(d->buf);
|
||||||
|
FREE(d->format);
|
||||||
FREE(*descr);
|
FREE(*descr);
|
||||||
|
DBG("tty closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -164,6 +177,8 @@ sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz){
|
|||||||
if(bufsz){
|
if(bufsz){
|
||||||
descr->buf = MALLOC(char, bufsz+1);
|
descr->buf = MALLOC(char, bufsz+1);
|
||||||
descr->bufsz = bufsz;
|
descr->bufsz = bufsz;
|
||||||
|
descr->comfd = -1;
|
||||||
|
DBG("sl_tty_t created");
|
||||||
return descr;
|
return descr;
|
||||||
}else WARNX(_("Need non-zero buffer for TTY device"));
|
}else WARNX(_("Need non-zero buffer for TTY device"));
|
||||||
}
|
}
|
||||||
@@ -172,6 +187,20 @@ sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz){
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief sl_tty_setformat - set format for just created tty object
|
||||||
|
* @param d - descriptor created with sl_tty_new
|
||||||
|
* @param format - string like "7O2" (maybe NULL for default, 8N1)
|
||||||
|
* @return FALSE if format is wrong
|
||||||
|
*/
|
||||||
|
int sl_tty_setformat(sl_tty_t *d, const char *format){
|
||||||
|
if(!d) return FALSE;
|
||||||
|
if(!format) return TRUE; // default format
|
||||||
|
if(!parse_format(format, NULL)) return FALSE;
|
||||||
|
d->format = strdup(format);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief sl_tty_open - init & open tty device
|
* @brief sl_tty_open - init & open tty device
|
||||||
* @param d - already filled structure (with new_tty or by hands)
|
* @param d - already filled structure (with new_tty or by hands)
|
||||||
@@ -182,13 +211,27 @@ sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive){
|
|||||||
if(!d || !d->portname) return NULL;
|
if(!d || !d->portname) return NULL;
|
||||||
if(exclusive) d->exclusive = TRUE;
|
if(exclusive) d->exclusive = TRUE;
|
||||||
else d->exclusive = FALSE;
|
else d->exclusive = FALSE;
|
||||||
if(tty_init(d)) return NULL;
|
DBG("ex: %d", exclusive);
|
||||||
|
int comfd = sl_tty_fdescr(d->portname, d->format, d->speed, d->exclusive);
|
||||||
|
if(comfd < 0) return NULL;
|
||||||
|
struct termios2 tty;
|
||||||
|
if(ioctl(comfd, TCGETS2, &tty)){ // Get settings
|
||||||
|
WARN(_("Can't get current TTY settings"));
|
||||||
|
}else{
|
||||||
|
if(d->speed != (int)tty.c_ispeed || d->speed != (int)tty.c_ospeed){
|
||||||
|
WARNX(_("Can't set exact speed %d"), d->speed);
|
||||||
|
d->speed = (int)tty.c_ispeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d->comfd = comfd;
|
||||||
|
DBG("sl_tty_t ready");
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct timeval tvdefault = {.tv_sec = 0, .tv_usec = 5000};
|
static struct timeval tvdefault = {.tv_sec = 0, .tv_usec = 5000};
|
||||||
/**
|
/**
|
||||||
* @brief sl_tty_tmout - set timeout for select() on reading
|
* @brief sl_tty_tmout - set timeout for select() on reading
|
||||||
|
* // WARNING! This function changes timeout for ALL opened ports!
|
||||||
* @param usec - microseconds of timeout
|
* @param usec - microseconds of timeout
|
||||||
* @return -1 if usec < 0, 0 if all OK
|
* @return -1 if usec < 0, 0 if all OK
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -308,22 +308,14 @@ char *sl_omitspacesr(const char *v){
|
|||||||
* BE CAREFULL! These functions aren't thread-safe!
|
* BE CAREFULL! These functions aren't thread-safe!
|
||||||
\******************************************************************************/
|
\******************************************************************************/
|
||||||
// console flags
|
// console flags
|
||||||
#ifndef SL_USE_OLD_TTY
|
|
||||||
static struct termios2 oldt, newt;
|
static struct termios2 oldt, newt;
|
||||||
#else
|
|
||||||
static struct termios oldt, newt;
|
|
||||||
#endif
|
|
||||||
static int console_changed = 0;
|
static int console_changed = 0;
|
||||||
/**
|
/**
|
||||||
* @brief sl_restore_con - restore console to default mode
|
* @brief sl_restore_con - restore console to default mode
|
||||||
*/
|
*/
|
||||||
void sl_restore_con(){
|
void sl_restore_con(){
|
||||||
if(console_changed)
|
if(console_changed)
|
||||||
#ifndef SL_USE_OLD_TTY
|
|
||||||
ioctl(STDIN_FILENO, TCSETS2, &oldt);
|
ioctl(STDIN_FILENO, TCSETS2, &oldt);
|
||||||
#else
|
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
|
|
||||||
#endif
|
|
||||||
console_changed = 0;
|
console_changed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,24 +324,12 @@ void sl_restore_con(){
|
|||||||
*/
|
*/
|
||||||
void sl_setup_con(){
|
void sl_setup_con(){
|
||||||
if(console_changed) return;
|
if(console_changed) return;
|
||||||
#ifndef SL_USE_OLD_TTY
|
|
||||||
ioctl(STDIN_FILENO, TCGETS2, &oldt);
|
ioctl(STDIN_FILENO, TCGETS2, &oldt);
|
||||||
#else
|
|
||||||
tcgetattr(STDIN_FILENO, &oldt);
|
|
||||||
#endif
|
|
||||||
newt = oldt;
|
newt = oldt;
|
||||||
newt.c_lflag &= ~(ICANON | ECHO);
|
newt.c_lflag &= ~(ICANON | ECHO);
|
||||||
#ifndef SL_USE_OLD_TTY
|
|
||||||
if(ioctl(STDIN_FILENO, TCSETS2, &newt)){
|
if(ioctl(STDIN_FILENO, TCSETS2, &newt)){
|
||||||
#else
|
|
||||||
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){
|
|
||||||
#endif
|
|
||||||
WARN(_("Can't setup console"));
|
WARN(_("Can't setup console"));
|
||||||
#ifndef SL_USE_OLD_TTY
|
|
||||||
ioctl(STDIN_FILENO, TCSETS2, &oldt);
|
ioctl(STDIN_FILENO, TCSETS2, &oldt);
|
||||||
#else
|
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
|
||||||
#endif
|
|
||||||
signals(1); //quit?
|
signals(1); //quit?
|
||||||
}
|
}
|
||||||
console_changed = 1;
|
console_changed = 1;
|
||||||
|
|||||||
@@ -22,11 +22,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#ifdef SL_USE_OLD_TTY
|
|
||||||
#include <termios.h> // termios
|
|
||||||
#else
|
|
||||||
#include <asm-generic/termbits.h>
|
#include <asm-generic/termbits.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <errno.h> // errno
|
#include <errno.h> // errno
|
||||||
#include <netdb.h> // struct addrinfo
|
#include <netdb.h> // struct addrinfo
|
||||||
@@ -36,6 +32,7 @@
|
|||||||
#include <unistd.h> // pid_t
|
#include <unistd.h> // pid_t
|
||||||
// just for different purposes
|
// just for different purposes
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <math.h> // inf/isinf
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#if defined GETTEXT
|
#if defined GETTEXT
|
||||||
@@ -166,42 +163,25 @@ int sl_canwrite(int fd);
|
|||||||
/******************************************************************************\
|
/******************************************************************************\
|
||||||
The original term.h
|
The original term.h
|
||||||
\******************************************************************************/
|
\******************************************************************************/
|
||||||
#ifdef SL_USE_OLD_TTY
|
|
||||||
typedef struct {
|
|
||||||
char *portname; // device filename (should be freed before structure freeing)
|
|
||||||
int speed; // baudrate in human-readable format
|
|
||||||
tcflag_t baudrate; // baudrate (B...)
|
|
||||||
struct termios oldtty; // TTY flags for previous port settings
|
|
||||||
struct termios tty; // TTY flags for current settings
|
|
||||||
int comfd; // TTY file descriptor
|
|
||||||
char *buf; // buffer for data read
|
|
||||||
size_t bufsz; // size of buf
|
|
||||||
size_t buflen; // length of data read into buf
|
|
||||||
int exclusive; // should device be exclusive opened
|
|
||||||
} sl_tty_t;
|
|
||||||
|
|
||||||
tcflag_t sl_tty_convspd(int speed);
|
|
||||||
#else
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *portname; // device filename (should be freed before structure freeing)
|
char *portname; // device filename (should be freed before structure freeing)
|
||||||
int speed; // baudrate in human-readable format
|
int speed; // baudrate in human-readable format
|
||||||
char *format; // format like 8N1
|
char *format; // format like 8N1
|
||||||
struct termios2 oldtty; // TTY flags for previous port settings
|
|
||||||
struct termios2 tty; // TTY flags for current settings
|
|
||||||
int comfd; // TTY file descriptor
|
int comfd; // TTY file descriptor
|
||||||
char *buf; // buffer for data read
|
char *buf; // buffer for data read
|
||||||
size_t bufsz; // size of buf
|
size_t bufsz; // size of buf
|
||||||
size_t buflen; // length of data read into buf
|
size_t buflen; // length of data read into buf
|
||||||
int exclusive; // should device be exclusive opened
|
int exclusive; // should device be exclusive opened
|
||||||
} sl_tty_t;
|
} sl_tty_t;
|
||||||
#endif
|
|
||||||
|
|
||||||
void sl_tty_close(sl_tty_t **descr);
|
int sl_tty_fdescr(const char *comdev, const char *format, int speed, int exclusive);
|
||||||
sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz);
|
sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz);
|
||||||
|
int sl_tty_setformat(sl_tty_t *d, const char *format);
|
||||||
sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive);
|
sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive);
|
||||||
int sl_tty_read(sl_tty_t *descr);
|
|
||||||
int sl_tty_tmout(double usec);
|
int sl_tty_tmout(double usec);
|
||||||
|
int sl_tty_read(sl_tty_t *descr);
|
||||||
int sl_tty_write(int comfd, const char *buff, size_t length);
|
int sl_tty_write(int comfd, const char *buff, size_t length);
|
||||||
|
void sl_tty_close(sl_tty_t **descr);
|
||||||
|
|
||||||
/******************************************************************************\
|
/******************************************************************************\
|
||||||
Logging
|
Logging
|
||||||
@@ -349,6 +329,8 @@ void WEAK sl_iffound_deflt(pid_t pid);
|
|||||||
void sl_check4running(char *selfname, char *pidfilename);
|
void sl_check4running(char *selfname, char *pidfilename);
|
||||||
// read name of process by its PID
|
// read name of process by its PID
|
||||||
char *sl_getPSname(pid_t pid);
|
char *sl_getPSname(pid_t pid);
|
||||||
|
// daemonize: reopen stdin/out/err as /dev/null, chdir to /, umask(0)
|
||||||
|
int sl_daemonize(); // return -1 if failed, 0 on OK
|
||||||
|
|
||||||
/******************************************************************************\
|
/******************************************************************************\
|
||||||
The original fifo_lifo.h
|
The original fifo_lifo.h
|
||||||
@@ -417,6 +399,8 @@ size_t sl_RB_writestr(sl_ringbuffer_t *b, char *s);
|
|||||||
/******************************************************************************\
|
/******************************************************************************\
|
||||||
The original socket.h
|
The original socket.h
|
||||||
\******************************************************************************/
|
\******************************************************************************/
|
||||||
|
// max length (including trailing '\0') of UNIX socket path
|
||||||
|
#define UNIX_SOCK_PATH_MAX 108
|
||||||
|
|
||||||
// handler result: what to send to client
|
// handler result: what to send to client
|
||||||
typedef enum{
|
typedef enum{
|
||||||
@@ -445,6 +429,15 @@ typedef struct{
|
|||||||
int len; // strlen of `val`
|
int len; // strlen of `val`
|
||||||
} sl_sock_string_t;
|
} sl_sock_string_t;
|
||||||
|
|
||||||
|
// optional keyword number like key[12] = 500
|
||||||
|
typedef struct{
|
||||||
|
double magick; // -Inf - to distinguish it from sl_sock_*_t
|
||||||
|
int n; // if n < 0 there was no any number in `key`
|
||||||
|
} sl_sock_keyno_t;
|
||||||
|
#define SL_SOCK_KEYNO_DEFAULT {.magick = -INFINITY, .n = -1}
|
||||||
|
void sl_sock_keyno_init(sl_sock_keyno_t*);
|
||||||
|
int sl_sock_keyno_check(sl_sock_keyno_t*);
|
||||||
|
|
||||||
struct sl_sock_hitem;
|
struct sl_sock_hitem;
|
||||||
struct sl_sock;
|
struct sl_sock;
|
||||||
|
|
||||||
@@ -468,6 +461,9 @@ typedef enum{
|
|||||||
|
|
||||||
struct sl_sock;
|
struct sl_sock;
|
||||||
|
|
||||||
|
// opent socket and return its file descriptor
|
||||||
|
int sl_sock_open(sl_socktype_e type, const char *path, int isserver, int ai_socktype);
|
||||||
|
|
||||||
// default max clients amount
|
// default max clients amount
|
||||||
#define SL_DEF_MAXCLIENTS (32)
|
#define SL_DEF_MAXCLIENTS (32)
|
||||||
// custom socket handlers: connect/disconnect/etc
|
// custom socket handlers: connect/disconnect/etc
|
||||||
@@ -535,4 +531,3 @@ 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_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_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);
|
sl_sock_hresult_e sl_sock_strhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user