SUPPORT-8455. Реализован модуль подписания
This commit is contained in:
commit
4243ebae5e
42 changed files with 4889 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
CMakeFiles/
|
||||
CMakeLists.txt.user
|
||||
src/config.h
|
||||
src/version.h
|
||||
|
||||
101
CMakeLists.txt
Normal file
101
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
CMAKE_MINIMUM_REQUIRED (VERSION 3.0)
|
||||
|
||||
PROJECT (ervu-esia-module VERSION 1.0.0 LANGUAGES C)
|
||||
|
||||
SET (CMAKE_C_COMPILER "gcc")
|
||||
|
||||
IF (CMAKE_VERBOSE)
|
||||
SET (CMAKE_VERBOSE_MAKEFILE 1)
|
||||
ENDIF ()
|
||||
|
||||
SET (CMAKE_C_FLAGS "-Wall -Wextra -Werror -Wno-unused-parameter")
|
||||
SET (CMAKE_C_FLAGS_DEBUG "-g -O0 -DDEBUG")
|
||||
SET (CMAKE_C_FLAGS_RELEASE "-g -O2 -DNDEBUG")
|
||||
SET (CMAKE_EXE_LINKER_FLAGS "-Wl,--no-undefined")
|
||||
|
||||
SET (SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
SET (UTILS_DIR ${SOURCE_DIR}/utils)
|
||||
SET (FCGISRV_DIR ${SOURCE_DIR}/fcgisrv)
|
||||
SET (MODULES_DIR ${SOURCE_DIR}/modules)
|
||||
|
||||
INCLUDE_DIRECTORIES (${SOURCE_DIR})
|
||||
|
||||
# PKG-CONFIG
|
||||
FIND_PACKAGE (PkgConfig REQUIRED)
|
||||
|
||||
# GLIB2
|
||||
MESSAGE ("")
|
||||
MESSAGE ("Try to find the glib-2.0..")
|
||||
PKG_CHECK_MODULES (GLIB2 REQUIRED glib-2.0)
|
||||
|
||||
IF (NOT GLIB2_FOUND)
|
||||
MESSAGE (SEND_ERROR "Can not find glib-2.0")
|
||||
ELSE ()
|
||||
MESSAGE ("glib-2.0 is found: ")
|
||||
MESSAGE ("GLIB2_LIB_INCLUDE_DIR : " ${GLIB2_LIB_INCLUDE_DIR})
|
||||
MESSAGE ("GLIB2_INCLUDE_DIR : " ${GLIB2_INCLUDE_DIR})
|
||||
MESSAGE ("GLIB2_LIBRARY_DIR : " ${GLIB2_LIBRARY_DIR})
|
||||
MESSAGE ("GLIB2_INCLUDE_DIRS : " ${GLIB2_INCLUDE_DIRS})
|
||||
MESSAGE ("GLIB2_LIBRARY_DIRS : " ${GLIB2_LIBRARY_DIRS})
|
||||
MESSAGE ("GLIB2_LIBRARIES : " ${GLIB2_LIBRARIES})
|
||||
ENDIF (NOT GLIB2_FOUND)
|
||||
MESSAGE ("")
|
||||
|
||||
# set glib2 path vars
|
||||
INCLUDE_DIRECTORIES (${GLIB2_INCLUDE_DIRS})
|
||||
|
||||
SET (DEP_LIBS
|
||||
-lpthread
|
||||
-lfcgi
|
||||
-lglib-2.0
|
||||
-ldl
|
||||
)
|
||||
|
||||
# version.h
|
||||
CONFIGURE_FILE (${SOURCE_DIR}/version.h.in ${SOURCE_DIR}/version.h)
|
||||
|
||||
# config.h
|
||||
IF (NOT DEFINED CONFIG_NAME)
|
||||
SET (CONFIG_NAME /etc/ervu-esia-module.conf)
|
||||
ENDIF ()
|
||||
|
||||
MESSAGE ("CONFIG_NAME: " ${CONFIG_NAME})
|
||||
MESSAGE ("")
|
||||
|
||||
CONFIGURE_FILE (${SOURCE_DIR}/config.h.in ${SOURCE_DIR}/config.h)
|
||||
|
||||
SET (CRYPTOPRO_INCLUDE_DIRS
|
||||
/opt/cprocsp/include/cpcsp
|
||||
/opt/cprocsp/include/pki
|
||||
)
|
||||
MESSAGE("CRYPTOPRO_INCLUDE_DIRS : " ${CRYPTOPRO_INCLUDE_DIRS})
|
||||
|
||||
ADD_DEFINITIONS(-DUNIX)
|
||||
INCLUDE_DIRECTORIES ("${CRYPTOPRO_INCLUDE_DIRS}")
|
||||
|
||||
FILE (GLOB_RECURSE HEADERS "${SOURCE_DIR}/*.h")
|
||||
|
||||
ADD_EXECUTABLE (${PROJECT_NAME}
|
||||
${HEADERS}
|
||||
${SOURCE_DIR}/main.c
|
||||
${SOURCE_DIR}/main_conf.c
|
||||
${SOURCE_DIR}/master.c
|
||||
${SOURCE_DIR}/service_manager.c
|
||||
${SOURCE_DIR}/worker.c
|
||||
${UTILS_DIR}/base64.c
|
||||
${UTILS_DIR}/capi.c
|
||||
${UTILS_DIR}/conf_file_context.c
|
||||
${UTILS_DIR}/cryptopro.c
|
||||
${UTILS_DIR}/gconf_file.c
|
||||
${UTILS_DIR}/library.c
|
||||
${UTILS_DIR}/logger.c
|
||||
${UTILS_DIR}/str_t.c
|
||||
${FCGISRV_DIR}/fcgi_map.c
|
||||
${FCGISRV_DIR}/fcgi_server.c
|
||||
${FCGISRV_DIR}/fcgi_thread.c
|
||||
${FCGISRV_DIR}/fcgi_utils.c
|
||||
${FCGISRV_DIR}/fcgi_worker.c
|
||||
${MODULES_DIR}/service_sign.c
|
||||
)
|
||||
|
||||
TARGET_LINK_LIBRARIES (${PROJECT_NAME} ${DEP_LIBS})
|
||||
106
README.md
Normal file
106
README.md
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
## Краткое описание
|
||||
|
||||
Модуль предназначен для подписи данных.
|
||||
|
||||
Приложение принимает POST-запрос по протоколу FastCGI (Content-Type: text/plain).
|
||||
Подписывает строку, полученную в теле запроса.
|
||||
В ответе возвращает подпись в формате urlSafeBase64 (Content-Type: text/plain).
|
||||
|
||||
Пример выполнения запроса:
|
||||
```
|
||||
$ curl -v http://127.0.0.1:8080/sign -H "Content-Type: text/plain" -d "test"
|
||||
* Trying 127.0.0.1:8080...
|
||||
* Connected to 127.0.0.1 (127.0.0.1) port 8080
|
||||
> POST /sign HTTP/1.1
|
||||
> Host: 127.0.0.1:8080
|
||||
> User-Agent: curl/8.4.0
|
||||
> Accept: */*
|
||||
> Content-Type: text/plain
|
||||
> Content-Length: 4
|
||||
>
|
||||
< HTTP/1.1 200 OK
|
||||
< Server: nginx/1.24.0
|
||||
< Date: Fri, 16 Aug 2024 07:33:13 GMT
|
||||
< Content-Type: text/plain
|
||||
< Transfer-Encoding: chunked
|
||||
< Connection: keep-alive
|
||||
<
|
||||
urlSafeBase64_of_signed_string_test
|
||||
```
|
||||
|
||||
## Сборка и установка
|
||||
|
||||
### Подготовка
|
||||
|
||||
1. Установить КриптоПро.
|
||||
|
||||
2. Установить сертификат для пользователя, под которым будет запускаться данное приложение.
|
||||
|
||||
3. Установить nginx.
|
||||
|
||||
4. В настройках nginx добавить перенаправление на данное приложение:
|
||||
```
|
||||
location = /sign {
|
||||
fastcgi_pass localhost:9009;
|
||||
include fastcgi_params;
|
||||
}
|
||||
```
|
||||
|
||||
### Cборка из исходников
|
||||
|
||||
Для **сборки** необходимы следующие библиотеки:
|
||||
- glib2-devel
|
||||
- libfcgi-devel
|
||||
- lsb-cprocsp-base
|
||||
- lsb-cprocsp-rdr-64
|
||||
- lsb-cprocsp-kc1-64
|
||||
- lsb-cprocsp-capilite-64
|
||||
- cprocsp-curl-64
|
||||
- lsb-cprocsp-ca-certs
|
||||
- cprocsp-pki-cades-64
|
||||
- lsb-cprocsp-devel
|
||||
|
||||
Для **установки** необходимы следующие библиотеки:
|
||||
- glib2
|
||||
- libfcgi
|
||||
- lsb-cprocsp-base
|
||||
- lsb-cprocsp-rdr-64
|
||||
- lsb-cprocsp-kc1-64
|
||||
- lsb-cprocsp-capilite-64
|
||||
- cprocsp-curl-64
|
||||
- lsb-cprocsp-ca-certs
|
||||
- cprocsp-pki-cades-64
|
||||
|
||||
Для сборки и установки приложения из исходников, выполните следующие команды:
|
||||
```
|
||||
$ mkdir .build && cd .build
|
||||
$ cmake ..
|
||||
$ make -j$(nproc)
|
||||
```
|
||||
|
||||
Чтобы задать произвольное имя конфигурационного файла, при сборке приложения необходимо задать параметр CONFIG_NAME.
|
||||
Пример:
|
||||
```
|
||||
cmake -DCONFIG_NAME=/opt/ervu-esia-module.conf
|
||||
```
|
||||
|
||||
По умолчанию, имя конфигурационного файла /etc/ervu-esia-module.conf.
|
||||
|
||||
### Настройка
|
||||
|
||||
Приложение настраивается в конфигурационном файле, заданном на этапе сборки (по умолчанию, /etc/ervu-esia-module.conf).
|
||||
|
||||
- В секции **\[main\]** задать количество воркеров
|
||||
worker_processes = 10 *\# значение по умолчанию: 10*
|
||||
|
||||
- В секции **\[fcgi\]** задать настройки fcgi-сервера:
|
||||
fcgi_listen_port = 9009 *\# значение по умолчанию: 9009, должно совпадать со значением в nginx.conf*
|
||||
fcgi_listen_host = 127.0.0.1 *\# значение по умолчанию: 127.0.0.1, должно совпадать со значением в nginx.conf*
|
||||
fcgi_thread_pool_size = 1 *\# значение по умолчанию: 1*
|
||||
|
||||
- В секции **\[sign\]** задать настройки модуля подписания:
|
||||
location = /sign *\# значение по умолчанию: /sign, должно совпадать со значением в nginx.conf*
|
||||
cp_file = libcapi20.so *\# путь до файла библиотеки криптопровайдера*
|
||||
signer_subject = signer@example.ru *\# email, ИНН, СНИЛС или любая другая строка из свойства контейнера «Субъект»*
|
||||
pin = \*\*\*\* *\# пароль от контейнера*
|
||||
|
||||
14
conf/ervu-esia-module.conf
Normal file
14
conf/ervu-esia-module.conf
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[main]
|
||||
#worker_processes = 10
|
||||
|
||||
[fcgi]
|
||||
fcgi_listen_port = 9009
|
||||
#fcgi_listen_host = 127.0.0.1
|
||||
#fcgi_thread_pool_size = 1
|
||||
|
||||
[sign]
|
||||
#location = /sign
|
||||
cp_file = libcapi20.so
|
||||
signer_subject = signer@example.ru
|
||||
pin = ****
|
||||
|
||||
6
src/config.h.in
Normal file
6
src/config.h.in
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef CONFIG_H_INCLUDED
|
||||
#define CONFIG_H_INCLUDED
|
||||
|
||||
#define CONF_NAME "@CONFIG_NAME@"
|
||||
|
||||
#endif // CONFIG_H_INCLUDED
|
||||
103
src/fcgisrv/fcgi_map.c
Normal file
103
src/fcgisrv/fcgi_map.c
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#include "fcgi_server_internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
static int
|
||||
fcgi_map_item_test(fcgi_handler_map_item_t* item, char* path)
|
||||
{
|
||||
assert(item != NULL);
|
||||
assert(path != NULL);
|
||||
|
||||
LOG_DEBUG("fcgi_map_item_test. item->path: %s; path: %s", item->path, path);
|
||||
return strcmp(item->path, path);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
fcgi_map_item_set(fcgi_handler_map_item_t* item, const char* path, const fcgi_handler_t* handler)
|
||||
{
|
||||
assert(item != NULL);
|
||||
assert(path != NULL);
|
||||
assert(handler != NULL);
|
||||
|
||||
item->path = strdup(path);
|
||||
if (item->path == NULL) {
|
||||
LOG_ERROR("fcgi_map_item_set. strdup failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
item->handler = *handler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fcgi_map_item_clear(fcgi_handler_map_item_t* item)
|
||||
{
|
||||
if (item == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
free(item->path);
|
||||
memset(item, 0, sizeof(fcgi_handler_map_item_t));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
fcgi_handler_map_set(fcgi_handler_map_t* map, char* path, fcgi_handler_t* handler)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
assert(map != NULL);
|
||||
assert(path != NULL);
|
||||
assert(handler != NULL);
|
||||
|
||||
LOG_DEBUG("fcgi_handler_map_set. map->num = %zu", map->num);
|
||||
LOG_DEBUG("fcgi_handler_map_set. path = %s", path);
|
||||
|
||||
for (i = 0; i < map->num; ++i) {
|
||||
if (!fcgi_map_item_test(&map->locations[i], path)) {
|
||||
fcgi_map_item_clear(&map->locations[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= FCGI_MAX_HANDLERS_NUM) {
|
||||
LOG_ERROR("fcgi_handler_map_set. exceeded FCGI_MAX_HANDLERS_NUM(%d)",
|
||||
FCGI_MAX_HANDLERS_NUM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fcgi_map_item_set(&map->locations[i], path, handler) < 0) {
|
||||
LOG_ERROR("fcgi_handler_map_set. fcgi_map_item_set failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
map->num++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
fcgi_handler_t*
|
||||
fcgi_handler_map_get(fcgi_handler_map_t* map, char* path)
|
||||
{
|
||||
fcgi_handler_t* handler = NULL;
|
||||
size_t i;
|
||||
|
||||
assert(map != NULL);
|
||||
assert(path != NULL);
|
||||
|
||||
LOG_DEBUG("fcgi_handler_map_get. path: %s", path);
|
||||
LOG_DEBUG("fcgi_handler_map_get. map->num = %zu", map->num);
|
||||
|
||||
for (i = 0; i < map->num; ++i) {
|
||||
if (!fcgi_map_item_test(&map->locations[i], path)) {
|
||||
handler = &map->locations[i].handler;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return handler;
|
||||
}
|
||||
305
src/fcgisrv/fcgi_server.c
Normal file
305
src/fcgisrv/fcgi_server.c
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
#include "fcgi_server_internal.h"
|
||||
|
||||
#include "utils/conf_file_context.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
#define FCGI_CONF_SECTION "fcgi"
|
||||
|
||||
#define FCGI_CONF_KEY_LISTEN_HOST "fcgi_listen_host"
|
||||
#define FCGI_CONF_KEY_LISTEN_PORT "fcgi_listen_port"
|
||||
#define FCGI_CONF_KEY_POOL_SIZE "fcgi_thread_pool_size"
|
||||
|
||||
/* default configuration values: */
|
||||
static const char *FCGI_CONF_DEFAULT_LISTEN_HOST = "127.0.0.1";
|
||||
static const int FCGI_CONF_DEFAULT_POOL_SIZE = 1;
|
||||
|
||||
#define FCGI_PENDING_CONN_QUEUE_MAX_LENGTH 16
|
||||
#define FCGI_DEFAULT_TCP_LISTEN_PORT 9009
|
||||
#define FCGI_DEFAULT_TCP_LISTEN_ADDRESS "127.0.0.1"
|
||||
|
||||
static pthread_mutex_t server_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
|
||||
int
|
||||
fcgi_load_conf(const char* filename, fcgi_conf_t* conf)
|
||||
{
|
||||
assert(filename != NULL && "fcgi_load_conf");
|
||||
assert(conf != NULL && "fcgi_load_conf");
|
||||
|
||||
memset(conf, 0, sizeof(fcgi_conf_t));
|
||||
|
||||
conf_file_field_t fields[] = {
|
||||
{
|
||||
FCGI_CONF_SECTION,
|
||||
FCGI_CONF_KEY_LISTEN_HOST,
|
||||
&conf->listen.host,
|
||||
CONF_FILE_VALUE_STRING,
|
||||
CONF_FILE_VALUE_HOSTNAME,
|
||||
&FCGI_CONF_DEFAULT_LISTEN_HOST
|
||||
},
|
||||
{
|
||||
FCGI_CONF_SECTION,
|
||||
FCGI_CONF_KEY_LISTEN_PORT,
|
||||
&conf->listen.port,
|
||||
CONF_FILE_VALUE_INTEGER,
|
||||
CONF_FILE_VALUE_TCP_PORT,
|
||||
NULL
|
||||
},
|
||||
{
|
||||
FCGI_CONF_SECTION,
|
||||
FCGI_CONF_KEY_POOL_SIZE,
|
||||
&conf->thread_pool_size,
|
||||
CONF_FILE_VALUE_INTEGER,
|
||||
CONF_FILE_VALUE_POSITIVE_INT,
|
||||
&FCGI_CONF_DEFAULT_POOL_SIZE
|
||||
},
|
||||
};
|
||||
|
||||
if (conf_file_load_from_file(filename, fields, sizeof(fields) / sizeof(conf_file_field_t))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_DEBUG("fcgi_conf->listen: %s:%d", conf->listen.host, conf->listen.port);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
fcgi_clear_conf(conf);
|
||||
LOG_ERROR("fcgi_load_conf exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
fcgi_clear_conf(fcgi_conf_t* conf)
|
||||
{
|
||||
if (conf != NULL) {
|
||||
free(conf->listen.host);
|
||||
memset(conf, 0, sizeof(fcgi_conf_t));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
fcgi_start_listen(const fcgi_tcp_t* tcp_conf, fcgi_listen_t* listen_obj)
|
||||
{
|
||||
int port;
|
||||
char* host;
|
||||
struct sockaddr_in st_addr;
|
||||
int rc = -1;
|
||||
int on;
|
||||
|
||||
LOG_TRACE("fcgi_start_listen enter");
|
||||
|
||||
assert(tcp_conf != NULL && "fcgi_start_listen");
|
||||
assert(listen_obj != NULL && "fcgi_start_listen");
|
||||
|
||||
rc = pthread_mutex_init(&listen_obj->accept_mutex, NULL);
|
||||
if (rc != 0) {
|
||||
LOG_ERROR("Could not initialize accept_mutex for listen socket. Desc: %s",
|
||||
strerror(rc));
|
||||
goto error;
|
||||
}
|
||||
listen_obj->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
|
||||
if (listen_obj->fd == -1) {
|
||||
LOG_ERROR("Could not create socket file descriptor");
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_DEBUG("fcgi_start_listen: listen_obj->fd = %d", listen_obj->fd);
|
||||
|
||||
memset(&st_addr, 0, sizeof(struct sockaddr_in));
|
||||
|
||||
port = tcp_conf->port != 0 ? tcp_conf->port : FCGI_DEFAULT_TCP_LISTEN_PORT;
|
||||
host = tcp_conf->host != NULL ? tcp_conf->host : FCGI_DEFAULT_TCP_LISTEN_ADDRESS; // "0.0.0.0" htonl(INADDR_ANY)
|
||||
|
||||
st_addr.sin_family = PF_INET;
|
||||
st_addr.sin_port = htons(port);
|
||||
|
||||
if (inet_pton(AF_INET, host, &st_addr.sin_addr) != 1) {
|
||||
LOG_ERROR("Could not parse bind-address '%s'. Desc: %s", host, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(listen_obj->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) {
|
||||
LOG_WARN("Could not set REUSEADDR. Desc: %s", strerror(errno));
|
||||
}
|
||||
|
||||
if (bind(listen_obj->fd, (struct sockaddr*)&st_addr, sizeof(struct sockaddr_in)) != 0) {
|
||||
LOG_ERROR("Could not bind socket to %s:%d. Desc: %s", host, port, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (listen(listen_obj->fd, FCGI_PENDING_CONN_QUEUE_MAX_LENGTH) != 0) {
|
||||
LOG_ERROR("Could not start listening on %s:%d. Desc: %s",
|
||||
host, port, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_DEBUG("fcgi_start_listen exit with success. host: %s, port: %d", host, port);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("fcgi_start_listen exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
fcgi_stop_listen(const fcgi_listen_t* listen)
|
||||
{
|
||||
LOG_TRACE("fcgi_stop_listen enter");
|
||||
|
||||
assert(listen != NULL && "fcgi_stop_listen");
|
||||
|
||||
int rc = 0;
|
||||
|
||||
if (shutdown(listen->fd, SHUT_RDWR) != 0) {
|
||||
LOG_ERROR("fcgi_stop_listen. shutdown(%d, SHUT_RDWR) failed. Desc: %s",
|
||||
listen->fd, strerror(errno));
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
if (close(listen->fd) != 0) {
|
||||
LOG_ERROR("fcgi_stop_listen. close(%d) failed. Desc: %s",
|
||||
listen->fd, strerror(errno));
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
LOG_TRACE("fcgi_stop_listen exit");
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
HFcgi
|
||||
fcgi_create_server(const fcgi_conf_t* conf)
|
||||
{
|
||||
LOG_TRACE("fcgi_create_server enter");
|
||||
|
||||
assert(conf != NULL && "fcgi_create_server");
|
||||
|
||||
fcgi_server_t* hfcgi = NULL;
|
||||
|
||||
if (FCGX_Init() != 0) {
|
||||
LOG_ERROR("Could not init libfcgi");
|
||||
goto error;
|
||||
}
|
||||
|
||||
hfcgi = calloc(1, sizeof(fcgi_server_t));
|
||||
|
||||
if (hfcgi == NULL) {
|
||||
LOG_ERROR("Could not allocate memory for HFcgi handle");
|
||||
goto error;
|
||||
}
|
||||
|
||||
hfcgi->mutex = &server_mutex;
|
||||
|
||||
if (fcgi_start_listen(&conf->listen, &hfcgi->listen) != 0) {
|
||||
LOG_ERROR("fcgi_start_listen failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
hfcgi->thread_pool_size = conf->thread_pool_size;
|
||||
|
||||
LOG_TRACE("fcgi_create_server exit with success");
|
||||
return hfcgi;
|
||||
|
||||
error:
|
||||
free(hfcgi);
|
||||
LOG_ERROR("fcgi_create_server exit with error");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
fcgi_register_handler(HFcgi hfcgi, char* path, fcgi_handler_t* handler)
|
||||
{
|
||||
return fcgi_handler_map_set(&hfcgi->handler_map, path, handler);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
fcgi_run_server(HFcgi hfcgi)
|
||||
{
|
||||
LOG_TRACE("fcgi_run_server enter");
|
||||
|
||||
if (hfcgi == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (fcgi_server_thread_pool_create(&hfcgi->thread_pool,
|
||||
hfcgi->thread_pool_size,
|
||||
hfcgi) != 0) {
|
||||
LOG_ERROR("fcgi_server_thread_pool_create failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_TRACE("fcgi_run_server exit with success");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("fcgi_run_server exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
fcgi_stop_server(HFcgi hfcgi)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
LOG_TRACE("fcgi_stop_server enter");
|
||||
|
||||
if (hfcgi == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(hfcgi->mutex);
|
||||
hfcgi->is_stopping = 1;
|
||||
pthread_mutex_unlock(hfcgi->mutex);
|
||||
|
||||
FCGX_ShutdownPending();
|
||||
|
||||
if (fcgi_stop_listen(&hfcgi->listen) != 0) {
|
||||
LOG_ERROR("fcgi_stop_server. fcgi_stop_listen failed");
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
fcgi_server_thread_pool_dispose(&hfcgi->thread_pool);
|
||||
|
||||
hfcgi->is_stopping = 0;
|
||||
|
||||
if (rc == -1) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_TRACE("fcgi_stop_server exit with success");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("fcgi_stop_server exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
fcgi_dispose_server(HFcgi hfcgi)
|
||||
{
|
||||
LOG_TRACE("fcgi_dispose_server enter");
|
||||
|
||||
free(hfcgi);
|
||||
|
||||
LOG_TRACE("fcgi_dispose_server exit");
|
||||
}
|
||||
73
src/fcgisrv/fcgi_server.h
Normal file
73
src/fcgisrv/fcgi_server.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#ifndef FCGI_SERVER_H_INCLUDED
|
||||
#define FCGI_SERVER_H_INCLUDED
|
||||
|
||||
#include <fcgiapp.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct fcgi_server_s* HFcgi;
|
||||
|
||||
|
||||
typedef enum fcgi_handler_status_e {
|
||||
HANDLER_ERROR = -1,
|
||||
HANDLER_SUCCESS = 0,
|
||||
HANDLER_HTTP_OK = 200,
|
||||
HANDLER_REDIRECT_TO_LOGIN = 302,
|
||||
HANDLER_HTTP_BAD_REQUEST = 400,
|
||||
HANDLER_HTTP_UNAUTHORIZED = 401,
|
||||
HANDLER_HTTP_NOT_FOUND = 404,
|
||||
HANDLER_HTTP_METHOD_NOT_ALLOWED = 405,
|
||||
HANDLER_HTTP_NOT_ACCEPTABLE = 406,
|
||||
HANDLER_HTTP_REQUEST_ENTITY_TOO_LARGE = 413,
|
||||
HANDLER_HTTP_INTERNAL_SERVER_ERROR = 500,
|
||||
} fcgi_handler_status_t;
|
||||
|
||||
|
||||
typedef fcgi_handler_status_t (*handler_func)(FCGX_Request* request, void* ctx);
|
||||
|
||||
|
||||
typedef struct fcgi_handler_s {
|
||||
handler_func execute;
|
||||
void *ctx;
|
||||
|
||||
} fcgi_handler_t;
|
||||
|
||||
|
||||
typedef struct fcgi_tcp_s {
|
||||
char *host;
|
||||
int port;
|
||||
|
||||
} fcgi_tcp_t;
|
||||
|
||||
|
||||
typedef struct fcgi_conf_s {
|
||||
fcgi_tcp_t listen;
|
||||
size_t thread_pool_size;
|
||||
|
||||
} fcgi_conf_t;
|
||||
|
||||
|
||||
int fcgi_load_conf(const char* filename, fcgi_conf_t* conf);
|
||||
void fcgi_clear_conf(fcgi_conf_t* conf);
|
||||
|
||||
HFcgi fcgi_create_server(const fcgi_conf_t* conf);
|
||||
void fcgi_dispose_server(HFcgi);
|
||||
|
||||
int fcgi_register_handler(HFcgi hfcgi, char* path, fcgi_handler_t* handler);
|
||||
|
||||
int fcgi_run_server(HFcgi);
|
||||
int fcgi_stop_server(HFcgi);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* FCGI_SERVER_H_INCLUDED */
|
||||
70
src/fcgisrv/fcgi_server_internal.h
Normal file
70
src/fcgisrv/fcgi_server_internal.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#ifndef FCGI_SERVER_INTERNAL_H_INCLUDED
|
||||
#define FCGI_SERVER_INTERNAL_H_INCLUDED
|
||||
|
||||
|
||||
#include "fcgi_utils.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define FCGI_MAX_HANDLERS_NUM 10
|
||||
|
||||
|
||||
extern fcgi_handler_t fcgi_default_handler;
|
||||
|
||||
typedef unsigned long fcgi_hash_t;
|
||||
|
||||
typedef struct fcgi_handler_map_item_s {
|
||||
fcgi_hash_t hash;
|
||||
char* path;
|
||||
fcgi_handler_t handler;
|
||||
|
||||
} fcgi_handler_map_item_t;
|
||||
|
||||
typedef struct fcgi_handler_map_s {
|
||||
fcgi_handler_map_item_t locations[FCGI_MAX_HANDLERS_NUM];
|
||||
size_t num;
|
||||
|
||||
} fcgi_handler_map_t;
|
||||
|
||||
typedef struct fcgi_listen_s {
|
||||
int fd;
|
||||
pthread_mutex_t accept_mutex;
|
||||
|
||||
} fcgi_listen_t;
|
||||
|
||||
typedef struct fcgi_thread_s {
|
||||
pthread_t descriptor;
|
||||
int ret_code;
|
||||
atomic_flag run;
|
||||
void* ctx;
|
||||
|
||||
} fcgi_thread_t;
|
||||
|
||||
typedef struct fcgi_thread_pool_s {
|
||||
size_t size;
|
||||
fcgi_thread_t* threads;
|
||||
|
||||
} fcgi_thread_pool_t;
|
||||
|
||||
typedef struct fcgi_server_s {
|
||||
fcgi_listen_t listen;
|
||||
fcgi_handler_map_t handler_map;
|
||||
fcgi_thread_pool_t thread_pool;
|
||||
size_t thread_pool_size;
|
||||
pthread_mutex_t *mutex;
|
||||
int is_stopping;
|
||||
|
||||
} fcgi_server_t;
|
||||
|
||||
|
||||
int fcgi_worker_main(fcgi_thread_t* thread);
|
||||
|
||||
int fcgi_server_thread_pool_create(fcgi_thread_pool_t* thread_pool, size_t size, HFcgi hfcgi);
|
||||
void fcgi_server_thread_pool_dispose(fcgi_thread_pool_t* thread_pool);
|
||||
|
||||
int fcgi_handler_map_set(fcgi_handler_map_t* map, char* path, fcgi_handler_t* handler);
|
||||
fcgi_handler_t* fcgi_handler_map_get(fcgi_handler_map_t* map, char* path);
|
||||
|
||||
#endif // FCGI_SERVER_INTERNAL_H_INCLUDED
|
||||
178
src/fcgisrv/fcgi_thread.c
Normal file
178
src/fcgisrv/fcgi_thread.c
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
#include "fcgi_server_internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
typedef int (*fcgi_thread_main_t)(fcgi_thread_t* thread);
|
||||
|
||||
|
||||
typedef struct pthread_ctx_s {
|
||||
fcgi_thread_main_t main;
|
||||
fcgi_thread_t* main_ctx;
|
||||
|
||||
} pthread_ctx_t;
|
||||
|
||||
|
||||
static
|
||||
void* pthread_main(void* _ctx)
|
||||
{
|
||||
LOG_TRACE("pthread_main enter");
|
||||
|
||||
assert(_ctx != NULL && "pthread_main");
|
||||
|
||||
pthread_ctx_t* pthread_ctx_ptr = (pthread_ctx_t*)_ctx;
|
||||
|
||||
int ret_code = pthread_ctx_ptr->main(pthread_ctx_ptr->main_ctx);
|
||||
|
||||
free(pthread_ctx_ptr);
|
||||
|
||||
LOG_TRACE("pthread_main exit with code %d", ret_code);
|
||||
|
||||
pthread_exit((void*)(intptr_t)ret_code);
|
||||
|
||||
return (void*)(intptr_t)ret_code;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int fcgi_thread_start(fcgi_thread_t* thread, fcgi_thread_main_t main, void* ctx)
|
||||
{
|
||||
LOG_TRACE("fcgi_thread_start enter");
|
||||
|
||||
assert(thread != NULL && "fcgi_thread_start: fcgi_thread_t* thread == NULL");
|
||||
assert(main != NULL && "fcgi_thread_start: fcgi_thread_main_t main == NULL");
|
||||
/* ctx may be NULL */
|
||||
|
||||
pthread_ctx_t* pthread_ctx_ptr = (pthread_ctx_t*)malloc(sizeof(pthread_ctx_t));
|
||||
if (pthread_ctx_ptr == NULL) {
|
||||
LOG_ERROR("Could not allocate memory for pthread_ctx_t");
|
||||
goto error;
|
||||
}
|
||||
|
||||
pthread_ctx_ptr->main = main;
|
||||
pthread_ctx_ptr->main_ctx = thread;
|
||||
atomic_flag_test_and_set(&thread->run);
|
||||
thread->ctx = ctx;
|
||||
|
||||
int rc = pthread_create(&thread->descriptor, NULL, pthread_main, (void*)pthread_ctx_ptr);
|
||||
if (rc != 0) {
|
||||
LOG_ERROR("pthread_create failed. Desc: %s", strerror(rc));
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_TRACE("fcgi_thread_start exit successfully");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("fcgi_thread_start exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int fcgi_thread_stop(fcgi_thread_t* thread)
|
||||
{
|
||||
LOG_TRACE("fcgi_thread_stop enter");
|
||||
|
||||
assert(thread != NULL && "fcgi_thread_stop: fcgi_thread_t* thread == NULL");
|
||||
|
||||
atomic_flag_clear(&thread->run);
|
||||
|
||||
int rc = pthread_join(thread->descriptor, (void**)&thread->ret_code);
|
||||
|
||||
if (rc != 0) {
|
||||
LOG_ERROR("pthread_join failed. Desc: %s", strerror(rc));
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_TRACE("fcgi_thread_stop exit successfully");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("fcgi_thread_stop exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void fcgi_threads_stop(fcgi_thread_t* threads, size_t size)
|
||||
{
|
||||
assert(threads != NULL && "fcgi_threads_stop: fcgi_thread_t* threads array == NULL");
|
||||
assert(size != 0 && "fcgi_threads_stop: size_t size (of threads array) == 0");
|
||||
|
||||
size_t i = size;
|
||||
|
||||
while (i--) {
|
||||
atomic_flag_clear(&threads[i].run);
|
||||
}
|
||||
|
||||
i = size;
|
||||
|
||||
while (i--) {
|
||||
fcgi_thread_stop(&threads[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int fcgi_threads_start(fcgi_thread_t* threads, size_t size, fcgi_thread_main_t main, void* ctx)
|
||||
{
|
||||
assert(threads != NULL && "fcgi_threads_start: fcgi_thread_t* threads array == NULL");
|
||||
assert(size != 0 && "fcgi_threads_start: size_t size (of threads array) == 0");
|
||||
assert(main != NULL && "fcgi_threads_start: fcgi_thread_main_t main == NULL");
|
||||
/* ctx may be NULL */
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (fcgi_thread_start(&threads[i], main, ctx) != 0) {
|
||||
LOG_ERROR("fcgi_thread_start failed");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
fcgi_threads_stop(threads, i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int fcgi_server_thread_pool_create(fcgi_thread_pool_t* thread_pool, size_t size, HFcgi hfcgi)
|
||||
{
|
||||
assert(thread_pool != NULL && "fcgi_server_thread_pool_create: fcgi_thread_pool_t* thread_pool == NULL");
|
||||
assert(size != 0 && "fcgi_server_thread_pool_create: size_t size (of threads array) == 0");
|
||||
assert(hfcgi != NULL && "fcgi_server_thread_pool_create: HFcgi hfcgi == NULL");
|
||||
|
||||
thread_pool->threads = (fcgi_thread_t*)malloc(size * sizeof(fcgi_thread_t));
|
||||
if (thread_pool->threads == NULL) {
|
||||
LOG_ERROR("Could not allocate memory of %zu for %zu thread objects",
|
||||
size * sizeof(fcgi_thread_t), size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (fcgi_threads_start(thread_pool->threads, size, fcgi_worker_main, (void*)hfcgi) != 0) {
|
||||
LOG_ERROR("fcgi_threads_start failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
thread_pool->size = size;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
free(thread_pool->threads);
|
||||
LOG_ERROR("fcgi_server_thread_pool_create exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void fcgi_server_thread_pool_dispose(fcgi_thread_pool_t* thread_pool)
|
||||
{
|
||||
assert(thread_pool != NULL && "fcgi_server_thread_pool_dispose: fcgi_thread_pool_t* thread_pool == NULL");
|
||||
|
||||
if (thread_pool->threads == NULL) return;
|
||||
|
||||
fcgi_threads_stop(thread_pool->threads, thread_pool->size);
|
||||
free(thread_pool->threads);
|
||||
}
|
||||
79
src/fcgisrv/fcgi_utils.c
Normal file
79
src/fcgisrv/fcgi_utils.c
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#include "fcgi_server_internal.h"
|
||||
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
int
|
||||
fcgi_get_content_length(const FCGX_Request* request)
|
||||
{
|
||||
int content_length = 0;
|
||||
char* p = FCGX_GetParam(FCGI_PARAM_NAME_CONTENT_LENGTH, request->envp);
|
||||
if (p != NULL) {
|
||||
content_length = atoi(p);
|
||||
}
|
||||
|
||||
return content_length;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
fcgi_request_get_param(const FCGX_Request* request, const char* name, /*out*/ bool* err)
|
||||
{
|
||||
assert(request != NULL);
|
||||
assert(name != NULL);
|
||||
assert(err != NULL);
|
||||
|
||||
char* param_value = FCGX_GetParam(name, request->envp);
|
||||
if (param_value == NULL) {
|
||||
LOG_ERROR("Could not get fcgi parameter '%s'", name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_DEBUG("fcgi_request_get_param. name: %s, value: %s", name, param_value);
|
||||
if (err) {
|
||||
*err = false;
|
||||
}
|
||||
return param_value;
|
||||
|
||||
error:
|
||||
LOG_ERROR("fcgi_request_get_param exit with error");
|
||||
if (err) {
|
||||
*err = true;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
fcgi_handler_status_t
|
||||
fcgi_printf_str(const FCGX_Request* request, const char* response, int response_len)
|
||||
{
|
||||
assert(request != NULL);
|
||||
assert(response != NULL);
|
||||
|
||||
//int n = FCGX_FPrintF(request->out, response);
|
||||
int n = FCGX_PutStr(response, response_len, request->out);
|
||||
if (n < 0) {
|
||||
LOG_ERROR("FCGX_PutStr failed");
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
if (n != response_len) {
|
||||
LOG_ERROR("response_len (%d) differs from the number of characters written (%d)",
|
||||
response_len, n);
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
return HANDLER_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
fcgi_handler_status_t
|
||||
fcgi_printf_header(const FCGX_Request* request, const char* name, const char* value, int value_len)
|
||||
{
|
||||
if (FCGX_FPrintF(request->out, "%s: %.*s"CRLF, name, value_len, value) == -1) {
|
||||
LOG_ERROR("FCGX_FPrintF failed");
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
return HANDLER_SUCCESS;
|
||||
}
|
||||
189
src/fcgisrv/fcgi_utils.h
Normal file
189
src/fcgisrv/fcgi_utils.h
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
#ifndef FCGI_UTILS_H_INCLUDED
|
||||
#define FCGI_UTILS_H_INCLUDED
|
||||
|
||||
|
||||
#include "fcgi_server.h"
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include "stdbool.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/************************** fastCGI parameters: *******************************/
|
||||
|
||||
|
||||
#define FCGI_PARAM_NAME_REMOTE_ADDR "REMOTE_ADDR"
|
||||
#define FCGI_PARAM_NAME_HTTP_COOKIE "HTTP_COOKIE"
|
||||
#define FCGI_PARAM_NAME_REQUEST_URI "REQUEST_URI"
|
||||
#define FCGI_PARAM_NAME_CONTENT_LENGTH "CONTENT_LENGTH"
|
||||
#define FCGI_PARAM_NAME_CONTENT_TYPE "CONTENT_TYPE"
|
||||
#define FCGI_PARAM_NAME_REQUEST_METHOD "REQUEST_METHOD"
|
||||
#define FCGI_PARAM_NAME_SERVER_ADDR "SERVER_ADDR"
|
||||
#define FCGI_PARAM_NAME_SERVER_PORT "SERVER_PORT"
|
||||
#define FCGI_PARAM_NAME_SERVER_NAME "SERVER_NAME"
|
||||
#define FCGI_PARAM_NAME_DOCUMENT_URI "DOCUMENT_URI"
|
||||
#define FCGI_PARAM_NAME_QUERY_STRING "QUERY_STRING"
|
||||
#define FCGI_PARAM_NAME_REQUEST_SCHEME "REQUEST_SCHEME"
|
||||
#define FCGI_PARAM_NAME_HOST "HTTP_HOST"
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
static inline void
|
||||
fcgi_request_print_envp(const FCGX_Request* request)
|
||||
{
|
||||
if (request == NULL) return;
|
||||
|
||||
char **envp = request->envp;
|
||||
if (envp == NULL) return;
|
||||
|
||||
int i = 0;
|
||||
for (; envp[i] != NULL; i++) {
|
||||
LOG_DEBUG("%d: %s", i, envp[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @note just pointer is copied
|
||||
*/
|
||||
char* fcgi_request_get_param(const FCGX_Request* request, const char* name, bool* err);
|
||||
|
||||
|
||||
int
|
||||
fcgi_get_content_length(const FCGX_Request* request);
|
||||
|
||||
|
||||
fcgi_handler_status_t
|
||||
fcgi_printf_str(const FCGX_Request* request, const char* response, int response_len);
|
||||
|
||||
|
||||
fcgi_handler_status_t
|
||||
fcgi_printf_header(const FCGX_Request* request, const char* name, const char* value, int value_len);
|
||||
|
||||
|
||||
/************************** fastCGI handlers: *********************************/
|
||||
|
||||
|
||||
#define CRLF "\r\n"
|
||||
|
||||
#define FCGI_400_RESPONSE_FORMAT \
|
||||
"Status: 400 Bad Request" CRLF\
|
||||
"Content-type: text/plain" CRLF\
|
||||
CRLF\
|
||||
"400 Bad Request" CRLF
|
||||
|
||||
#define FCGI_401_RESPONSE_FORMAT \
|
||||
"Status: 401 Unauthorized" CRLF\
|
||||
"Content-type: text/plain" CRLF\
|
||||
CRLF\
|
||||
"401 Unauthorized" CRLF
|
||||
|
||||
#define FCGI_404_RESPONSE_FORMAT \
|
||||
"Status: 404 Not Found" CRLF\
|
||||
"Content-type: text/plain" CRLF\
|
||||
CRLF\
|
||||
"404 Not Found" CRLF
|
||||
|
||||
#define FCGI_405_RESPONSE_FORMAT \
|
||||
"Status: 405 Method Not Allowed" CRLF\
|
||||
"Content-type: text/plain" CRLF\
|
||||
CRLF\
|
||||
"405 Method Not Allowed" CRLF
|
||||
|
||||
#define FCGI_406_RESPONSE_FORMAT \
|
||||
"Status: 406 Not Acceptable" CRLF\
|
||||
"Content-type: text/plain" CRLF\
|
||||
CRLF\
|
||||
"406 Not Acceptable" CRLF
|
||||
|
||||
#define FCGI_413_RESPONSE_FORMAT \
|
||||
"Status: 413 Request Entity Too Large " CRLF\
|
||||
"Content-type: text/plain" CRLF\
|
||||
CRLF\
|
||||
"413 Request Entity Too Large " CRLF
|
||||
|
||||
#define FCGI_500_RESPONSE_FORMAT \
|
||||
"Status: 500 Internal Server Error" CRLF\
|
||||
"Content-type: text/plain" CRLF\
|
||||
CRLF\
|
||||
"500 Internal Server Error" CRLF
|
||||
|
||||
|
||||
|
||||
#define FCGI_CHECK_PRINTF_STATUS(request, format, code) \
|
||||
(HANDLER_ERROR != fcgi_printf_str(request, format, sizeof(format) - 1)) ? \
|
||||
code : HANDLER_ERROR
|
||||
|
||||
|
||||
typedef fcgi_handler_status_t (*fcgi_request_handler_pt)(const FCGX_Request* request, void* ctx);
|
||||
typedef fcgi_request_handler_pt (*FCGI_REQUEST_FINALIZE_HANDLER)(int rc);
|
||||
|
||||
|
||||
static inline fcgi_handler_status_t
|
||||
fcgi_400_bad_request_handler(const FCGX_Request* request, void* ctx __attribute__((unused)))
|
||||
{
|
||||
LOG_INFO("response status: '400 Bad Request'");
|
||||
return FCGI_CHECK_PRINTF_STATUS(request, FCGI_400_RESPONSE_FORMAT, 400);
|
||||
}
|
||||
|
||||
|
||||
static inline fcgi_handler_status_t
|
||||
fcgi_401_unauthorized_handler(const FCGX_Request* request, void* ctx __attribute__((unused)))
|
||||
{
|
||||
LOG_INFO("response status: '401 Unauthorized'");
|
||||
return FCGI_CHECK_PRINTF_STATUS(request, FCGI_401_RESPONSE_FORMAT, 401);
|
||||
}
|
||||
|
||||
|
||||
static inline fcgi_handler_status_t
|
||||
fcgi_404_not_found_handler(const FCGX_Request* request, void* ctx __attribute__((unused)))
|
||||
{
|
||||
LOG_INFO("response status: '404 Not Found'");
|
||||
return FCGI_CHECK_PRINTF_STATUS(request, FCGI_404_RESPONSE_FORMAT, 404);
|
||||
}
|
||||
|
||||
|
||||
static inline fcgi_handler_status_t
|
||||
fcgi_405_method_not_allowed_handler(const FCGX_Request* request, void* ctx __attribute__((unused)))
|
||||
{
|
||||
LOG_INFO("response status: '405 Method Not Allowed'");
|
||||
return FCGI_CHECK_PRINTF_STATUS(request, FCGI_405_RESPONSE_FORMAT, 405);
|
||||
}
|
||||
|
||||
|
||||
static inline fcgi_handler_status_t
|
||||
fcgi_406_not_acceptable_handler(const FCGX_Request* request, void* ctx __attribute__((unused)))
|
||||
{
|
||||
LOG_INFO("response status: '406 Not Acceptable'");
|
||||
return FCGI_CHECK_PRINTF_STATUS(request, FCGI_406_RESPONSE_FORMAT, 406);
|
||||
}
|
||||
|
||||
|
||||
static inline fcgi_handler_status_t
|
||||
fcgi_413_request_entity_too_large_handler(const FCGX_Request* request, void* ctx __attribute__((unused)))
|
||||
{
|
||||
LOG_INFO("response status: '413 Request Entity Too Large'");
|
||||
return FCGI_CHECK_PRINTF_STATUS(request, FCGI_413_RESPONSE_FORMAT, 413);
|
||||
}
|
||||
|
||||
|
||||
static inline fcgi_handler_status_t
|
||||
fcgi_500_internal_server_error_handler(const FCGX_Request* request, void* ctx __attribute__((unused)))
|
||||
{
|
||||
LOG_ERROR("response status: '500 Internal Server Error'");
|
||||
return FCGI_CHECK_PRINTF_STATUS(request, FCGI_500_RESPONSE_FORMAT, 500);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FCGI_UTILS_H_INCLUDED */
|
||||
139
src/fcgisrv/fcgi_worker.c
Normal file
139
src/fcgisrv/fcgi_worker.c
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#include "fcgi_server_internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define FCGI_DEFAULT_RESPONSE_FORMAT \
|
||||
"Content-type: text/plain" CRLF\
|
||||
CRLF\
|
||||
"FCGI_SERVER" CRLF\
|
||||
"Hello from FCGI server!" CRLF\
|
||||
"Request number %d" CRLF
|
||||
|
||||
|
||||
fcgi_handler_status_t fcgi_default_handler_func(FCGX_Request* request, void* ctx);
|
||||
|
||||
fcgi_handler_t fcgi_default_handler = { fcgi_default_handler_func, NULL };
|
||||
|
||||
|
||||
fcgi_handler_status_t
|
||||
fcgi_default_handler_func(FCGX_Request* request, void* ctx)
|
||||
{
|
||||
assert(request != NULL && "fcgi_default_handler_func: FCGX_Request* request == NULL");
|
||||
/* ctx may be NULL */
|
||||
|
||||
static int counter = 0;
|
||||
|
||||
LOG_DEBUG(FCGI_DEFAULT_RESPONSE_FORMAT, ++counter);
|
||||
FCGX_FPrintF(request->out, FCGI_DEFAULT_RESPONSE_FORMAT, ++counter);
|
||||
|
||||
/* print envp */
|
||||
size_t i;
|
||||
for (i = 0; request->envp[i] != NULL; ++i) {
|
||||
LOG_DEBUG("envp[%zu]: %s" CRLF, i, request->envp[i]);
|
||||
FCGX_FPrintF(request->out, "envp[%d]: %s" CRLF, i, request->envp[i]);
|
||||
}
|
||||
|
||||
return HANDLER_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static fcgi_handler_t*
|
||||
fcgi_route_request(HFcgi hfcgi, FCGX_Request* request)
|
||||
{
|
||||
assert(hfcgi != NULL);
|
||||
assert(request != NULL);
|
||||
|
||||
/* do request routing; map request-path to handler */
|
||||
/* evaluate hash of request path */
|
||||
char* path = FCGX_GetParam(FCGI_PARAM_NAME_DOCUMENT_URI, request->envp);
|
||||
if (path == NULL) {
|
||||
LOG_ERROR("Could not get DOCUMENT_URI of request");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fcgi_handler_map_get(&hfcgi->handler_map, path);
|
||||
}
|
||||
|
||||
|
||||
static fcgi_handler_status_t
|
||||
fcgi_handle_request(HFcgi hfcgi, FCGX_Request* request)
|
||||
{
|
||||
LOG_TRACE("fcgi_handle_request");
|
||||
|
||||
assert(hfcgi != NULL && "fcgi_handle_request: HFcgi hfcgi == NULL");
|
||||
assert(request != NULL && "fcgi_handle_request: FCGX_Request* request == NULL");
|
||||
|
||||
fcgi_handler_status_t status;
|
||||
fcgi_handler_t* handler = fcgi_route_request(hfcgi, request);
|
||||
|
||||
if (handler == NULL) {
|
||||
LOG_WARN("appropriate fcgi handler not found");
|
||||
LOG_WARN("Response status: '404 Not Found'");
|
||||
status = fcgi_404_not_found_handler(request, (void*)hfcgi);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* finally - execute handler*/
|
||||
status = handler->execute(request, handler->ctx);
|
||||
|
||||
done:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
fcgi_worker_main(fcgi_thread_t* thread)
|
||||
{
|
||||
LOG_TRACE("fcgi_worker_main enter");
|
||||
|
||||
assert(thread != NULL && "fcgi_worker_main: fcgi_thread_t* thread == NULL");
|
||||
|
||||
fcgi_handler_status_t status = HANDLER_SUCCESS;
|
||||
HFcgi hfcgi = (HFcgi)thread->ctx;
|
||||
|
||||
FCGX_Request request;
|
||||
|
||||
LOG_DEBUG("fcgi_worker_main: hfcgi->listen.fd = %d", hfcgi->listen.fd);
|
||||
|
||||
if (FCGX_InitRequest(&request, hfcgi->listen.fd, 0) != 0) {
|
||||
LOG_ERROR("Could not init request object");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/** init_handler_(); */
|
||||
|
||||
while (atomic_flag_test_and_set(&thread->run)) {
|
||||
pthread_mutex_lock(&hfcgi->listen.accept_mutex);
|
||||
int rc = FCGX_Accept_r(&request);
|
||||
pthread_mutex_unlock(&hfcgi->listen.accept_mutex);
|
||||
|
||||
if (rc != 0) {
|
||||
pthread_mutex_lock(hfcgi->mutex);
|
||||
if (!hfcgi->is_stopping) {
|
||||
LOG_WARN("FCGX_Accept_r failed. Desc: %d", rc);
|
||||
}
|
||||
pthread_mutex_unlock(hfcgi->mutex);
|
||||
//goto error;
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Request accepted:");
|
||||
fcgi_request_print_envp(&request);
|
||||
|
||||
status = fcgi_handle_request(hfcgi, &request);
|
||||
|
||||
FCGX_Finish_r(&request);
|
||||
|
||||
if (status == HANDLER_ERROR) {
|
||||
LOG_ERROR("fcgi_handle_request returned HANDLER_ERROR");
|
||||
//goto error;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_TRACE("fcgi_worker_main exit with success");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("fcgi_worker_main exit with error");
|
||||
return -1;
|
||||
}
|
||||
101
src/main.c
Normal file
101
src/main.c
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#include "master.h"
|
||||
#include "config.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
static int get_app_options(int argc, char* argv[]);
|
||||
static void show_usage();
|
||||
static void show_version();
|
||||
|
||||
static int
|
||||
get_app_options(int argc, char* argv[])
|
||||
{
|
||||
int option_idx = 0;
|
||||
|
||||
static char short_opts[] = "hv";
|
||||
static struct option long_options[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
while (1) {
|
||||
int c = getopt_long(argc, argv, short_opts, long_options, &option_idx);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
show_usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
case 'v':
|
||||
show_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "get_app_options. unknown option: %s\n", optarg);
|
||||
goto print_and_exit_with_error;
|
||||
} /* switch (c) */
|
||||
} /* while (true) */
|
||||
|
||||
if (optind < argc) {
|
||||
fprintf(stderr, "non-option arguments: ");
|
||||
while (optind < argc) {
|
||||
fprintf(stderr, "%s ", argv[optind++]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
goto print_and_exit_with_error;
|
||||
}
|
||||
|
||||
/* options successfully parsed */
|
||||
return 0;
|
||||
|
||||
print_and_exit_with_error:
|
||||
show_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
show_usage()
|
||||
{
|
||||
printf("Usage: ervu-esia-module [-hv]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -h, --help : Show this help, then exit\n"
|
||||
" -v, --version : Show version, then exit\n");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
show_version()
|
||||
{
|
||||
printf("Version: " ESIA_MODULE_VERSION "\n");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
int rc = -1;
|
||||
service_manager_conf_t conf;
|
||||
|
||||
get_app_options(argc, argv);
|
||||
|
||||
if (service_manager_load_conf(CONF_NAME, &conf)) {
|
||||
LOG_ERROR("service_manager_load_conf failed");
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = master_main(&conf);
|
||||
|
||||
end:
|
||||
service_manager_clear_conf(&conf);
|
||||
return rc;
|
||||
}
|
||||
75
src/main_conf.c
Normal file
75
src/main_conf.c
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include "main_conf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAIN_CONF_SECTION "main"
|
||||
#define MAIN_CONF_KEY_WORKER_PROCESSES "worker_processes"
|
||||
|
||||
/* default configuration values: */
|
||||
static const int FCGI_CONF_DEFAULT_WORKER_PROCESSES = 10;
|
||||
|
||||
static char*
|
||||
copy_filename(const char *filename)
|
||||
{
|
||||
char *str = strdup(filename);
|
||||
if (NULL == str) {
|
||||
LOG_ERROR("strdup failed");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main_conf_load(main_conf_t* conf, const char *filename, const conf_file_context_t conf_file)
|
||||
{
|
||||
LOG_TRACE("main_conf_load enter");
|
||||
|
||||
assert(filename != NULL);
|
||||
assert(conf_file != NULL);
|
||||
assert(conf != NULL);
|
||||
|
||||
memset(conf, 0, sizeof(main_conf_t));
|
||||
|
||||
conf_file_field_t fields[] = {
|
||||
{
|
||||
MAIN_CONF_SECTION,
|
||||
MAIN_CONF_KEY_WORKER_PROCESSES,
|
||||
&(conf->worker_processes),
|
||||
CONF_FILE_VALUE_INTEGER,
|
||||
CONF_FILE_VALUE_POSITIVE_INT,
|
||||
&FCGI_CONF_DEFAULT_WORKER_PROCESSES
|
||||
},
|
||||
};
|
||||
|
||||
if (conf_file_load_values(conf_file, fields, sizeof(fields) / sizeof(conf_file_field_t))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
conf->conf_file = copy_filename(filename);
|
||||
if (NULL == conf->conf_file) {
|
||||
LOG_ERROR("could not copy config filename, %s", filename);
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_INFO("conf_file = %s", conf->conf_file);
|
||||
LOG_TRACE("main_conf_load exit with success");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
main_conf_clear(conf);
|
||||
LOG_ERROR("main_conf_load exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
main_conf_clear(main_conf_t* conf)
|
||||
{
|
||||
if (conf == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
free(conf->conf_file);
|
||||
memset(conf, 0, sizeof(main_conf_t));
|
||||
}
|
||||
15
src/main_conf.h
Normal file
15
src/main_conf.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef MAIN_CONF_H_INCLUDED
|
||||
#define MAIN_CONF_H_INCLUDED
|
||||
|
||||
#include "utils/conf_file_context.h"
|
||||
|
||||
typedef struct main_conf_s {
|
||||
int worker_processes;
|
||||
char *conf_file;
|
||||
} main_conf_t;
|
||||
|
||||
|
||||
int main_conf_load(main_conf_t* config, const char *filename, const conf_file_context_t conf_file);
|
||||
void main_conf_clear(main_conf_t* config);
|
||||
|
||||
#endif // MAIN_CONF_H_INCLUDED
|
||||
220
src/master.c
Normal file
220
src/master.c
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
#include "master.h"
|
||||
#include "worker.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <wait.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
typedef struct fcgi_worker_info_s {
|
||||
pid_t pid;
|
||||
int port;
|
||||
|
||||
} fcgi_worker_info_t;
|
||||
|
||||
static fcgi_worker_info_t *worker_info = NULL;
|
||||
|
||||
static int spawn_worker(const service_manager_conf_t* conf);
|
||||
static void kill_workers(service_manager_conf_t* conf, int sig);
|
||||
|
||||
static inline int
|
||||
worker_info_get_index_by_pid(int worker_processes, pid_t pid)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < worker_processes; ++i) {
|
||||
if (pid == worker_info[i].pid) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
LOG_WARN("worker_info: Could not get index, %d", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
worker_info_init(service_manager_conf_t *conf)
|
||||
{
|
||||
LOG_TRACE("worker_info_init enter");
|
||||
|
||||
assert(conf != NULL && "worker_info_init: conf is NULL");
|
||||
|
||||
const int listen_port = conf->fcgi_cf.listen.port;
|
||||
|
||||
int i = 0;
|
||||
for (i = 0; i < conf->main_cf.worker_processes; ++i) {
|
||||
// создаём потомка
|
||||
worker_info[i].port = listen_port + i;
|
||||
conf->fcgi_cf.listen.port = worker_info[i].port;
|
||||
worker_info[i].pid = spawn_worker(conf);
|
||||
}
|
||||
|
||||
LOG_TRACE("worker_info_init exit");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fork_worker(service_manager_conf_t* conf, pid_t pid)
|
||||
{
|
||||
LOG_TRACE("fork_worker enter");
|
||||
|
||||
assert(conf != NULL && "fork_worker: conf is NULL");
|
||||
|
||||
int i = worker_info_get_index_by_pid(conf->main_cf.worker_processes, pid);
|
||||
if (i != -1) {
|
||||
conf->fcgi_cf.listen.port = worker_info[i].port;
|
||||
worker_info[i].pid = spawn_worker(conf);
|
||||
LOG_DEBUG("fork a new child pid %d", worker_info[i].pid);
|
||||
}
|
||||
|
||||
LOG_TRACE("fork_worker exit");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
master_main(service_manager_conf_t* conf)
|
||||
{
|
||||
LOG_TRACE("master_main enter");
|
||||
|
||||
int status;
|
||||
int need_start = 1;
|
||||
sigset_t sigset;
|
||||
siginfo_t siginfo;
|
||||
|
||||
assert(conf != NULL);
|
||||
|
||||
worker_info = calloc(conf->main_cf.worker_processes, sizeof(fcgi_worker_info_t));
|
||||
if (worker_info == NULL) {
|
||||
LOG_ERROR("Could not allocate memory for worker_info");
|
||||
LOG_ERROR("master_main exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// настраиваем сигналы которые будем обрабатывать
|
||||
sigemptyset(&sigset);
|
||||
|
||||
// сигнал остановки процесса пользователем
|
||||
sigaddset(&sigset, SIGQUIT);
|
||||
|
||||
// сигнал для остановки процесса пользователем с терминала
|
||||
sigaddset(&sigset, SIGINT);
|
||||
|
||||
// сигнал запроса завершения процесса
|
||||
sigaddset(&sigset, SIGTERM);
|
||||
|
||||
// сигнал посылаемый при изменении статуса дочернего процесс
|
||||
sigaddset(&sigset, SIGCHLD);
|
||||
|
||||
sigprocmask(SIG_BLOCK, &sigset, NULL);
|
||||
|
||||
worker_info_init(conf);
|
||||
|
||||
// бесконечный цикл работы
|
||||
for (;;) {
|
||||
// если необходимо создать потомка
|
||||
if (need_start) {
|
||||
pid_t pid;
|
||||
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
||||
/* look for exiting child's pid */
|
||||
fork_worker(conf, pid);
|
||||
}
|
||||
}
|
||||
|
||||
need_start = 1;
|
||||
|
||||
// ожидаем поступление сигнала
|
||||
sigwaitinfo(&sigset, &siginfo);
|
||||
|
||||
LOG_DEBUG("[MASTER] signal received: %s", strsignal(siginfo.si_signo));
|
||||
|
||||
// если пришел сигнал от потомка
|
||||
if (siginfo.si_signo == SIGCHLD) {
|
||||
// получаем статус завершение
|
||||
wait(&status);
|
||||
|
||||
// преобразуем статус в нормальный вид
|
||||
status = WEXITSTATUS(status);
|
||||
|
||||
// если потомок завершил работу с кодом говорящем о том, что нет нужны дальше работать
|
||||
if (status == CHILD_NEED_TERMINATE) {
|
||||
// запишем в лог сообщени об этом
|
||||
LOG_INFO("[MASTER] Child stopped");
|
||||
|
||||
// убьем потомков
|
||||
kill_workers(conf, SIGTERM);
|
||||
|
||||
status = 0;
|
||||
|
||||
// прервем цикл
|
||||
break;
|
||||
|
||||
} else if (status == CHILD_NEED_WORK) { // если требуется перезапустить потомка
|
||||
// запишем в лог данное событие
|
||||
LOG_INFO("[MASTER] Child restart");
|
||||
fork_worker(conf, siginfo.si_pid);
|
||||
}
|
||||
} else { // если пришел какой-либо другой ожидаемый сигнал
|
||||
// запишем в лог информацию о пришедшем сигнале
|
||||
LOG_INFO("[MASTER] Signal %s", strsignal(siginfo.si_signo));
|
||||
|
||||
// убьем потомков
|
||||
kill_workers(conf, SIGTERM);
|
||||
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// запишем в лог, что мы остановились
|
||||
LOG_INFO("[MASTER] Stopped");
|
||||
|
||||
free(worker_info);
|
||||
|
||||
LOG_TRACE("master_main exit with status '%d'", status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static pid_t
|
||||
spawn_worker(const service_manager_conf_t* conf)
|
||||
{
|
||||
assert(conf != NULL);
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1) { // если произошла ошибка
|
||||
// запишем в лог сообщение об этом
|
||||
LOG_ERROR("[MASTER] fork() failed; (%s)", strerror(errno));
|
||||
|
||||
} else if (!pid) { // если мы потомок
|
||||
// запустим функцию отвечающую за работу демона
|
||||
int rc = worker_main(conf);
|
||||
|
||||
// завершим процесс
|
||||
exit(rc);
|
||||
|
||||
} else { // если мы родитель
|
||||
// данный код выполняется в родителе
|
||||
LOG_DEBUG("[MASTER] fork() succeded; worker pid: %d", pid);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
kill_workers(service_manager_conf_t* conf, int sig)
|
||||
{
|
||||
LOG_TRACE("kill_workers enter");
|
||||
|
||||
assert(conf != NULL);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < conf->main_cf.worker_processes; i++) {
|
||||
// убьем потомка
|
||||
kill(worker_info[i].pid, sig);
|
||||
LOG_TRACE("worker#%d (pid: %d) killed", i, worker_info[i].pid);
|
||||
}
|
||||
|
||||
LOG_TRACE("kill_workers exit");
|
||||
}
|
||||
9
src/master.h
Normal file
9
src/master.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef MASTER_H_INCLUDED
|
||||
#define MASTER_H_INCLUDED
|
||||
|
||||
#include "service_manager.h"
|
||||
|
||||
|
||||
int master_main(service_manager_conf_t* conf);
|
||||
|
||||
#endif // MASTER_H_INCLUDED
|
||||
375
src/modules/service_sign.c
Normal file
375
src/modules/service_sign.c
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
#include "service_sign.h"
|
||||
|
||||
#include "fcgisrv/fcgi_utils.h"
|
||||
|
||||
#include "utils/cryptopro.h"
|
||||
|
||||
#define SIGN_CONF_SECTION "sign"
|
||||
#define SIGN_CONF_KEY_LOCATION "location"
|
||||
#define SIGN_CONF_KEY_CP_FILE "cp_file"
|
||||
#define SIGN_CONF_KEY_SIGNER "signer_subject"
|
||||
#define SIGN_CONF_KEY_PIN "pin"
|
||||
|
||||
#define FCGI_OK_RESPONSE_FORMAT \
|
||||
"Content-type: text/plain" CRLF\
|
||||
CRLF\
|
||||
"%.*s" CRLF
|
||||
|
||||
static const str_t SIGN_CONF_DEFAULT_LOCATION = str_t_const("/sign");
|
||||
|
||||
static const int CLIENT_MAX_BODY_SIZE = 4096;
|
||||
static const str_t ACCEPTABLE_CONTENT_TYPE = str_t_const("text/plain");
|
||||
|
||||
typedef struct sign_service_s {
|
||||
const sign_conf_t *conf;
|
||||
|
||||
cryptopro_context_t cryptopro_ctx;
|
||||
|
||||
} sign_service_t;
|
||||
|
||||
typedef struct fcgi_sign_request_s {
|
||||
char *content;
|
||||
int content_length;
|
||||
|
||||
str_t signed_content;
|
||||
|
||||
} fcgi_sign_request_t;
|
||||
|
||||
static void fcgi_sign_request_clear(fcgi_sign_request_t *req_info);
|
||||
|
||||
static fcgi_request_handler_pt fcgi_request_finalize_handler(fcgi_handler_status_t status);
|
||||
|
||||
static fcgi_handler_status_t check_content_length(int content_length);
|
||||
|
||||
static fcgi_handler_status_t check_content_type(const FCGX_Request* request);
|
||||
|
||||
static fcgi_handler_status_t fcgi_request_load_data(const FCGX_Request* request,
|
||||
int content_length, char** content);
|
||||
|
||||
static int sign_content(const sign_service_t *hsign, fcgi_sign_request_t *req_info);
|
||||
|
||||
int
|
||||
sign_conf_load(sign_conf_t *conf, const conf_file_context_t conf_file)
|
||||
{
|
||||
LOG_TRACE("sign_conf_load enter");
|
||||
|
||||
memset(conf, 0, sizeof(sign_conf_t));
|
||||
|
||||
conf_file_field_t fields[] = {
|
||||
{
|
||||
SIGN_CONF_SECTION,
|
||||
SIGN_CONF_KEY_LOCATION,
|
||||
&conf->location,
|
||||
CONF_FILE_VALUE_STRT,
|
||||
CONF_FILE_VALUE_LOCATION,
|
||||
&SIGN_CONF_DEFAULT_LOCATION
|
||||
},
|
||||
{
|
||||
SIGN_CONF_SECTION,
|
||||
SIGN_CONF_KEY_CP_FILE,
|
||||
&(conf->cp_file),
|
||||
CONF_FILE_VALUE_STRING,
|
||||
CONF_FILE_VALUE_NONE,
|
||||
NULL
|
||||
},
|
||||
{
|
||||
SIGN_CONF_SECTION,
|
||||
SIGN_CONF_KEY_SIGNER,
|
||||
&(conf->signer),
|
||||
CONF_FILE_VALUE_STRING,
|
||||
CONF_FILE_VALUE_NONE,
|
||||
NULL
|
||||
},
|
||||
{
|
||||
SIGN_CONF_SECTION,
|
||||
SIGN_CONF_KEY_PIN,
|
||||
&(conf->pin),
|
||||
CONF_FILE_VALUE_STRT,
|
||||
CONF_FILE_VALUE_PSWD,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
if (conf_file_load_values(conf_file, fields, sizeof(fields) / sizeof(conf_file_field_t))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_TRACE("sign_conf_load exit");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
sign_conf_clear(conf);
|
||||
LOG_ERROR("sign_conf_load exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
sign_conf_clear(sign_conf_t *conf)
|
||||
{
|
||||
LOG_TRACE("sign_conf_clear");
|
||||
|
||||
if (conf == NULL) return;
|
||||
|
||||
if (conf->pin.data != 0) {
|
||||
explicit_bzero(conf->pin.data, conf->pin.len);
|
||||
}
|
||||
str_t_clear(&conf->pin);
|
||||
|
||||
str_t_clear(&conf->location);
|
||||
free(conf->cp_file);
|
||||
free(conf->signer);
|
||||
|
||||
memset(conf, 0, sizeof(sign_conf_t));
|
||||
}
|
||||
|
||||
HSign
|
||||
sign_service_create(const sign_conf_t *conf)
|
||||
{
|
||||
LOG_TRACE("sign_service_create enter");
|
||||
|
||||
sign_service_t *hsign = (sign_service_t*) calloc(1, sizeof(sign_service_t));
|
||||
if (hsign == NULL) {
|
||||
LOG_ERROR("Could not allocate memory for HSign");
|
||||
goto error;
|
||||
}
|
||||
|
||||
hsign->conf = conf;
|
||||
|
||||
if (!cryptopro_init(conf->cp_file)) {
|
||||
LOG_ERROR("Could not init Cryptographic Provider");
|
||||
goto error;
|
||||
}
|
||||
|
||||
cryptopro_context_set(&hsign->cryptopro_ctx, conf->signer, &conf->pin);
|
||||
|
||||
LOG_TRACE("sign_service_create exit");
|
||||
return (HSign)hsign;
|
||||
|
||||
error:
|
||||
sign_service_free((HSign)hsign);
|
||||
LOG_ERROR("sign_service_create exit with error");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
sign_service_free(HSign hsign)
|
||||
{
|
||||
sign_service_t *ctx = (sign_service_t *) hsign;
|
||||
|
||||
if (ctx == NULL) return;
|
||||
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
fcgi_handler_status_t
|
||||
fcgi_sign_handler(FCGX_Request* request, void* ctx)
|
||||
{
|
||||
fcgi_handler_status_t status = HANDLER_ERROR;
|
||||
const sign_service_t *hsign = (const sign_service_t *) ctx;
|
||||
fcgi_sign_request_t req_info = {0};
|
||||
|
||||
LOG_TRACE("fcgi_sign_handler enter");
|
||||
|
||||
if (request == NULL) {
|
||||
LOG_ERROR("fcgi_sign_handler exit with error. Desc: request is NULL");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
req_info.content_length = fcgi_get_content_length(request);
|
||||
|
||||
status = check_content_length(req_info.content_length);
|
||||
if (status != HANDLER_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
status = check_content_type(request);
|
||||
if (status != HANDLER_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
status = fcgi_request_load_data(request, req_info.content_length, &req_info.content);
|
||||
if (status != HANDLER_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (sign_content(hsign, &req_info)) {
|
||||
status = HANDLER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// status = HANDLER_SUCCESS;
|
||||
|
||||
exit:
|
||||
status = fcgi_request_finalize_handler(status)(request, &req_info);
|
||||
|
||||
fcgi_sign_request_clear(&req_info);
|
||||
|
||||
LOG_TRACE("fcgi_sign_handler exit");
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
fcgi_sign_request_clear(fcgi_sign_request_t *req_info)
|
||||
{
|
||||
LOG_TRACE("fcgi_sign_request_clear");
|
||||
|
||||
free(req_info->content);
|
||||
str_t_clear(&(req_info->signed_content));
|
||||
}
|
||||
|
||||
static fcgi_handler_status_t
|
||||
fcgi_ok_handler(const FCGX_Request* request, void *ctx)
|
||||
{
|
||||
LOG_TRACE("fcgi_ok_handler");
|
||||
|
||||
const fcgi_sign_request_t *req_info = (fcgi_sign_request_t*) ctx;
|
||||
|
||||
LOG_DEBUG("response status: " FCGI_OK_RESPONSE_FORMAT,
|
||||
(int) req_info->signed_content.len, req_info->signed_content.data);
|
||||
|
||||
if (FCGX_FPrintF(request->out, FCGI_OK_RESPONSE_FORMAT,
|
||||
(int) req_info->signed_content.len, req_info->signed_content.data) < 0) {
|
||||
LOG_ERROR("FCGX_FPrintF() failed");
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
return HANDLER_SUCCESS;
|
||||
}
|
||||
|
||||
static fcgi_request_handler_pt
|
||||
fcgi_request_finalize_handler(fcgi_handler_status_t status)
|
||||
{
|
||||
fcgi_request_handler_pt handler;
|
||||
|
||||
switch (status) {
|
||||
case HANDLER_SUCCESS:
|
||||
case HANDLER_HTTP_OK:
|
||||
handler = fcgi_ok_handler;
|
||||
break;
|
||||
|
||||
case HANDLER_HTTP_BAD_REQUEST:
|
||||
handler = fcgi_400_bad_request_handler;
|
||||
break;
|
||||
|
||||
case HANDLER_HTTP_NOT_ACCEPTABLE:
|
||||
handler = fcgi_406_not_acceptable_handler;
|
||||
break;
|
||||
|
||||
case HANDLER_HTTP_REQUEST_ENTITY_TOO_LARGE:
|
||||
handler = fcgi_413_request_entity_too_large_handler;
|
||||
break;
|
||||
|
||||
case HANDLER_ERROR:
|
||||
default:
|
||||
handler = fcgi_500_internal_server_error_handler;
|
||||
break;
|
||||
}
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
static fcgi_handler_status_t
|
||||
check_content_length(int content_length)
|
||||
{
|
||||
LOG_TRACE("check_content_length");
|
||||
|
||||
if (content_length > 0 && content_length <= CLIENT_MAX_BODY_SIZE) {
|
||||
LOG_DEBUG("Content-Length is ok");
|
||||
return HANDLER_SUCCESS;
|
||||
}
|
||||
|
||||
if (content_length == 0) {
|
||||
LOG_DEBUG("Content-Length == 0, so there is nothing to sign");
|
||||
return HANDLER_HTTP_OK;
|
||||
}
|
||||
|
||||
if (content_length < 0) {
|
||||
LOG_WARN("Content-Length < 0 (%d)", content_length);
|
||||
return HANDLER_HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if (content_length > CLIENT_MAX_BODY_SIZE) {
|
||||
LOG_WARN("Content-Length (%d) is too large (> %d)",
|
||||
content_length,
|
||||
CLIENT_MAX_BODY_SIZE);
|
||||
return HANDLER_HTTP_REQUEST_ENTITY_TOO_LARGE;
|
||||
}
|
||||
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
static fcgi_handler_status_t
|
||||
check_content_type(const FCGX_Request* request)
|
||||
{
|
||||
LOG_TRACE("check_content_type");
|
||||
|
||||
const char* content_type = FCGX_GetParam(FCGI_PARAM_NAME_CONTENT_TYPE, request->envp);
|
||||
if (content_type == NULL) {
|
||||
LOG_ERROR("Could not get FCGI param: "FCGI_PARAM_NAME_CONTENT_TYPE);
|
||||
return HANDLER_HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if (strncmp(content_type, ACCEPTABLE_CONTENT_TYPE.data, ACCEPTABLE_CONTENT_TYPE.len) == 0) {
|
||||
LOG_DEBUG("Content-Type is ok");
|
||||
return HANDLER_SUCCESS;
|
||||
}
|
||||
|
||||
LOG_WARN("Content-Type is not acceptable, '%s'", content_type);
|
||||
|
||||
return HANDLER_HTTP_NOT_ACCEPTABLE;
|
||||
}
|
||||
|
||||
static fcgi_handler_status_t
|
||||
fcgi_request_load_data(const FCGX_Request* request, int content_length, char** content)
|
||||
{
|
||||
LOG_TRACE("fcgi_request_load_data enter");
|
||||
|
||||
char* data = malloc(content_length + 1);
|
||||
if (data == NULL) {
|
||||
LOG_ERROR("Could not allocate memory for request body (%d bytes)", content_length + 1);
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
||||
int actual_content_length = FCGX_GetStr(data, content_length, request->in);
|
||||
|
||||
if (actual_content_length != content_length) {
|
||||
LOG_ERROR("fcgi_request_load_data: "
|
||||
"actual_content_length(%d) != content_length(%d); content: '%.*s'",
|
||||
actual_content_length, content_length,
|
||||
actual_content_length, data);
|
||||
free(data);
|
||||
return HANDLER_HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
data[content_length] = '\0';
|
||||
*content = data;
|
||||
|
||||
LOG_DEBUG("content_length: %d", content_length);
|
||||
LOG_DEBUG("content: '%s'", *content);
|
||||
|
||||
LOG_TRACE("fcgi_request_load_data exit");
|
||||
|
||||
return HANDLER_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
sign_content(const sign_service_t *hsign, fcgi_sign_request_t *req_info)
|
||||
{
|
||||
LOG_TRACE("sign_content enter");
|
||||
|
||||
str_t content = {
|
||||
.data = req_info->content,
|
||||
.len = req_info->content_length
|
||||
};
|
||||
|
||||
if (cryptopro_sign(&hsign->cryptopro_ctx, &content, &req_info->signed_content)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_TRACE("sign_content exit");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("sign_content exit with error");
|
||||
return -1;
|
||||
}
|
||||
29
src/modules/service_sign.h
Normal file
29
src/modules/service_sign.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef SERVICE_SIGN_H_INCLUDED
|
||||
#define SERVICE_SIGN_H_INCLUDED
|
||||
|
||||
#include "fcgisrv/fcgi_server.h"
|
||||
|
||||
#include "utils/conf_file_context.h"
|
||||
#include "utils/str_t.h"
|
||||
|
||||
typedef struct sign_service_t* HSign;
|
||||
|
||||
typedef struct sign_conf_s {
|
||||
str_t location;
|
||||
|
||||
char *cp_file; /* файл криптопровайдера */
|
||||
|
||||
char *signer; /* субъект контейнера */
|
||||
str_t pin; /* pin-код от контейнера */
|
||||
|
||||
} sign_conf_t;
|
||||
|
||||
int sign_conf_load(sign_conf_t *conf, const conf_file_context_t conf_file);
|
||||
void sign_conf_clear(sign_conf_t *conf);
|
||||
|
||||
HSign sign_service_create(const sign_conf_t *conf);
|
||||
void sign_service_free(HSign hsign);
|
||||
|
||||
fcgi_handler_status_t fcgi_sign_handler(FCGX_Request* request, void* ctx);
|
||||
|
||||
#endif // SERVICE_SIGN_H_INCLUDED
|
||||
245
src/service_manager.c
Normal file
245
src/service_manager.c
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
#include "service_manager.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
static void
|
||||
service_manager_handle_conf_free(service_handle_conf_t *handle_conf)
|
||||
{
|
||||
if (handle_conf != NULL) {
|
||||
str_t_clear(&handle_conf->location);
|
||||
handle_conf->conf_free(handle_conf->conf);
|
||||
free(handle_conf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void*
|
||||
service_manager_handle_conf_load(const conf_file_context_t conf_file, service_handle_conf_t *handle_conf)
|
||||
{
|
||||
assert(conf_file != NULL);
|
||||
assert(handle_conf != NULL);
|
||||
|
||||
void* cfg = handle_conf->conf_load(conf_file, &handle_conf->location);
|
||||
if (cfg == NULL) {
|
||||
LOG_ERROR("service_manager_handle_conf_load - conf_load failed");
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
service_manager_handle_conf_merge(main_conf_t *main_cf, service_handle_conf_t *handle_conf)
|
||||
{
|
||||
assert(main_cf != NULL);
|
||||
assert(main_cf != NULL);
|
||||
|
||||
int rc = handle_conf->conf_merge(main_cf, handle_conf->conf);
|
||||
if (rc) {
|
||||
LOG_ERROR("service_manager_handle_conf_merge - conf_merge failed");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
register_service(service_manager_t* services,
|
||||
const service_manager_conf_t* services_cf,
|
||||
handler_func handler,
|
||||
void* ctx,
|
||||
const str_t* location)
|
||||
{
|
||||
LOG_TRACE("register_service enter");
|
||||
|
||||
char *path = NULL;
|
||||
|
||||
if (location == NULL) {
|
||||
LOG_ERROR("location is NULL");
|
||||
goto error;
|
||||
}
|
||||
if (str_t_is_null(*location)) {
|
||||
LOG_ERROR("location is empty");
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_DEBUG("location: %.*s", (int)location->len, location->data);
|
||||
|
||||
fcgi_handler_t fcgi_handler = { .execute = handler, .ctx = ctx };
|
||||
|
||||
if (location->data[0] != '/') {
|
||||
LOG_ERROR("location must start with '/'");
|
||||
goto error;
|
||||
}
|
||||
|
||||
path = str_t_to_string(location);
|
||||
if (path == NULL) {
|
||||
LOG_ERROR("Could not copy location: %.*s", (int)location->len, location->data);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (fcgi_register_handler(services->hfcgi, path, &fcgi_handler) != 0) {
|
||||
LOG_ERROR("Could not register handler by path: %s", path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
free(path);
|
||||
|
||||
LOG_INFO("service registered, %.*s", (int) location->len, location->data);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("register_service exit with error");
|
||||
free(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
service_handle_conf_t *
|
||||
service_manager_handle_conf_create(const conf_file_context_t conf_file,
|
||||
main_conf_t *main_cf,
|
||||
SERVICE_CONF_INIT_PROC handle_conf_init)
|
||||
{
|
||||
LOG_TRACE("service_manager_handle_conf_create enter");
|
||||
|
||||
service_handle_conf_t *hc = calloc(1, sizeof(service_handle_conf_t));
|
||||
if (hc == NULL) {
|
||||
LOG_ERROR("calloc failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
handle_conf_init(hc);
|
||||
hc->conf = service_manager_handle_conf_load(conf_file, hc);
|
||||
if (hc->conf == NULL) {
|
||||
LOG_ERROR("could not load config");
|
||||
goto error;
|
||||
}
|
||||
if (service_manager_handle_conf_merge(main_cf, hc)) {
|
||||
LOG_ERROR("could not merge config");
|
||||
goto error;
|
||||
}
|
||||
LOG_TRACE("service_manager_handle_conf_create exit with success");
|
||||
return hc;
|
||||
|
||||
error:
|
||||
service_manager_handle_conf_free(hc);
|
||||
LOG_ERROR("service_manager_handle_conf_create exit with error");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
service_manager_load_conf(const char* config_file_name, service_manager_conf_t* services_cf)
|
||||
{
|
||||
conf_file_context_t conf_file = NULL;
|
||||
|
||||
LOG_TRACE("service_manager_load_conf enter");
|
||||
|
||||
assert(config_file_name != NULL);
|
||||
assert(services_cf != NULL);
|
||||
|
||||
memset(services_cf, 0, sizeof(service_manager_conf_t));
|
||||
|
||||
conf_file = conf_file_open_file(config_file_name);
|
||||
if (conf_file == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (main_conf_load(&services_cf->main_cf, config_file_name, conf_file)) {
|
||||
LOG_ERROR("main_conf_load failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (fcgi_load_conf(config_file_name, &services_cf->fcgi_cf)) {
|
||||
LOG_ERROR("FastCGI server configuraton loading failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sign_conf_load(&services_cf->sign_cf, conf_file)) {
|
||||
LOG_ERROR("Sign service configuraton loading failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
conf_file_close_file(conf_file);
|
||||
|
||||
LOG_TRACE("service_manager_load_conf exit with success");
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
service_manager_clear_conf(services_cf);
|
||||
conf_file_close_file(conf_file);
|
||||
LOG_ERROR("service_manager_load_conf exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
service_manager_clear_conf(service_manager_conf_t* services_cf)
|
||||
{
|
||||
assert(services_cf != NULL);
|
||||
|
||||
sign_conf_clear(&services_cf->sign_cf);
|
||||
fcgi_clear_conf(&services_cf->fcgi_cf);
|
||||
main_conf_clear(&services_cf->main_cf);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
deinit_services(service_manager_t* services)
|
||||
{
|
||||
LOG_TRACE("deinit_services enter");
|
||||
|
||||
assert(services != NULL);
|
||||
|
||||
fcgi_stop_server(services->hfcgi);
|
||||
fcgi_dispose_server(services->hfcgi);
|
||||
|
||||
sign_service_free(services->hsign);
|
||||
|
||||
LOG_TRACE("deinit_services exit");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
init_services(service_manager_t* services, const service_manager_conf_t* services_cf)
|
||||
{
|
||||
LOG_TRACE("init_services enter");
|
||||
|
||||
assert(services != NULL);
|
||||
assert(services_cf != NULL);
|
||||
|
||||
/* create fcgi service */
|
||||
services->hfcgi = fcgi_create_server(&services_cf->fcgi_cf);
|
||||
if (services->hfcgi == NULL) {
|
||||
LOG_ERROR("Could not create FastCGI server.");
|
||||
goto error;
|
||||
}
|
||||
|
||||
services->hsign = sign_service_create(&services_cf->sign_cf);
|
||||
if (services->hsign == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (register_service(services, services_cf, fcgi_sign_handler, services->hsign,
|
||||
&services_cf->sign_cf.location)) {
|
||||
LOG_ERROR("Could not register 'sign service'");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* run fastcgi server */
|
||||
if (fcgi_run_server(services->hfcgi) != 0) {
|
||||
LOG_ERROR("Could not run FastCGI server");
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_TRACE("init_services exit with success");
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
deinit_services(services);
|
||||
LOG_ERROR("init_services exit with error");
|
||||
return -1;
|
||||
}
|
||||
76
src/service_manager.h
Normal file
76
src/service_manager.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#ifndef SERVICE_MANAGER_H_INCLUDED
|
||||
#define SERVICE_MANAGER_H_INCLUDED
|
||||
|
||||
#include "main_conf.h"
|
||||
|
||||
#include "utils/str_t.h"
|
||||
|
||||
#include "fcgisrv/fcgi_server.h"
|
||||
|
||||
#include "modules/service_sign.h"
|
||||
|
||||
struct service_s;
|
||||
|
||||
struct service_handle_conf_s;
|
||||
|
||||
typedef struct service_s service_t;
|
||||
|
||||
typedef struct service_handle_conf_s service_handle_conf_t;
|
||||
|
||||
typedef void* service_handle_t;
|
||||
|
||||
typedef void(*SERVICE_CONF_FREE_PROC)(void *conf);
|
||||
typedef void*(*SERVICE_CONF_LOAD_PROC)(const conf_file_context_t conf_file, str_t* location);
|
||||
typedef int(*SERVICE_CONF_MERGE_PROC)(main_conf_t *main_conf, void *conf);
|
||||
typedef int(*SERVICE_CONF_INIT_PROC)(service_handle_conf_t *conf);
|
||||
|
||||
typedef void(*SERVICE_HANDLE_FREE_PROC)(service_handle_t handle);
|
||||
|
||||
|
||||
struct service_handle_conf_s {
|
||||
str_t location;
|
||||
void* conf;
|
||||
SERVICE_CONF_FREE_PROC conf_free;
|
||||
SERVICE_CONF_LOAD_PROC conf_load;
|
||||
SERVICE_CONF_MERGE_PROC conf_merge;
|
||||
};
|
||||
|
||||
struct service_s {
|
||||
service_handle_t handle;
|
||||
handler_func handler;
|
||||
SERVICE_HANDLE_FREE_PROC handle_free;
|
||||
};
|
||||
|
||||
|
||||
typedef struct service_manager_conf_s {
|
||||
fcgi_conf_t fcgi_cf;
|
||||
main_conf_t main_cf;
|
||||
sign_conf_t sign_cf;
|
||||
|
||||
} service_manager_conf_t;
|
||||
|
||||
typedef struct service_manager_s {
|
||||
HFcgi hfcgi;
|
||||
HSign hsign;
|
||||
|
||||
} service_manager_t;
|
||||
|
||||
|
||||
int
|
||||
register_service(service_manager_t* services,
|
||||
const service_manager_conf_t* services_cf,
|
||||
handler_func handler,
|
||||
void* ctx,
|
||||
const str_t* location);
|
||||
|
||||
service_handle_conf_t *
|
||||
service_manager_handle_conf_create(const conf_file_context_t conf_file,
|
||||
main_conf_t *main_cf,
|
||||
SERVICE_CONF_INIT_PROC handle_conf_init);
|
||||
int service_manager_load_conf(const char* config_file_name, service_manager_conf_t* services_cf);
|
||||
void service_manager_clear_conf(service_manager_conf_t* services_cf);
|
||||
|
||||
int init_services(service_manager_t* services, const service_manager_conf_t* services_cf);
|
||||
int deinit_services(service_manager_t* services);
|
||||
|
||||
#endif // SERVICE_MANAGER_H_INCLUDED
|
||||
82
src/utils/base64.c
Normal file
82
src/utils/base64.c
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#include "base64.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static void
|
||||
init_encode_base64_url(unsigned char **table)
|
||||
{
|
||||
static unsigned char basis64_url[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
|
||||
};
|
||||
|
||||
assert(table != NULL);
|
||||
|
||||
*table = basis64_url;
|
||||
}
|
||||
|
||||
void
|
||||
encode_base64_url(str_t *dst, const str_t *src)
|
||||
{
|
||||
size_t n;
|
||||
uint8_t a;
|
||||
uint8_t b;
|
||||
uint8_t c;
|
||||
uint8_t d;
|
||||
const uint8_t *p;
|
||||
|
||||
p = (const uint8_t *) src->data;
|
||||
|
||||
n = src->len / 3;
|
||||
|
||||
unsigned char *basis64_url;
|
||||
init_encode_base64_url(&basis64_url);
|
||||
|
||||
if (src->len == (n * 3 + 1)) {
|
||||
if (src->data != NULL && dst->data != NULL) {
|
||||
a = (p[n * 3] & 0xFC) >> 2;
|
||||
b = (p[n * 3] & 0x03) << 4;
|
||||
|
||||
dst->data[n * 4] = basis64_url[a];
|
||||
dst->data[n * 4 + 1] = basis64_url[b];
|
||||
dst->data[n * 4 + 2] = '\0';
|
||||
}
|
||||
|
||||
dst->len = n * 4 + 2;
|
||||
} else if (src->len == (n * 3 + 2)) {
|
||||
if (src->data != NULL && dst->data != NULL) {
|
||||
a = (p[n * 3] & 0xFC) >> 2;
|
||||
b = ((p[n * 3] & 0x03) << 4) | ((p[n * 3 + 1] & 0xF0) >> 4);
|
||||
c = (p[n * 3 + 1] & 0x0F) << 2;
|
||||
|
||||
dst->data[n * 4] = basis64_url[a];
|
||||
dst->data[n * 4 + 1] = basis64_url[b];
|
||||
dst->data[n * 4 + 2] = basis64_url[c];
|
||||
dst->data[n * 4 + 3] = '\0';
|
||||
}
|
||||
|
||||
dst->len = n * 4 + 3;
|
||||
} else {
|
||||
if (dst->data != NULL) {
|
||||
dst->data[n * 4] = '\0';
|
||||
}
|
||||
dst->len = n * 4;
|
||||
}
|
||||
|
||||
if (src->data != NULL && dst->data != NULL) {
|
||||
while(n-- > 0) {
|
||||
a = (p[n * 3] & 0xFC) >> 2;
|
||||
b = ((p[n * 3] & 0x03) << 4) | ((p[n * 3 + 1] & 0xF0) >> 4);
|
||||
c = ((p[n * 3 + 1] & 0x0F) << 2) | ((p[n * 3 + 2] & 0xC0) >> 6);
|
||||
d = p[n * 3 + 2] & 0x3F;
|
||||
|
||||
dst->data[n * 4] = basis64_url[a];
|
||||
dst->data[n * 4 + 1] = basis64_url[b];
|
||||
dst->data[n * 4 + 2] = basis64_url[c];
|
||||
dst->data[n * 4 + 3] = basis64_url[d];
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/utils/base64.h
Normal file
10
src/utils/base64.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
|
||||
#include "str_t.h"
|
||||
|
||||
#define base64_encoded_length(len) (((len + 2) / 3) * 4)
|
||||
|
||||
void encode_base64_url(str_t *dst, const str_t *src);
|
||||
|
||||
#endif // BASE64_H
|
||||
37
src/utils/capi.c
Normal file
37
src/utils/capi.c
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#include "capi.h"
|
||||
|
||||
#include "library.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define LIBRARY_RESOLVE(fn, lib, fn_name) fn = lib->resolve(lib, fn_name); if (fn == NULL) return false
|
||||
|
||||
|
||||
bool
|
||||
capi_function_list_init(library_t *lib, capi_function_list_t *fl)
|
||||
{
|
||||
assert(lib != NULL);
|
||||
assert(fl != NULL);
|
||||
|
||||
LIBRARY_RESOLVE(fl->CertCloseStore, lib, "CertCloseStore");
|
||||
LIBRARY_RESOLVE(fl->CertFindCertificateInStore, lib, "CertFindCertificateInStore");
|
||||
LIBRARY_RESOLVE(fl->CertFreeCertificateContext, lib, "CertFreeCertificateContext");
|
||||
LIBRARY_RESOLVE(fl->CertGetCertificateContextProperty, lib, "CertGetCertificateContextProperty");
|
||||
LIBRARY_RESOLVE(fl->CertOpenStore, lib, "CertOpenStore");
|
||||
LIBRARY_RESOLVE(fl->CertSetCertificateContextProperty, lib, "CertSetCertificateContextProperty");
|
||||
LIBRARY_RESOLVE(fl->CryptAcquireCertificatePrivateKey, lib, "CryptAcquireCertificatePrivateKey");
|
||||
LIBRARY_RESOLVE(fl->CryptCreateHash, lib, "CryptCreateHash");
|
||||
LIBRARY_RESOLVE(fl->CryptDestroyHash, lib, "CryptDestroyHash");
|
||||
LIBRARY_RESOLVE(fl->CryptHashData, lib, "CryptHashData");
|
||||
LIBRARY_RESOLVE(fl->CryptReleaseContext, lib, "CryptReleaseContext");
|
||||
LIBRARY_RESOLVE(fl->CryptSignMessage, lib, "CryptSignMessage");
|
||||
LIBRARY_RESOLVE(fl->GetLastError, lib, "GetLastError");
|
||||
|
||||
#ifdef UNICODE
|
||||
LIBRARY_RESOLVE(fl->CryptSignHash, lib, "CryptSignHashW");
|
||||
#else
|
||||
LIBRARY_RESOLVE(fl->CryptSignHash, lib, "CryptSignHashA");
|
||||
#endif // !UNICODE
|
||||
|
||||
return true;
|
||||
}
|
||||
154
src/utils/capi.h
Normal file
154
src/utils/capi.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
#ifndef CAPI_H_INCLUDED
|
||||
#define CAPI_H_INCLUDED
|
||||
|
||||
#define SIZEOF_VOID_P (UINTPTR_MAX+0 > 0xffffffffUL ? 8 : 4)
|
||||
|
||||
typedef struct library_s library_t;
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <cades.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DECLARE_FN(api, type, name, args) typedef WINAPI type (*name ## _FN) args
|
||||
|
||||
DECLARE_FN(WINBASEAPI, DWORD, GET_LAST_ERROR, (void));
|
||||
DECLARE_FN(WINADVAPI, BOOL, CRYPT_RELEASE_CONTEXT, (HCRYPTPROV hProv, DWORD dwFlags));
|
||||
DECLARE_FN(WINCRYPT32API,
|
||||
BOOL,
|
||||
CRYPT_SIGN_MESSAGE,
|
||||
(IN PCRYPT_SIGN_MESSAGE_PARA pSignPara,
|
||||
IN BOOL fDetachedSignature,
|
||||
IN DWORD cToBeSigned,
|
||||
IN const BYTE *rgpbToBeSigned[],
|
||||
IN DWORD rgcbToBeSigned[],
|
||||
OUT BYTE *pbSignedBlob,
|
||||
IN OUT DWORD *pcbSignedBlob));
|
||||
|
||||
DECLARE_FN(WINCRYPT32API,
|
||||
BOOL,
|
||||
CERT_FREE_CERTIFICATE_CONTEXT,
|
||||
(IN PCCERT_CONTEXT pCertContext));
|
||||
|
||||
DECLARE_FN(WINCRYPT32API,
|
||||
BOOL,
|
||||
CERT_GET_CERTIFICATE_CONTEXT_PROPERTY,
|
||||
(IN PCCERT_CONTEXT pCertContext,
|
||||
IN DWORD dwPropId,
|
||||
OUT void *pvData,
|
||||
IN OUT DWORD *pcbData));
|
||||
|
||||
DECLARE_FN(WINCRYPT32API,
|
||||
BOOL,
|
||||
CERT_SET_CERTIFICATE_CONTEXT_PROPERTY,
|
||||
(IN PCCERT_CONTEXT pCertContext,
|
||||
IN DWORD dwPropId,
|
||||
IN DWORD dwFlags,
|
||||
IN const void *pvData));
|
||||
|
||||
DECLARE_FN(WINCRYPT32API,
|
||||
PCCERT_CONTEXT,
|
||||
CERT_FIND_CERTIFICATE_IN_STORE,
|
||||
(IN HCERTSTORE hCertStore,
|
||||
IN DWORD dwCertEncodingType,
|
||||
IN DWORD dwFindFlags,
|
||||
IN DWORD dwFindType,
|
||||
IN const void *pvFindPara,
|
||||
IN PCCERT_CONTEXT pPrevCertContext));
|
||||
|
||||
DECLARE_FN(WINCRYPT32API,
|
||||
HCERTSTORE,
|
||||
CERT_OPEN_STORE,
|
||||
(IN LPCSTR lpszStoreProvider,
|
||||
IN DWORD dwEncodingType,
|
||||
IN HCRYPTPROV hCryptProv,
|
||||
IN DWORD dwFlags,
|
||||
IN const void *pvPara));
|
||||
|
||||
DECLARE_FN(WINCRYPT32API,
|
||||
BOOL,
|
||||
CERT_CLOSE_STORE,
|
||||
(IN HCERTSTORE hCertStore,
|
||||
DWORD dwFlags));
|
||||
|
||||
DECLARE_FN(WINCRYPT32API,
|
||||
BOOL,
|
||||
CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY,
|
||||
(IN PCCERT_CONTEXT pCert,
|
||||
IN DWORD dwFlags,
|
||||
IN void *pvReserved,
|
||||
OUT HCRYPTPROV *phCryptProv,
|
||||
OUT OPTIONAL DWORD *pdwKeySpec,
|
||||
OUT OPTIONAL BOOL *pfCallerFreeProv));
|
||||
|
||||
DECLARE_FN(WINADVAPI,
|
||||
BOOL,
|
||||
CRYPT_CREATE_HASH,
|
||||
(HCRYPTPROV hProv,
|
||||
ALG_ID Algid,
|
||||
HCRYPTKEY hKey,
|
||||
DWORD dwFlags,
|
||||
HCRYPTHASH *phHash));
|
||||
|
||||
DECLARE_FN(WINADVAPI,
|
||||
BOOL,
|
||||
CRYPT_HASH_DATA,
|
||||
(HCRYPTHASH hHash,
|
||||
CONST BYTE *pbData,
|
||||
DWORD dwDataLen,
|
||||
DWORD dwFlags));
|
||||
|
||||
DECLARE_FN (WINADVAPI,
|
||||
BOOL,
|
||||
CRYPT_DESTROY_HASH,
|
||||
(HCRYPTHASH hHash));
|
||||
|
||||
DECLARE_FN(WINADVAPI,
|
||||
BOOL,
|
||||
CRYPT_SIGN_HASH_A,
|
||||
(HCRYPTHASH hHash,
|
||||
DWORD dwKeySpec,
|
||||
LPCSTR szDescription,
|
||||
DWORD dwFlags,
|
||||
BYTE *pbSignature,
|
||||
DWORD *pdwSigLen));
|
||||
|
||||
DECLARE_FN(WINADVAPI,
|
||||
BOOL,
|
||||
CRYPT_SIGN_HASH_W,
|
||||
(HCRYPTHASH hHash,
|
||||
DWORD dwKeySpec,
|
||||
LPCWSTR szDescription,
|
||||
DWORD dwFlags,
|
||||
BYTE *pbSignature,
|
||||
DWORD *pdwSigLen));
|
||||
|
||||
#ifdef UNICODE
|
||||
#define CRYPT_SIGN_HASH_FN CRYPT_SIGN_HASH_W_FN
|
||||
#else
|
||||
#define CRYPT_SIGN_HASH_FN CRYPT_SIGN_HASH_A_FN
|
||||
#endif // !UNICODE
|
||||
|
||||
|
||||
typedef struct {
|
||||
CRYPT_SIGN_MESSAGE_FN CryptSignMessage;
|
||||
CERT_FREE_CERTIFICATE_CONTEXT_FN CertFreeCertificateContext;
|
||||
CERT_OPEN_STORE_FN CertOpenStore;
|
||||
CERT_CLOSE_STORE_FN CertCloseStore;
|
||||
GET_LAST_ERROR_FN GetLastError;
|
||||
CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY_FN CryptAcquireCertificatePrivateKey;
|
||||
CRYPT_CREATE_HASH_FN CryptCreateHash;
|
||||
CRYPT_HASH_DATA_FN CryptHashData;
|
||||
CRYPT_SIGN_HASH_FN CryptSignHash;
|
||||
CRYPT_DESTROY_HASH_FN CryptDestroyHash;
|
||||
CRYPT_RELEASE_CONTEXT_FN CryptReleaseContext;
|
||||
CERT_FIND_CERTIFICATE_IN_STORE_FN CertFindCertificateInStore;
|
||||
CERT_GET_CERTIFICATE_CONTEXT_PROPERTY_FN CertGetCertificateContextProperty;
|
||||
CERT_SET_CERTIFICATE_CONTEXT_PROPERTY_FN CertSetCertificateContextProperty;
|
||||
} capi_function_list_t;
|
||||
|
||||
|
||||
bool capi_function_list_init(library_t *lib, capi_function_list_t *fl);
|
||||
|
||||
#endif // CAPI_H_INCLUDED
|
||||
700
src/utils/conf_file_context.c
Normal file
700
src/utils/conf_file_context.c
Normal file
|
|
@ -0,0 +1,700 @@
|
|||
#include "conf_file_context.h"
|
||||
#include "gconf_file.h"
|
||||
#include "types.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define KEY_FILE_DEFAULT_LIST_SEPARATOR ' '
|
||||
|
||||
|
||||
typedef GKeyFile conf_file_context_s;
|
||||
|
||||
#define is_allowed_tcp_port(port_num) (port_num >= 0 && port_num < 65536)
|
||||
|
||||
/** Checks if character @c is allowed in location.
|
||||
* @return 1 - true, 0 - false
|
||||
*/
|
||||
static int
|
||||
allowed_location_char(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return 1; /* true */
|
||||
|
||||
unsigned char ch = (unsigned char) (c | 0x20); /* lower */
|
||||
if (ch >= 'a' && ch <= 'z') return 1; /* true */
|
||||
|
||||
switch (c) {
|
||||
/* pct-encoding */
|
||||
case '%':
|
||||
|
||||
/* unreserved */
|
||||
case '-': case '.': case '_': case '~':
|
||||
|
||||
/* sub-delims */
|
||||
case '!': case '$': case '&': case '\'':
|
||||
case '(': case ')': case '*': case '+':
|
||||
case ',': case ';': case '=':
|
||||
|
||||
/* gen-delims (allowed) */
|
||||
case '/': case ':': case '@':
|
||||
|
||||
return 1; /* true */
|
||||
|
||||
/* gen-delims (not allowed) */
|
||||
case '?': case '#': case '[': case ']':
|
||||
default:
|
||||
|
||||
return 0; /* false */
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if character @c is allowed in uri.
|
||||
* @return 1 - true, 0 - false
|
||||
*/
|
||||
static int
|
||||
allowed_uri_char(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return 1; /* true */
|
||||
|
||||
unsigned char ch = (unsigned char) (c | 0x20); /* lower */
|
||||
if (ch >= 'a' && ch <= 'z') return 1; /* true */
|
||||
|
||||
switch (c) {
|
||||
/* pct-encoding */
|
||||
case '%':
|
||||
|
||||
/* unreserved */
|
||||
case '-': case '.': case '_': case '~':
|
||||
|
||||
/* sub-delims */
|
||||
case '!': case '$': case '&': case '\'':
|
||||
case '(': case ')': case '*': case '+':
|
||||
case ',': case ';': case '=':
|
||||
|
||||
/* gen-delims */
|
||||
case '/': case ':': case '@':
|
||||
case '?': case '#': case '[': case ']':
|
||||
|
||||
return 1; /* true */
|
||||
|
||||
default:
|
||||
|
||||
return 0; /* false */
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if character @c is allowed in host name.
|
||||
* @return 1 - true, 0 - false
|
||||
*/
|
||||
static int
|
||||
allowed_hostname_char(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return 1; /* true */
|
||||
|
||||
unsigned char ch = (unsigned char) (c | 0x20); /* lower */
|
||||
if (ch >= 'a' && ch <= 'z') return 1; /* true */
|
||||
|
||||
switch (c) {
|
||||
/* pct-encoding */
|
||||
case '%':
|
||||
|
||||
/* unreserved */
|
||||
case '-': case '.': case '_': case '~':
|
||||
|
||||
/* sub-delims */
|
||||
case '!': case '$': case '&': case '\'':
|
||||
case '(': case ')': case '*': case '+':
|
||||
case ',': case ';': case '=':
|
||||
|
||||
/* gen-delims (allowed) */
|
||||
case ':': case '[': case ']':
|
||||
|
||||
return 1; /* true */
|
||||
|
||||
/* gen-delims (not allowed) */
|
||||
case '/': case '@': case '?': case '#':
|
||||
default:
|
||||
|
||||
return 0; /* false */
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks the syntax of str_t @location: it is allowed to consist of only
|
||||
* rfc-3986 defined characters
|
||||
* @return 1 - true, 0 - false
|
||||
*/
|
||||
static int
|
||||
allowed_location_strt(const str_t* location)
|
||||
{
|
||||
assert(location != NULL && !str_t_is_null(*location));
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < location->len; i++) {
|
||||
if (!allowed_location_char(location->data[i])) {
|
||||
LOG_ERROR("location '%.*s' is not valid URI-path: '%c' character is not allowed",
|
||||
(int)location->len, location->data, location->data[i]);
|
||||
return 0; /* false */
|
||||
}
|
||||
}
|
||||
|
||||
return 1; /* true */
|
||||
}
|
||||
|
||||
/** Checks the syntax of NULL-terminated string @location: it is allowed
|
||||
* to consist of only rfc-3986 defined characters.
|
||||
* @return 1 - true, 0 - false
|
||||
*/
|
||||
static int
|
||||
allowed_location_string(const char* location)
|
||||
{
|
||||
assert(location != NULL);
|
||||
|
||||
const char* loc;
|
||||
for (loc = location; *loc != '\0'; loc++) {
|
||||
if (!allowed_location_char(*loc)) {
|
||||
LOG_ERROR("location '%s' is not valid URI-path: '%c' character is not allowed",
|
||||
location, *loc);
|
||||
return 0; /* false */
|
||||
}
|
||||
}
|
||||
|
||||
return 1; /* true */
|
||||
}
|
||||
|
||||
/** Checks the syntax of str_t @uri: it is allowed to consist of only rfc-3986
|
||||
* defined characters
|
||||
* @return 1 - true, 0 - false
|
||||
*/
|
||||
static int
|
||||
allowed_uri_strt(const str_t* uri)
|
||||
{
|
||||
assert(uri != NULL && !str_t_is_null(*uri));
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < uri->len; i++) {
|
||||
if (!allowed_uri_char(uri->data[i])) {
|
||||
LOG_ERROR("uri '%.*s' is not valid URI: '%c' character is not allowed",
|
||||
(int)uri->len, uri->data, uri->data[i]);
|
||||
return 0; /* false */
|
||||
}
|
||||
}
|
||||
|
||||
return 1; /* true */
|
||||
}
|
||||
|
||||
/** Checks the syntax of NULL-terminated string @uri: it is allowed
|
||||
* to consist of only rfc-3986 defined characters.
|
||||
* @return 1 - true, 0 - false
|
||||
*/
|
||||
static int
|
||||
allowed_uri_string(const char* uri)
|
||||
{
|
||||
assert(uri != NULL);
|
||||
|
||||
const char* u;
|
||||
for (u = uri; *u != '\0'; u++) {
|
||||
if (!allowed_uri_char(*u)) {
|
||||
LOG_ERROR("uri '%s' is not valid URI-path: '%c' character is not allowed",
|
||||
uri, *u);
|
||||
return 0; /* false */
|
||||
}
|
||||
}
|
||||
|
||||
return 1; /* true */
|
||||
}
|
||||
|
||||
/** Checks the syntax of NULL-terminated string @host: it is allowed
|
||||
* to consist of only rfc-3986 defined characters. And limit hostname length is
|
||||
* HOST_NAME_MAX
|
||||
* @return 1 - true, 0 - false
|
||||
*/
|
||||
static int
|
||||
allowed_hostname(const char* host)
|
||||
{
|
||||
assert(host != NULL);
|
||||
|
||||
size_t len = 0;
|
||||
const char* h = host;
|
||||
|
||||
for (; *h != '\0' && len <= HOST_NAME_MAX ; h++, len++) {
|
||||
if (!allowed_hostname_char(*h)) {
|
||||
LOG_ERROR("Hostname '%s' is not valid: '%c' character is not allowed",
|
||||
host, *h);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 0 || len > HOST_NAME_MAX) {
|
||||
LOG_ERROR("Hostname length (%zu) is not allowed: == 0 or > HOST_NAME_MAX(%d)",
|
||||
len, HOST_NAME_MAX);
|
||||
return 0; /*false*/
|
||||
}
|
||||
|
||||
return 1; /*true*/
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
conf_file_set_boolean_value(const conf_file_field_t *field, int value)
|
||||
{
|
||||
*((int*)field->value) = value;
|
||||
LOG_DEBUG("%s::%s = %s", field->section, field->key, value == TRUE ? "true" : "false");
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
conf_file_set_integer_value(const conf_file_field_t *field, int value)
|
||||
{
|
||||
*((int*)field->value) = value;
|
||||
LOG_DEBUG("%s::%s = %d", field->section, field->key, value);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
conf_file_set_integer_list_value(const conf_file_field_t *field, const int_list_t *value)
|
||||
{
|
||||
*((int_list_t*)field->value) = *value;
|
||||
|
||||
LOG_DEBUG("%s::%s = {", field->section, field->key);;
|
||||
int_list_print(value);
|
||||
LOG_DEBUG("}");
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
conf_file_set_string_value(const conf_file_field_t *field, char *value)
|
||||
{
|
||||
*(char**)field->value = value;
|
||||
|
||||
if (field->check != CONF_FILE_VALUE_PSWD) {
|
||||
if (value != NULL) {
|
||||
LOG_DEBUG("%s::%s = %s", field->section, field->key, value);
|
||||
} else {
|
||||
LOG_DEBUG("%s::%s = NULL", field->section, field->key);
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG("%s::%s = ******", field->section, field->key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
conf_file_set_string_list_value(const conf_file_field_t *field, const string_list_t *value)
|
||||
{
|
||||
*((string_list_t*)field->value) = *value;
|
||||
|
||||
LOG_DEBUG("%s::%s = {", field->section, field->key);
|
||||
string_list_print(value);
|
||||
LOG_DEBUG("}");
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
conf_file_set_strt_value(const conf_file_field_t *field, const str_t *value)
|
||||
{
|
||||
*(str_t*)field->value = *value;
|
||||
|
||||
if (field->check != CONF_FILE_VALUE_PSWD) {
|
||||
LOG_DEBUG("%s::%s = %.*s", field->section, field->key, (int)value->len, value->data);
|
||||
} else {
|
||||
LOG_DEBUG("%s::%s = ******", field->section, field->key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
conf_file_set_default_value(const conf_file_field_t *field)
|
||||
{
|
||||
assert(field != NULL);
|
||||
|
||||
switch (field->type) {
|
||||
case CONF_FILE_VALUE_BOOLEAN:
|
||||
|
||||
conf_file_set_boolean_value(field, *((int*)field->default_value));
|
||||
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_INTEGER:
|
||||
|
||||
conf_file_set_integer_value(field, *((int*)field->default_value));
|
||||
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_INTEGER_LIST:
|
||||
{
|
||||
int_list_t value = {0};
|
||||
if (int_list_copy(&value, field->default_value)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
conf_file_set_integer_list_value(field, &value);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_STRING:
|
||||
{
|
||||
char *value = NULL;
|
||||
char *default_value = *((char**)field->default_value);
|
||||
if (default_value != NULL) {
|
||||
value = strdup(default_value);
|
||||
if (value == NULL) {
|
||||
LOG_ERROR("Could not copy default value for '%s::%s'",
|
||||
field->section, field->key);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
conf_file_set_string_value(field, value);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_STRING_LIST:
|
||||
{
|
||||
string_list_t value = {0};
|
||||
if (string_list_copy(&value, field->default_value)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
conf_file_set_string_list_value(field, &value);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_STRT:
|
||||
{
|
||||
str_t value = {0};
|
||||
if (str_t_copy(&value, field->default_value)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
conf_file_set_strt_value(field, &value);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("conf_file_set_default_value exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
conf_file_load_value(GKeyFile* ini_file, const conf_file_field_t *field, GError** gerror)
|
||||
{
|
||||
assert(ini_file != NULL);
|
||||
assert(field != NULL && field->value != NULL);
|
||||
assert(field->key != NULL && field->section != NULL);
|
||||
assert(gerror != NULL && *gerror == NULL);
|
||||
|
||||
switch (field->type) {
|
||||
case CONF_FILE_VALUE_BOOLEAN:
|
||||
{
|
||||
gboolean value = g_key_file_get_boolean(ini_file, field->section, field->key, gerror);
|
||||
conf_file_set_boolean_value(field, (int)value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_INTEGER:
|
||||
{
|
||||
gint value = g_key_file_get_integer(ini_file, field->section, field->key, gerror);
|
||||
conf_file_set_integer_value(field, (int)value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_INTEGER_LIST:
|
||||
{
|
||||
int_list_t value = {0};
|
||||
value.list = g_key_file_get_integer_list(ini_file, field->section, field->key, &(value.size), gerror);
|
||||
if (value.list == NULL) break;
|
||||
|
||||
conf_file_set_integer_list_value(field, &value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_STRING:
|
||||
{
|
||||
char *value = gconf_file_get_string(ini_file, field->section, field->key, gerror);
|
||||
if (value == NULL) break;
|
||||
|
||||
conf_file_set_string_value(field, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_STRING_LIST:
|
||||
{
|
||||
string_list_t value = {0};
|
||||
value.list = gconf_file_get_string_list(ini_file, field->section, field->key, &(value.size), gerror);
|
||||
if (value.list == NULL) break;
|
||||
|
||||
conf_file_set_string_list_value(field, &value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_STRT:
|
||||
{
|
||||
str_t value = {0};
|
||||
if (gconf_file_get_strt(ini_file, field->section, field->key, gerror, &value)) {
|
||||
break;
|
||||
}
|
||||
if (str_t_is_null(value)) break;
|
||||
|
||||
conf_file_set_strt_value(field, &value);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (*gerror != NULL) {
|
||||
if (field->default_value != NULL && (*gerror)->code != G_KEY_FILE_ERROR_INVALID_VALUE) {
|
||||
LOG_DEBUG("Conf field '%s::%s' is optional. Skip error (%s)",
|
||||
field->section, field->key, (*gerror)->message);
|
||||
g_error_free(*gerror);
|
||||
*gerror = NULL;
|
||||
|
||||
LOG_DEBUG("Set default value for field '%s::%s'", field->section, field->key);
|
||||
if (conf_file_set_default_value(field)) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("conf_file_load_value '%s':'%s' exit with error", field->section, field->key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
conf_file_check_value(const conf_file_field_t *field)
|
||||
{
|
||||
assert(field != NULL);
|
||||
|
||||
switch (field->check) {
|
||||
case CONF_FILE_VALUE_NONE:
|
||||
case CONF_FILE_VALUE_PSWD:
|
||||
/* do nothing */
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_HOSTNAME:
|
||||
if (field->type == CONF_FILE_VALUE_STRING) {
|
||||
char* host = *(char**)field->value;
|
||||
if (!allowed_hostname(host)) {
|
||||
LOG_ERROR("%s:%s '%s' is not allowed hostname",
|
||||
field->section, field->key, host);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_LOCATION:
|
||||
if (field->type == CONF_FILE_VALUE_STRING) {
|
||||
char *location = *(char**)field->value;
|
||||
if (!allowed_location_string(location)) {
|
||||
LOG_ERROR("%s:%s '%s' is not allowed location",
|
||||
field->section, field->key, location);
|
||||
goto error;
|
||||
}
|
||||
|
||||
} else if (field->type == CONF_FILE_VALUE_STRT) {
|
||||
str_t *location = (str_t*)field->value;
|
||||
if (!allowed_location_strt(location)) {
|
||||
LOG_ERROR("%s:%s '%.*s' is not allowed location", field->section,
|
||||
field->key, (int)location->len, location->data);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_TCP_PORT:
|
||||
if (field->type == CONF_FILE_VALUE_INTEGER) {
|
||||
int port = *(int*)field->value;
|
||||
if (!is_allowed_tcp_port(port)) {
|
||||
LOG_ERROR("%s:%s '%d' not allowed tcp port",
|
||||
field->section, field->key, port);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_POSITIVE_INT:
|
||||
if (field->type == CONF_FILE_VALUE_INTEGER) {
|
||||
int value = *(int*)field->value;
|
||||
if (value < 0) {
|
||||
LOG_ERROR("%s:%s is not valid: %d (expected value >= 0)",
|
||||
field->section, field->key, value);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CONF_FILE_VALUE_URI:
|
||||
if (field->type == CONF_FILE_VALUE_STRING) {
|
||||
char *uri = *(char**)field->value;
|
||||
if (uri != NULL) {
|
||||
if (!allowed_uri_string(uri)) {
|
||||
LOG_ERROR("%s:%s '%s' is not allowed uri",
|
||||
field->section, field->key, uri);
|
||||
goto error;
|
||||
}
|
||||
} else if (field->default_value == NULL) {
|
||||
LOG_ERROR("%s:%s 'NULL' is not allowed uri",
|
||||
field->section, field->key);
|
||||
goto error;
|
||||
}
|
||||
} else if (field->type == CONF_FILE_VALUE_STRT) {
|
||||
str_t *uri = (str_t*)field->value;
|
||||
if (!str_t_is_null(*uri)) {
|
||||
if (!allowed_uri_strt(uri)) {
|
||||
LOG_ERROR("%s:%s '%.*s' is not allowed uri", field->section,
|
||||
field->key, (int)uri->len, uri->data);
|
||||
goto error;
|
||||
}
|
||||
} else if (field->default_value == NULL) {
|
||||
LOG_ERROR("%s:%s 'NULL' is not allowed uri",
|
||||
field->section, field->key);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
conf_file_load_from_file(const char *filename, conf_file_field_t* fields, int fields_count)
|
||||
{
|
||||
LOG_TRACE("conf_file_load_from_file enter. Config filename: %s", filename);
|
||||
|
||||
conf_file_context_t ctx = conf_file_open_file(filename);
|
||||
if (ctx == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (conf_file_load_values(ctx, fields, fields_count)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
conf_file_close_file(ctx);
|
||||
|
||||
LOG_TRACE("conf_file_load_from_file exit");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("Load configuration from file '%s' FAILED", filename);
|
||||
|
||||
conf_file_close_file(ctx);
|
||||
|
||||
LOG_ERROR("conf_file_load_from_file exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
conf_file_context_t
|
||||
conf_file_open_file(const char *filename)
|
||||
{
|
||||
GKeyFile* ini_file = NULL;
|
||||
GError* gerror = NULL;
|
||||
|
||||
LOG_TRACE("conf_file_open_file enter. Config filename: %s", filename);
|
||||
|
||||
assert(filename != NULL);
|
||||
|
||||
ini_file = g_key_file_new();
|
||||
if (ini_file == NULL) {
|
||||
LOG_ERROR("conf_file_open_file failed. Desc: could not create key file instance");
|
||||
goto error;
|
||||
}
|
||||
|
||||
g_key_file_set_list_separator(ini_file, KEY_FILE_DEFAULT_LIST_SEPARATOR);
|
||||
|
||||
if (!g_key_file_load_from_file(ini_file, filename, G_KEY_FILE_KEEP_COMMENTS,
|
||||
&gerror)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return (conf_file_context_t)ini_file;
|
||||
|
||||
error:
|
||||
LOG_ERROR("Open configuration file '%s' FAILED", filename);
|
||||
|
||||
if (gerror != NULL) {
|
||||
LOG_ERROR("Desc: %s", gerror->message);
|
||||
g_error_free(gerror);
|
||||
}
|
||||
|
||||
if (ini_file != NULL) {
|
||||
g_key_file_free(ini_file);
|
||||
}
|
||||
|
||||
LOG_ERROR("conf_file_open_file exit with error");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
conf_file_close_file(conf_file_context_t ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
GKeyFile* ini_file = (GKeyFile*) ctx;
|
||||
g_key_file_free(ini_file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
conf_file_load_values(const conf_file_context_t ctx, const conf_file_field_t* fields, int fields_count)
|
||||
{
|
||||
GKeyFile* ini_file = (GKeyFile*) ctx;
|
||||
GError* gerror = NULL;
|
||||
int i = 0;
|
||||
|
||||
for (; i < fields_count; i++) {
|
||||
if (conf_file_load_value(ini_file, &fields[i], &gerror)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (conf_file_check_value(&fields[i])) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("Load configuration FAILED");
|
||||
if (gerror != NULL) {
|
||||
LOG_ERROR("Desc: %s", gerror->message);
|
||||
g_error_free(gerror);
|
||||
}
|
||||
LOG_ERROR("conf_file_load_values exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
conf_file_has_section(const conf_file_context_t ctx, const char *section)
|
||||
{
|
||||
GKeyFile* ini_file = (GKeyFile*) ctx;
|
||||
|
||||
return g_key_file_has_group(ini_file, section) ? 0 : 1;
|
||||
}
|
||||
72
src/utils/conf_file_context.h
Normal file
72
src/utils/conf_file_context.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#ifndef CONF_FILE_CONTEXT_H_INCLUDED
|
||||
#define CONF_FILE_CONTEXT_H_INCLUDED
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
typedef struct conf_file_context_s* conf_file_context_t;
|
||||
|
||||
|
||||
typedef enum conf_file_value_type_e {
|
||||
CONF_FILE_VALUE_BOOLEAN,
|
||||
CONF_FILE_VALUE_INTEGER,
|
||||
CONF_FILE_VALUE_INTEGER_LIST,
|
||||
CONF_FILE_VALUE_STRING,
|
||||
CONF_FILE_VALUE_STRING_LIST,
|
||||
CONF_FILE_VALUE_STRT,
|
||||
|
||||
} conf_file_value_type_t;
|
||||
|
||||
|
||||
typedef enum conf_file_value_check_e {
|
||||
CONF_FILE_VALUE_NONE,
|
||||
CONF_FILE_VALUE_HOSTNAME,
|
||||
CONF_FILE_VALUE_LOCATION,
|
||||
CONF_FILE_VALUE_POSITIVE_INT,
|
||||
CONF_FILE_VALUE_TCP_PORT,
|
||||
CONF_FILE_VALUE_URI,
|
||||
CONF_FILE_VALUE_PSWD
|
||||
|
||||
} conf_file_value_check_t;
|
||||
|
||||
|
||||
typedef struct conf_field_s {
|
||||
const char *section;
|
||||
const char *key;
|
||||
void *value;
|
||||
conf_file_value_type_t type;
|
||||
conf_file_value_check_t check;
|
||||
const void *default_value;
|
||||
|
||||
} conf_file_field_t;
|
||||
|
||||
|
||||
int conf_file_load_from_file(const char *filename, conf_file_field_t* fields, int fields_count);
|
||||
|
||||
|
||||
conf_file_context_t conf_file_open_file(const char *filename);
|
||||
void conf_file_close_file(conf_file_context_t ctx);
|
||||
|
||||
int conf_file_load_values(const conf_file_context_t ctx, const conf_file_field_t* fields, int fields_count);
|
||||
|
||||
|
||||
/* Return value:
|
||||
* 0 - on success if conf file has the section
|
||||
* 1 - if conf file does not have the section
|
||||
*/
|
||||
int conf_file_has_section(const conf_file_context_t ctx, const char *section);
|
||||
|
||||
|
||||
static inline const char*
|
||||
conf_file_select_existing_section(const conf_file_context_t ctx, const char *section, const char *default_section)
|
||||
{
|
||||
switch (conf_file_has_section(ctx, section)) {
|
||||
case 0:
|
||||
return section;
|
||||
case 1:
|
||||
LOG_DEBUG("section '%s' does not exist. Use default section: '%s'", section, default_section);
|
||||
return default_section;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // CONF_FILE_CONTEXT_H_INCLUDED
|
||||
295
src/utils/cryptopro.c
Normal file
295
src/utils/cryptopro.c
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
#include "cryptopro.h"
|
||||
|
||||
#include "base64.h"
|
||||
#include "capi.h"
|
||||
#include "library.h"
|
||||
#include "logger.h"
|
||||
|
||||
static capi_function_list_t cp_function_list;
|
||||
static library_t libcapi;
|
||||
|
||||
static int sign_hash_data(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t *sign);
|
||||
|
||||
static int reverse_sign(const str_t *sign, /*out*/ str_t *sign_reversed);
|
||||
|
||||
static HCERTSTORE cert_open_store();
|
||||
|
||||
static PCCERT_CONTEXT get_signer_cert(const cryptopro_context_t *ctx, HCERTSTORE hStoreHandle);
|
||||
|
||||
static int set_pin(PCCERT_CONTEXT pSignerCert, const str_t *pin);
|
||||
|
||||
bool
|
||||
cryptopro_init(const char* cp_file)
|
||||
{
|
||||
static bool is_initialized = false;
|
||||
|
||||
if (!is_initialized) {
|
||||
library_init(&libcapi, cp_file);
|
||||
if (!capi_function_list_init(&libcapi, &cp_function_list)) {
|
||||
LOG_ERROR("capi_function_list_init failed");
|
||||
return false;
|
||||
}
|
||||
is_initialized = true;
|
||||
LOG_INFO("Cryptographic Provider initialized");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
cryptopro_sign(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t *sign)
|
||||
{
|
||||
str_t signed_data = str_t_null;
|
||||
str_t sign_reversed = str_t_null;
|
||||
|
||||
LOG_TRACE("cryptopro_sign enter");
|
||||
|
||||
assert(ctx != NULL);
|
||||
assert(ctx->signer != NULL);
|
||||
assert(ctx->pin != NULL);
|
||||
assert(data != NULL && !str_t_is_null(*data));
|
||||
assert(sign != NULL);
|
||||
|
||||
if (sign_hash_data(ctx, data, &signed_data)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (reverse_sign(&signed_data, &sign_reversed)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
sign->len = base64_encoded_length(sign_reversed.len);
|
||||
sign->data = calloc(1, sign->len + 1);
|
||||
if (sign->data == NULL) {
|
||||
LOG_ERROR("Could not allocate memory for sign (%zu bytes)", sign->len + 1);
|
||||
goto error;
|
||||
}
|
||||
|
||||
encode_base64_url(sign, &sign_reversed);
|
||||
|
||||
str_t_clear(&signed_data);
|
||||
str_t_clear(&sign_reversed);
|
||||
|
||||
LOG_TRACE("cryptopro_sign exit");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
str_t_clear(&signed_data);
|
||||
str_t_clear(&sign_reversed);
|
||||
LOG_ERROR("cryptopro_sign exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
sign_hash_data(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t *sign)
|
||||
{
|
||||
int rc = -1;
|
||||
HCERTSTORE hStoreHandle;
|
||||
PCCERT_CONTEXT pSignerCert = NULL;
|
||||
BOOL bReleaseContext = FALSE;
|
||||
HCRYPTPROV hCryptProv;
|
||||
HCRYPTHASH hash = 0;
|
||||
BYTE *pbSignedMessageBlob;
|
||||
DWORD cbSignedMessageBlob;
|
||||
|
||||
hStoreHandle = cert_open_store();
|
||||
if (hStoreHandle == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pSignerCert = get_signer_cert(ctx, hStoreHandle);
|
||||
if (pSignerCert == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!cp_function_list.CryptAcquireCertificatePrivateKey(
|
||||
pSignerCert,
|
||||
CRYPT_ACQUIRE_SILENT_FLAG,
|
||||
NULL,
|
||||
&hCryptProv,
|
||||
NULL,
|
||||
&bReleaseContext)) {
|
||||
LOG_ERROR("Cannot acquire the certificate private key");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!cp_function_list.CryptCreateHash(hCryptProv, CALG_GR3411_2012_256, 0, 0, &hash)) {
|
||||
LOG_ERROR("CryptCreateHash() failed");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!cp_function_list.CryptHashData(hash, (BYTE *)data->data, data->len, 0)) {
|
||||
LOG_ERROR("CryptHashData() failed");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!cp_function_list.CryptSignHash(hash, AT_KEYEXCHANGE, NULL, 0, NULL, &cbSignedMessageBlob)) {
|
||||
LOG_ERROR("CryptSignHash() failed");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pbSignedMessageBlob = malloc(cbSignedMessageBlob * sizeof(BYTE));
|
||||
|
||||
if (!cp_function_list.CryptSignHash(hash, AT_KEYEXCHANGE, NULL, 0, pbSignedMessageBlob, &cbSignedMessageBlob)) {
|
||||
LOG_ERROR("CryptSignHash() failed");
|
||||
free(pbSignedMessageBlob);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
sign->data = (char*)pbSignedMessageBlob;
|
||||
sign->len = (size_t)cbSignedMessageBlob;
|
||||
|
||||
rc = 0;
|
||||
|
||||
exit:
|
||||
if (hash) {
|
||||
cp_function_list.CryptDestroyHash(hash);
|
||||
}
|
||||
|
||||
if (bReleaseContext) {
|
||||
cp_function_list.CryptReleaseContext(hCryptProv, 0);
|
||||
}
|
||||
|
||||
if (pSignerCert) {
|
||||
cp_function_list.CertFreeCertificateContext(pSignerCert);
|
||||
}
|
||||
|
||||
if (hStoreHandle) {
|
||||
if (!cp_function_list.CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG)) {
|
||||
LOG_ERROR("CertCloseStore() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
LOG_ERROR("sign_hash_data exit with error. Last error code: 0x%08x", cp_function_list.GetLastError());
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
reverse_sign(const str_t *sign, /*out*/ str_t *sign_reversed)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
sign_reversed->data = malloc(sign->len);
|
||||
if (sign_reversed->data == NULL) {
|
||||
LOG_ERROR("Could not allocate memory for reversed sign (%zd bytes)", sign->len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < sign->len; i++) {
|
||||
sign_reversed->data[i] = sign->data[sign->len - i - 1];
|
||||
}
|
||||
|
||||
sign_reversed->len = sign->len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static HCERTSTORE
|
||||
cert_open_store()
|
||||
{
|
||||
HCERTSTORE hStoreHandle;
|
||||
|
||||
LOG_TRACE("cert_open_store enter");
|
||||
|
||||
hStoreHandle = cp_function_list.CertOpenStore(CERT_STORE_PROV_SYSTEM,
|
||||
0,
|
||||
0,
|
||||
CERT_SYSTEM_STORE_CURRENT_USER,
|
||||
L"MY");
|
||||
|
||||
if (hStoreHandle == NULL) {
|
||||
LOG_ERROR("The store could not be opened");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOG_DEBUG("The MY store is open");
|
||||
|
||||
return hStoreHandle;
|
||||
}
|
||||
|
||||
static PCCERT_CONTEXT
|
||||
get_signer_cert(const cryptopro_context_t *ctx, HCERTSTORE hStoreHandle)
|
||||
{
|
||||
PCCERT_CONTEXT pSignerCert;
|
||||
|
||||
pSignerCert = cp_function_list.CertFindCertificateInStore(hStoreHandle,
|
||||
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
||||
0,
|
||||
CERT_FIND_SUBJECT_STR,
|
||||
ctx->signer,
|
||||
NULL);
|
||||
if (pSignerCert == NULL) {
|
||||
LOG_ERROR("Could not find certificate in store");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (set_pin(pSignerCert, ctx->pin)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_DEBUG("The signer's certificate was found");
|
||||
return pSignerCert;
|
||||
|
||||
error:
|
||||
if (pSignerCert) {
|
||||
cp_function_list.CertFreeCertificateContext(pSignerCert);
|
||||
}
|
||||
LOG_ERROR("get_signer_cert exit with error");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
set_pin(PCCERT_CONTEXT pSignerCert, const str_t *pin)
|
||||
{
|
||||
DWORD dwSize;
|
||||
if (!cp_function_list.CertGetCertificateContextProperty(
|
||||
pSignerCert,
|
||||
CERT_KEY_PROV_INFO_PROP_ID,
|
||||
NULL,
|
||||
&dwSize)) {
|
||||
LOG_ERROR("Error getting key property");
|
||||
LOG_ERROR("set_pin exit with error. Last error code: 0x%08x", cp_function_list.GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char pKeyInfoBuffer[dwSize];
|
||||
CRYPT_KEY_PROV_INFO* pKeyInfo = (CRYPT_KEY_PROV_INFO*) pKeyInfoBuffer;
|
||||
|
||||
if (!cp_function_list.CertGetCertificateContextProperty(
|
||||
pSignerCert,
|
||||
CERT_KEY_PROV_INFO_PROP_ID,
|
||||
pKeyInfo,
|
||||
&dwSize)){
|
||||
LOG_ERROR("The second call to the function failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
DWORD keyType = (pKeyInfo->dwKeySpec == AT_SIGNATURE) ? PP_SIGNATURE_PIN
|
||||
: PP_KEYEXCHANGE_PIN;
|
||||
|
||||
CRYPT_KEY_PROV_PARAM keyProvParam;
|
||||
memset(&keyProvParam, 0, sizeof(CRYPT_KEY_PROV_PARAM));
|
||||
keyProvParam.dwFlags = 0;
|
||||
keyProvParam.dwParam = keyType;
|
||||
keyProvParam.cbData = (DWORD)(pin->len + 1);
|
||||
keyProvParam.pbData = (BYTE*) pin->data;
|
||||
|
||||
pKeyInfo->cProvParam = 1;
|
||||
pKeyInfo->rgProvParam = &keyProvParam;
|
||||
|
||||
if (!cp_function_list.CertSetCertificateContextProperty(
|
||||
pSignerCert,
|
||||
CERT_KEY_PROV_INFO_PROP_ID,
|
||||
0,
|
||||
pKeyInfo)) {
|
||||
LOG_ERROR("Error setting key property");
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("set_pin exit with error. Last error code: 0x%08x", cp_function_list.GetLastError());
|
||||
return -1;
|
||||
}
|
||||
29
src/utils/cryptopro.h
Normal file
29
src/utils/cryptopro.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef CRYPTOPRO_H_INCLUDED
|
||||
#define CRYPTOPRO_H_INCLUDED
|
||||
|
||||
#include "str_t.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
typedef struct cryptopro_context_s {
|
||||
const char *signer;
|
||||
const str_t *pin;
|
||||
} cryptopro_context_t;
|
||||
|
||||
|
||||
static inline void
|
||||
cryptopro_context_set(cryptopro_context_t *ctx, const char *signer, const str_t *pin)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
|
||||
ctx->signer = signer;
|
||||
ctx->pin = pin;
|
||||
}
|
||||
|
||||
bool cryptopro_init(const char* cp_file);
|
||||
|
||||
int cryptopro_sign(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t *sign);
|
||||
|
||||
#endif // CRYPTOPRO_H_INCLUDED
|
||||
244
src/utils/gconf_file.c
Normal file
244
src/utils/gconf_file.c
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
#include "gconf_file.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/** Returns the length of the gchar string @str
|
||||
*/
|
||||
static inline size_t gstr_byte_len(const gchar* str)
|
||||
{
|
||||
return strlen((char*) str);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies the gchar string pointed by src into the content pointed by dst, including
|
||||
* the terminating null character (and stopping at that point).
|
||||
* Uses to avoid dependence from glib.
|
||||
* @src: gchar string to be copied
|
||||
* @dst: pointer to destination of type str_t where content is to be copied
|
||||
* Return value: 0 - success, -1 - error
|
||||
*/
|
||||
static int
|
||||
copy_gchar2strt(const gchar* src, str_t* dst)
|
||||
{
|
||||
assert(src != NULL);
|
||||
assert(dst != NULL);
|
||||
|
||||
size_t len = gstr_byte_len(src);
|
||||
char* data = (char*) malloc(len + 1);
|
||||
if (data == NULL) {
|
||||
LOG_ERROR("gchar2strt exit with error: could not allocate memory for dst");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dst->data = data;
|
||||
dst->len = len;
|
||||
memcpy(dst->data, src, dst->len + 1);
|
||||
dst->data[dst->len] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Copies the gchar string pointed by src into the content pointed by return value,
|
||||
* including the terminating null character (and stopping at that point).
|
||||
* Uses to avoid dependence from glib.
|
||||
* @src: gchar string to be copied
|
||||
* Return value: pointer to destination where content is to be copied
|
||||
*/
|
||||
static char*
|
||||
copy_gchar2char(const gchar* src)
|
||||
{
|
||||
assert(src != NULL);
|
||||
|
||||
// char* tmp_end = strchr(src, '\0');
|
||||
char* dst = NULL;
|
||||
size_t len = gstr_byte_len(src);
|
||||
dst = (char*) malloc(len + 1);
|
||||
if (dst == NULL) {
|
||||
LOG_ERROR("gchar2char exit with error: could not allocate memory for dst");
|
||||
return NULL;
|
||||
}
|
||||
memcpy(dst, src, len + 1);
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
/* Returns the string value from GKeyFile associated with key under section and converted to str_t
|
||||
* @ini_file: GKeyFile
|
||||
* @section: section name in config file
|
||||
* @key: key name
|
||||
* @gerror: return location for a GError, or NULL
|
||||
* @conf_field: str_t which will contain string
|
||||
* Return value: 0 - success, -1 - error.
|
||||
*/
|
||||
int
|
||||
gconf_file_get_strt(GKeyFile* ini_file, const char* section, const char* key,
|
||||
GError** gerror, str_t* conf_field)
|
||||
{
|
||||
assert(ini_file != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
assert(gerror != NULL);
|
||||
assert(conf_field != NULL);
|
||||
|
||||
gchar* g_tmp = g_key_file_get_string(ini_file, section, key, gerror);
|
||||
if (g_tmp == NULL) {
|
||||
goto error;
|
||||
}
|
||||
if (copy_gchar2strt(g_tmp, conf_field) < 0) {
|
||||
g_free(g_tmp);
|
||||
LOG_ERROR("Could not copy %s:%s", section, key);
|
||||
goto error;
|
||||
}
|
||||
g_free(g_tmp);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_TRACE("gconf_file_get_strt for key '%s::%s' exit with error", section, key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
gconf_file_get_locale_strt(GKeyFile* ini_file, const char* section, const char* key,
|
||||
const char* locale, GError** gerror, str_t* conf_field)
|
||||
{
|
||||
assert(ini_file != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
assert(gerror != NULL);
|
||||
assert(conf_field != NULL);
|
||||
/* locale may be NULL */
|
||||
|
||||
gchar* g_tmp = g_key_file_get_locale_string(ini_file, section, key, locale, gerror);
|
||||
if (g_tmp == NULL) {
|
||||
goto error;
|
||||
}
|
||||
if (copy_gchar2strt(g_tmp, conf_field) < 0) {
|
||||
g_free(g_tmp);
|
||||
LOG_ERROR("Could not copy %s:%s", section, key);
|
||||
goto error;
|
||||
}
|
||||
g_free(g_tmp);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_TRACE("gconf_file_get_locale_strt for key '%s::%s' exit with error", section, key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* @ini_file: GKeyFile
|
||||
* @section: section name in config file
|
||||
* @key: key name
|
||||
* @gerror: return location for a GError, or NULL
|
||||
* Return value: a newly allocated null-terminated string or NULL
|
||||
* if the specified key cannot be found.
|
||||
*/
|
||||
char*
|
||||
gconf_file_get_string(GKeyFile* ini_file, const char* section, const char* key,
|
||||
GError** gerror)
|
||||
{
|
||||
assert(ini_file != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
assert(gerror != NULL);
|
||||
|
||||
char* conf_field = NULL;
|
||||
gchar* g_tmp = g_key_file_get_string(ini_file, section, key, gerror);
|
||||
if (g_tmp == NULL) {
|
||||
goto error;
|
||||
}
|
||||
conf_field = copy_gchar2char(g_tmp);
|
||||
g_free(g_tmp);
|
||||
|
||||
return conf_field;
|
||||
|
||||
error:
|
||||
LOG_TRACE("gconf_file_get_string for key '%s::%s' exit with error", section, key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static inline char**
|
||||
char_p_array_calloc(size_t array_length)
|
||||
{
|
||||
char** char_p_array = (char**)calloc(array_length + 1, sizeof(char*)); // +1 for null-terminated array
|
||||
|
||||
return char_p_array;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
char_p_array_free(char** char_p_array)
|
||||
{
|
||||
if (char_p_array == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0 ; char_p_array[i] != NULL; i++) {
|
||||
free(char_p_array[i]);
|
||||
}
|
||||
|
||||
free(char_p_array);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static inline char**
|
||||
copy_array_gchar2char(gchar** gchar_p_array, size_t array_length)
|
||||
{
|
||||
char** char_p_array = char_p_array_calloc(array_length);
|
||||
if (char_p_array == NULL) {
|
||||
LOG_ERROR("copy_array_gchar2char exit with error: could not allocate "
|
||||
"memory for char_p_array");
|
||||
goto error;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0 ; i < array_length; ++i) {
|
||||
char* p = copy_gchar2char(gchar_p_array[i]);
|
||||
if (p == NULL) {
|
||||
goto error;
|
||||
}
|
||||
char_p_array[i] = p;
|
||||
}
|
||||
|
||||
return char_p_array;
|
||||
|
||||
error:
|
||||
char_p_array_free(char_p_array);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
char**
|
||||
gconf_file_get_string_list(GKeyFile* ini_file, const char* section,
|
||||
const char* key, size_t* length, GError** gerror)
|
||||
{
|
||||
assert(ini_file != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
assert(gerror != NULL);
|
||||
|
||||
gsize array_length = 0;
|
||||
gchar** gchar_p_array = g_key_file_get_string_list(ini_file, section, key,
|
||||
&array_length, gerror);
|
||||
if (gchar_p_array == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
char** char_p_array = copy_array_gchar2char(gchar_p_array, array_length);
|
||||
if (char_p_array != NULL) {
|
||||
*length = array_length;
|
||||
}
|
||||
g_strfreev(gchar_p_array);
|
||||
|
||||
return char_p_array;
|
||||
|
||||
error:
|
||||
LOG_TRACE("gconf_file_get_string_list for key '%s::%s' exit with error", section, key);
|
||||
return NULL;
|
||||
}
|
||||
68
src/utils/gconf_file.h
Normal file
68
src/utils/gconf_file.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#ifndef GCONF_FILE_H_INCLUDED
|
||||
#define GCONF_FILE_H_INCLUDED
|
||||
|
||||
#include "str_t.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
/*
|
||||
* utils for working with GKeyFile
|
||||
*/
|
||||
|
||||
/*
|
||||
* Returns the string value from GKeyFile associated with key under section and converted to str_t
|
||||
* @ini_file: GKeyFile
|
||||
* @section: section name in config file
|
||||
* @key: key name
|
||||
* @gerror: return location for a GError, or NULL
|
||||
* @conf_field: str_t which will contain string
|
||||
* Return value: 0 - success, -1 - error.
|
||||
*/
|
||||
|
||||
int gconf_file_get_strt(GKeyFile* ini_file, const char* section, const char* key, GError** gerror, str_t* conf_field);
|
||||
|
||||
|
||||
/*
|
||||
* Returns the locale string value from GKeyFile associated with key under
|
||||
* section and converted to str_t
|
||||
* @ini_file: GKeyFile
|
||||
* @section: section name in config file
|
||||
* @key: key name
|
||||
* @gerror: return location for a GError, or NULL
|
||||
* @conf_field: str_t which will contain string
|
||||
* Return value: 0 - success, -1 - error.
|
||||
*/
|
||||
|
||||
int
|
||||
gconf_file_get_locale_strt(GKeyFile* ini_file,
|
||||
const char* section,
|
||||
const char* key,
|
||||
const char* locale,
|
||||
GError** gerror,
|
||||
str_t* conf_field);
|
||||
|
||||
/*
|
||||
* Returns the string value from GKeyFile associated with key under section
|
||||
* @ini_file: GKeyFile
|
||||
* @section: section name in config file
|
||||
* @key: key name
|
||||
* @gerror: return location for a GError, or NULL
|
||||
* Return value: a newly allocated null-terminated string or NULL
|
||||
* if the specified key cannot be found.
|
||||
*/
|
||||
|
||||
char* gconf_file_get_string(GKeyFile* ini_file, const char* section, const char* key, GError** gerror);
|
||||
|
||||
|
||||
char** gconf_file_get_string_list(GKeyFile* ini_file,
|
||||
const char* section,
|
||||
const char* key,
|
||||
size_t* length,
|
||||
GError** gerror);
|
||||
|
||||
|
||||
void char_p_array_free(char** char_p_array);
|
||||
|
||||
|
||||
#endif // GCONF_FILE_H_INCLUDED
|
||||
63
src/utils/library.c
Normal file
63
src/utils/library.c
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include "library.h"
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static int
|
||||
library_load(library_t *self)
|
||||
{
|
||||
self->m_handle = dlopen(self->filename, RTLD_LAZY); /* RTLD_NOW */
|
||||
if (self->m_handle == NULL) {
|
||||
LOG_ERROR("dlopen failed. Desc: %s", dlerror());
|
||||
goto error;
|
||||
}
|
||||
self->is_loaded = 1;
|
||||
return 0;
|
||||
error:
|
||||
LOG_ERROR("library_load exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void*
|
||||
library_resolve(library_t *self, const char *symbol)
|
||||
{
|
||||
if (!self->m_handle && library_load(self) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *p = dlsym(self->m_handle, symbol);
|
||||
char *err = dlerror();
|
||||
if (err) {
|
||||
LOG_ERROR("dlsym failed. Desc: %s", err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static int
|
||||
library_unload(library_t *self)
|
||||
{
|
||||
int rv = dlclose(self->m_handle);
|
||||
self->m_handle = NULL;
|
||||
self->is_loaded = 0;
|
||||
if (rv != 0) {
|
||||
LOG_ERROR("dlclose failed. Desc: %s", dlerror());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
library_init(library_t *inst, const char *filename)
|
||||
{
|
||||
inst->m_handle = NULL;
|
||||
inst->is_loaded = 0;
|
||||
inst->filename = filename;
|
||||
inst->load = library_load;
|
||||
inst->resolve = library_resolve;
|
||||
inst->unload = library_unload;
|
||||
}
|
||||
18
src/utils/library.h
Normal file
18
src/utils/library.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef LIBRARY_H_INCLUDED
|
||||
#define LIBRARY_H_INCLUDED
|
||||
|
||||
typedef struct library_s library_t;
|
||||
|
||||
struct library_s {
|
||||
const char *filename;
|
||||
void *m_handle;
|
||||
int is_loaded;
|
||||
|
||||
int (*load)(library_t *self);
|
||||
void*(*resolve)(library_t *self, const char *symbol);
|
||||
int(*unload)(library_t *self);
|
||||
};
|
||||
|
||||
void library_init(library_t *inst, const char *filename);
|
||||
|
||||
#endif // LIBRARY_H_INCLUDED
|
||||
15
src/utils/logger.c
Normal file
15
src/utils/logger.c
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#include "logger.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
char *
|
||||
nowsec(void)
|
||||
{
|
||||
static _Thread_local char strbuf[MAXSTRFTIME];
|
||||
time_t now = time(NULL);
|
||||
struct tm lt = {0};
|
||||
|
||||
strftime(strbuf, MAXSTRFTIME, "%Y-%m-%d %H:%M:%S", localtime_r(&now, <));
|
||||
|
||||
return strbuf;
|
||||
}
|
||||
111
src/utils/logger.h
Normal file
111
src/utils/logger.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*********************************************
|
||||
Simple stderr logger based on variadic macros
|
||||
*********************************************/
|
||||
|
||||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAXSTRFTIME 128
|
||||
|
||||
char* nowsec();
|
||||
|
||||
/* Logger configuration */
|
||||
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_WARN_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
# define LOG_DEBUG_ENABLE
|
||||
# define LOG_TRACE_ENABLE
|
||||
#endif
|
||||
|
||||
/* ******************** */
|
||||
|
||||
|
||||
#define LOG_TRAILER "\n"
|
||||
|
||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
|
||||
|
||||
#define TAG_ERROR "ERROR: "
|
||||
#define TAG_WARN "WARN: "
|
||||
#define TAG_INFO "INFO: "
|
||||
#define TAG_DEBUG "DEBUG: "
|
||||
#define TAG_TRACE "TRACE: "
|
||||
|
||||
|
||||
#if defined(LOG_ERROR_ENABLE)
|
||||
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
# define LOG_ERROR(format, ...) fprintf(stderr, "%s [%d] " TAG_ERROR "%s:%*u " format LOG_TRAILER, nowsec(), (int)getpid(), __FILENAME__, (int)(strlen(__FILENAME__)-24), __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
# define LOG_ERROR(format, ...) fprintf(stderr, "%s [%d] " TAG_ERROR format LOG_TRAILER, nowsec(), (int)getpid(), ##__VA_ARGS__)
|
||||
#endif /* DEBUG || _DEBUG*/
|
||||
|
||||
#else
|
||||
# define LOG_ERROR(format, ...)
|
||||
#endif /*LOG_ERROR_ENABLE*/
|
||||
|
||||
|
||||
|
||||
#if defined(LOG_WARN_ENABLE)
|
||||
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
# define LOG_WARN(format, ...) fprintf(stderr, "%s [%d] " TAG_WARN "%s:%*u " format LOG_TRAILER, nowsec(), (int)getpid(), __FILENAME__, (int)(strlen(__FILENAME__)-24), __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
# define LOG_WARN(format, ...) fprintf(stderr, "%s [%d] " TAG_WARN format LOG_TRAILER, nowsec(), (int)getpid(), ##__VA_ARGS__)
|
||||
#endif /* DEBUG || _DEBUG*/
|
||||
|
||||
#else
|
||||
# define LOG_WARN(format, ...)
|
||||
#endif /*LOG_WARN_ENABLE*/
|
||||
|
||||
|
||||
|
||||
#if defined(LOG_INFO_ENABLE)
|
||||
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
# define LOG_INFO(format, ...) fprintf(stderr, "%s [%d] " TAG_INFO "%s:%*u " format LOG_TRAILER, nowsec(), (int)getpid(), __FILENAME__, (int)(strlen(__FILENAME__)-24), __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
# define LOG_INFO(format, ...) fprintf(stderr, "%s [%d] " TAG_INFO format LOG_TRAILER, nowsec(), (int)getpid(), ##__VA_ARGS__)
|
||||
#endif /* DEBUG || _DEBUG*/
|
||||
|
||||
#else
|
||||
# define LOG_INFO(format, ...)
|
||||
#endif /*LOG_INFO_ENABLE*/
|
||||
|
||||
|
||||
|
||||
#if defined(LOG_DEBUG_ENABLE)
|
||||
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
# define LOG_DEBUG(format, ...) fprintf(stderr, "%s [%d] " TAG_DEBUG "%s:%*u " format LOG_TRAILER, nowsec(), (int)getpid(), __FILENAME__, (int)(strlen(__FILENAME__)-24), __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
# define LOG_DEBUG(format, ...) fprintf(stderr, "%s [%d] " TAG_DEBUG format LOG_TRAILER, nowsec(), (int)getpid(), ##__VA_ARGS__)
|
||||
#endif /* DEBUG || _DEBUG*/
|
||||
|
||||
#else
|
||||
# define LOG_DEBUG(format, ...)
|
||||
#endif /*LOG_DEBUG_ENABLE*/
|
||||
|
||||
|
||||
|
||||
#if defined(LOG_TRACE_ENABLE)
|
||||
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
# define LOG_TRACE(format, ...) fprintf(stderr, "%s [%d] " TAG_TRACE "%s:%*u " format LOG_TRAILER, nowsec(), (int)getpid(), __FILENAME__, (int)(strlen(__FILENAME__)-24), __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
# define LOG_TRACE(format, ...) fprintf(stderr, "%s [%d] " TAG_TRACE format LOG_TRAILER, nowsec(), (int)getpid(), ##__VA_ARGS__)
|
||||
#endif /* DEBUG || _DEBUG*/
|
||||
|
||||
#else
|
||||
# define LOG_TRACE(format, ...)
|
||||
#endif /*LOG_TRACE_ENABLE*/
|
||||
|
||||
|
||||
#endif /*LOGGER_H*/
|
||||
51
src/utils/str_t.c
Normal file
51
src/utils/str_t.c
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#include "str_t.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
int
|
||||
str_t_copy(str_t* dst, const str_t* src)
|
||||
{
|
||||
assert(dst != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
str_t_nullify(dst);
|
||||
|
||||
if (str_t_is_null(*src)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(src->data != NULL);
|
||||
|
||||
dst->data = malloc(src->len);
|
||||
if (dst->data == NULL) {
|
||||
LOG_ERROR("Could not allocate memory (%zu bytes)", src->len);
|
||||
goto error;
|
||||
}
|
||||
memcpy(dst->data, src->data, src->len);
|
||||
dst->len = src->len;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("str_t_copy exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
str_t_to_string(const str_t* str)
|
||||
{
|
||||
assert(str != NULL && !str_t_is_null(*str));
|
||||
|
||||
char* string = malloc(str->len + 1);
|
||||
if (string == NULL) {
|
||||
LOG_ERROR("Could not allocate %zu bytes", str->len + 1);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(string, str->data, str->len);
|
||||
string[str->len] = '\0';
|
||||
|
||||
return string;
|
||||
}
|
||||
95
src/utils/str_t.h
Normal file
95
src/utils/str_t.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#ifndef STR_T_H_INCLUDED
|
||||
#define STR_T_H_INCLUDED
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct str_s {
|
||||
char* data;
|
||||
size_t len;
|
||||
|
||||
} str_t;
|
||||
|
||||
#define str_t_null { .data = NULL, .len = 0 }
|
||||
#define str_t_init(_data, _len) { .data = _data, .len = _len }
|
||||
#define str_t_const(c_str) { .data = (char*)c_str, .len = sizeof(c_str)-1 }
|
||||
|
||||
/*
|
||||
str_t_is_null(str)
|
||||
checks if str_t at null-state.
|
||||
IMPORTANT: assumed that str is passed by value, not by pointer!
|
||||
Because macro does not want to bother about pointer value.
|
||||
Userspace is responsible for str_t-pointers.
|
||||
IMPORTANT: it is IMPROPERLY to check like this: (str.len == 0 && str.data == NULL).
|
||||
Because in that case the expression: !str_t_is_null(str) would give a wrong answer.
|
||||
*/
|
||||
#define str_t_is_null(str) ((str).len == 0 || (str).data == NULL)
|
||||
|
||||
#define str_t_is_null_terminated(str) ((str).data ? (str).data[(str).len] == '\0' : 1)
|
||||
|
||||
#define str_t_set_const(str, c_str) { (str).data = c_str; (str).len = sizeof(c_str)-1; }
|
||||
|
||||
static inline void
|
||||
str_t_nullify(str_t* str)
|
||||
{
|
||||
str->data = NULL;
|
||||
str->len = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
str_t_set(str_t* str, char* c_str, size_t c_str_len)
|
||||
{
|
||||
str->data = c_str;
|
||||
if (c_str_len == 0) {
|
||||
str->len = strlen(c_str);
|
||||
} else {
|
||||
str->len = c_str_len;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
str_t_clear(str_t* str)
|
||||
{
|
||||
if (str != NULL) {
|
||||
free(str->data);
|
||||
str_t_nullify(str);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
str_t_free(str_t* str)
|
||||
{
|
||||
str_t_clear(str);
|
||||
free(str);
|
||||
}
|
||||
|
||||
typedef struct const_str_s {
|
||||
const char* data;
|
||||
size_t len;
|
||||
|
||||
} const_str_t;
|
||||
|
||||
#define const_str_t_init(_data, _len) { .data = _data, .len = _len }
|
||||
|
||||
/*
|
||||
* поля структуры заполяются нулями (память не освобождается)
|
||||
*/
|
||||
static inline void
|
||||
const_str_t_null(const_str_t* str)
|
||||
{
|
||||
str->data = NULL;
|
||||
str->len = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
const_str_t_set(const_str_t* str, const char* c_str)
|
||||
{
|
||||
str->data = c_str;
|
||||
str->len = strlen(c_str);
|
||||
}
|
||||
|
||||
int str_t_copy(str_t* dst, const str_t* src);
|
||||
|
||||
char* str_t_to_string(const str_t* str);
|
||||
|
||||
#endif // STR_T_H_INCLUDED
|
||||
176
src/utils/types.h
Normal file
176
src/utils/types.h
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
#ifndef TYPES_H_INCLUDED
|
||||
#define TYPES_H_INCLUDED
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/******************************************************************************/
|
||||
/****************************** int_list_t *********************************/
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
typedef struct int_list_s {
|
||||
int *list;
|
||||
size_t size;
|
||||
} int_list_t;
|
||||
|
||||
|
||||
static inline void
|
||||
int_list_clear(int_list_t *list)
|
||||
{
|
||||
if (list == NULL) return;
|
||||
|
||||
free(list->list);
|
||||
|
||||
memset(list, 0, sizeof(int_list_t));
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
int_list_print(const int_list_t *list __attribute__((unused)))
|
||||
{
|
||||
#if defined(LOG_DEBUG_ENABLE)
|
||||
if (list == NULL) return;
|
||||
|
||||
size_t i = 0;
|
||||
for (; i < list->size; i++) {
|
||||
LOG_DEBUG("\t\t%d", list->list[i]);
|
||||
}
|
||||
|
||||
#endif // defined
|
||||
}
|
||||
|
||||
|
||||
static inline int
|
||||
int_list_copy(int_list_t *dst, const int_list_t *src)
|
||||
{
|
||||
assert(dst != NULL);
|
||||
assert(src != NULL);
|
||||
assert(src->list != NULL);
|
||||
|
||||
size_t size = src->size * sizeof(int);
|
||||
|
||||
dst->list = calloc(src->size, sizeof(int));
|
||||
if (dst->list == NULL) {
|
||||
LOG_ERROR("Could not allocate memory (%zu bytes)", size);
|
||||
goto error;
|
||||
}
|
||||
memcpy(dst->list, src->list, size);
|
||||
dst->size = src->size;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("int_list_copy exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
/*************************** string_list_t *********************************/
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
typedef struct string_list_s {
|
||||
char **list;
|
||||
size_t size;
|
||||
} string_list_t;
|
||||
|
||||
|
||||
static inline string_list_t*
|
||||
string_list_create(size_t size)
|
||||
{
|
||||
string_list_t *list = (string_list_t*) calloc(1, sizeof(string_list_t));
|
||||
if (list == NULL) {
|
||||
LOG_ERROR("Could not allocate memory for string_list_t (size: %zu)",
|
||||
sizeof(string_list_t));
|
||||
goto error;
|
||||
}
|
||||
if (size == 0) {
|
||||
return list;
|
||||
}
|
||||
|
||||
list->size = size;
|
||||
list->list = (char**) calloc(1, size * sizeof(char*));
|
||||
if (list->list == NULL) {
|
||||
LOG_ERROR("Could not allocate memory for string_list_t->list (size: %zu)",
|
||||
size * sizeof(char*));
|
||||
goto error;
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
error:
|
||||
free(list);
|
||||
LOG_ERROR("string_list_create exit with error");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
string_list_clear(string_list_t *list)
|
||||
{
|
||||
if (list == NULL) return;
|
||||
|
||||
size_t i = 0;
|
||||
for (; i < list->size; i++) {
|
||||
free(list->list[i]);
|
||||
}
|
||||
|
||||
free(list->list);
|
||||
|
||||
memset(list, 0, sizeof(string_list_t));
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
string_list_print(const string_list_t *list __attribute__((unused)))
|
||||
{
|
||||
#if defined(LOG_DEBUG_ENABLE)
|
||||
if (list == NULL) return;
|
||||
|
||||
size_t i = 0;
|
||||
for (; i < list->size; i++) {
|
||||
LOG_DEBUG("\t\t%s", list->list[i]);
|
||||
}
|
||||
|
||||
#endif // defined
|
||||
}
|
||||
|
||||
|
||||
static inline int
|
||||
string_list_copy(string_list_t *dst, const string_list_t *src)
|
||||
{
|
||||
assert(dst != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
memset(dst, 0, sizeof(string_list_t));
|
||||
|
||||
if (src->size == 0 && src->list == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(src->size != 0);
|
||||
assert(src->list != NULL);
|
||||
|
||||
size_t size = src->size * sizeof(char*);
|
||||
|
||||
dst->list = calloc(src->size, sizeof(char*));
|
||||
if (dst->list == NULL) {
|
||||
LOG_ERROR("Could not allocate memory (%zu bytes)", size);
|
||||
goto error;
|
||||
}
|
||||
memcpy(dst->list, src->list, size);
|
||||
dst->size = src->size;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOG_ERROR("string_list_copy exit with error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#endif /* TYPES_H_INCLUDED */
|
||||
6
src/version.h.in
Normal file
6
src/version.h.in
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef VERSION_H_INCLUDED
|
||||
#define VERSION_H_INCLUDED
|
||||
|
||||
#define ESIA_MODULE_VERSION "@PROJECT_VERSION@"
|
||||
|
||||
#endif // VERSION_H_INCLUDED
|
||||
137
src/worker.c
Normal file
137
src/worker.c
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#if !defined(_GNU_SOURCE)
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "worker.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <wait.h>
|
||||
#include <execinfo.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
#define BACKTRACE_DEPTH 16
|
||||
|
||||
static service_manager_t services;
|
||||
|
||||
|
||||
static void signal_error(int sig, siginfo_t *si, void *ptr);
|
||||
|
||||
|
||||
int
|
||||
worker_main(const service_manager_conf_t* conf)
|
||||
{
|
||||
struct sigaction sigact;
|
||||
sigset_t sigset;
|
||||
int signo;
|
||||
int status;
|
||||
|
||||
|
||||
// сигналы об ошибках в программе будут обрататывать более тщательно
|
||||
// указываем что хотим получать расширенную информацию об ошибках
|
||||
sigact.sa_flags = SA_SIGINFO;
|
||||
// задаем функцию обработчик сигналов
|
||||
sigact.sa_sigaction = signal_error;
|
||||
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
|
||||
// установим наш обработчик на сигналы
|
||||
|
||||
sigaction(SIGFPE, &sigact, 0); // ошибка FPU
|
||||
sigaction(SIGILL, &sigact, 0); // ошибочная инструкция
|
||||
sigaction(SIGSEGV, &sigact, 0); // ошибка доступа к памяти
|
||||
sigaction(SIGBUS, &sigact, 0); // ошибка шины, при обращении к физической памяти
|
||||
sigaction(SIGABRT, &sigact, 0); // cигнал о прекращении, посланный abort(3)
|
||||
|
||||
sigemptyset(&sigset);
|
||||
|
||||
// блокируем сигналы которые будем ожидать
|
||||
// сигнал остановки процесса пользователем
|
||||
sigaddset(&sigset, SIGQUIT);
|
||||
|
||||
// сигнал для остановки процесса пользователем с терминала
|
||||
sigaddset(&sigset, SIGINT);
|
||||
|
||||
// сигнал запроса завершения процесса
|
||||
sigaddset(&sigset, SIGTERM);
|
||||
|
||||
sigprocmask(SIG_BLOCK, &sigset, NULL);
|
||||
|
||||
// запишем в лог, что наш демон стартовал
|
||||
LOG_INFO("[WORKER] Started");
|
||||
|
||||
// запускаем все рабочие потоки
|
||||
status = init_services(&services, conf);
|
||||
|
||||
if (status) {
|
||||
LOG_ERROR("[WORKER] Create work thread failed");
|
||||
} else {
|
||||
// цикл ожидания сообщений
|
||||
for (;;) {
|
||||
// ждем указанных сообщений
|
||||
sigwait(&sigset, &signo);
|
||||
|
||||
LOG_INFO("[WORKER] signal received: %s", strsignal(signo));
|
||||
|
||||
// выйдим из цикла
|
||||
break;
|
||||
}
|
||||
|
||||
// остановим все рабочеи потоки и корректно закроем всё что надо
|
||||
deinit_services(&services);
|
||||
}
|
||||
|
||||
LOG_INFO("[WORKER] Stopped");
|
||||
|
||||
// вернем код не требующим перезапуска
|
||||
return CHILD_NEED_TERMINATE;
|
||||
}
|
||||
|
||||
|
||||
// функция обработки сигналов
|
||||
static void
|
||||
signal_error(int sig, siginfo_t *si, void *ptr)
|
||||
{
|
||||
void* ErrorAddr;
|
||||
void* Trace[16];
|
||||
int x;
|
||||
int TraceSize;
|
||||
char** Messages;
|
||||
|
||||
// запишем в лог что за сигнал пришел
|
||||
LOG_INFO("Signal: %s, Addr: %p", strsignal(sig), si->si_addr);
|
||||
|
||||
|
||||
#if __WORDSIZE == 64 // если дело имеем с 64 битной ОС
|
||||
// получим адрес инструкции которая вызвала ошибку
|
||||
ErrorAddr = (void*)((ucontext_t*)ptr)->uc_mcontext.gregs[REG_RIP];
|
||||
#else
|
||||
// получим адрес инструкции которая вызвала ошибку
|
||||
ErrorAddr = (void*)((ucontext_t*)ptr)->uc_mcontext.gregs[REG_EIP];
|
||||
#endif
|
||||
|
||||
// произведем backtrace чтобы получить весь стек вызовов
|
||||
TraceSize = backtrace(Trace, BACKTRACE_DEPTH);
|
||||
Trace[1] = ErrorAddr;
|
||||
|
||||
// получим расшифровку трасировки
|
||||
Messages = backtrace_symbols(Trace, TraceSize);
|
||||
if (Messages) {
|
||||
LOG_INFO("== Backtrace ==");
|
||||
|
||||
// запишем в лог
|
||||
for (x = 1; x < TraceSize; x++) {
|
||||
LOG_INFO("%s", Messages[x]);
|
||||
}
|
||||
|
||||
LOG_INFO("== End Backtrace ==");
|
||||
free(Messages);
|
||||
}
|
||||
|
||||
LOG_INFO("Stopped");
|
||||
|
||||
// остановим все рабочие потоки и корректно закроем всё что надо
|
||||
deinit_services(&services);
|
||||
|
||||
// завершим процесс с кодом требующим перезапуска
|
||||
exit(CHILD_NEED_WORK);
|
||||
}
|
||||
13
src/worker.h
Normal file
13
src/worker.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef WORKER_H_INCLUDED
|
||||
#define WORKER_H_INCLUDED
|
||||
|
||||
#include "service_manager.h"
|
||||
|
||||
// константы для кодов завершения процесса
|
||||
#define CHILD_NEED_WORK 1
|
||||
#define CHILD_NEED_TERMINATE 2
|
||||
|
||||
|
||||
int worker_main(const service_manager_conf_t* conf);
|
||||
|
||||
#endif // WORKER_H_INCLUDED
|
||||
Loading…
Add table
Add a link
Reference in a new issue