/*
* Copyright (c) 2020 YuQing <384681@qq.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the Lesser GNU General Public License, version 3
* or later ("LGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the Lesser GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//ini_file_reader.c
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <dlfcn.h>
#include "shared_func.h"
#include "logger.h"
#include "http_func.h"
#include "local_ip_func.h"
#include "pthread_func.h"
#include "fc_memory.h"
#include "ini_file_reader.h"
#define _LINE_BUFFER_SIZE 512
#define _INIT_ALLOC_ITEM_COUNT 32
#define _PREPROCESS_TAG_STR_IF "#@if "
#define _PREPROCESS_TAG_STR_ELSE "#@else"
#define _PREPROCESS_TAG_STR_ENDIF "#@endif"
#define _PREPROCESS_TAG_STR_FOR "#@for "
#define _PREPROCESS_TAG_STR_ENDFOR "#@endfor"
#define _PREPROCESS_TAG_STR_SET "#@set "
#define _PREPROCESS_TAG_LEN_IF (sizeof(_PREPROCESS_TAG_STR_IF) - 1)
#define _PREPROCESS_TAG_LEN_ELSE (sizeof(_PREPROCESS_TAG_STR_ELSE) - 1)
#define _PREPROCESS_TAG_LEN_ENDIF (sizeof(_PREPROCESS_TAG_STR_ENDIF) - 1)
#define _PREPROCESS_TAG_LEN_FOR (sizeof(_PREPROCESS_TAG_STR_FOR) - 1)
#define _PREPROCESS_TAG_LEN_ENDFOR (sizeof(_PREPROCESS_TAG_STR_ENDFOR) - 1)
#define _PREPROCESS_TAG_LEN_SET (sizeof(_PREPROCESS_TAG_STR_SET) - 1)
#define _PREPROCESS_VARIABLE_STR_LOCAL_IP "%{LOCAL_IP}"
#define _PREPROCESS_VARIABLE_STR_LOCAL_HOST "%{LOCAL_HOST}"
#define _PREPROCESS_VARIABLE_LEN_LOCAL_IP \
(sizeof(_PREPROCESS_VARIABLE_STR_LOCAL_IP) - 1)
#define _PREPROCESS_VARIABLE_LEN_LOCAL_HOST \
(sizeof(_PREPROCESS_VARIABLE_STR_LOCAL_HOST) - 1)
#define _PREPROCESS_TAG_STR_FOR_FROM "from"
#define _PREPROCESS_TAG_LEN_FOR_FROM (sizeof(_PREPROCESS_TAG_STR_FOR_FROM) - 1)
#define _PREPROCESS_TAG_STR_FOR_TO "to"
#define _PREPROCESS_TAG_LEN_FOR_TO (sizeof(_PREPROCESS_TAG_STR_FOR_TO) - 1)
#define _PREPROCESS_TAG_STR_FOR_STEP "step"
#define _PREPROCESS_TAG_LEN_FOR_STEP (sizeof(_PREPROCESS_TAG_STR_FOR_STEP) - 1)
#define _INIT_DYNAMIC_CONTENTS 8
#define _BUILTIN_ANNOTATION_COUNT 3
static AnnotationEntry *g_annotations = NULL;
static int g_annotation_count = 0;
typedef struct {
int count;
int alloc;
char **contents;
} DynamicContents;
typedef struct {
int offset; //deal offset
HashArray *vars; //variables with #@set
} SetDirectiveVars;
typedef struct {
int count;
int alloc;
AnnotationEntry *annotations;
} DynamicAnnotations;
typedef struct {
bool used;
IniContext *context;
DynamicContents dynamicContents;
SetDirectiveVars set;
DynamicAnnotations dynamicAnnotations;
} CDCPair;
typedef struct {
volatile int init_counter;
int alloc;
int count;
int index;
CDCPair *contents;
pthread_mutex_t lock;
} DynamicContentArray;
//dynamic alloced contents which will be freed when destroy
static DynamicContentArray g_dynamic_content_array = {0, 0, 0, 0, NULL};
static int remallocSection(IniSection *pSection, IniItem **pItem);
static int iniDoLoadFromFile(const char *szFilename, \
IniContext *pContext);
static int iniLoadItemsFromBuffer(char *content, \
IniContext *pContext);
static DynamicAnnotations *iniAllocAnnotations(IniContext *pContext,
const int annotation_count);
static AnnotationEntry *iniGetAnnotations(IniContext *pContext);
static SetDirectiveVars *iniGetVars(IniContext *pContext);
#define RETRY_FETCH_GLOBAL(szSectionName, bRetryGlobal) \
((szSectionName != NULL && *szSectionName != '\0') && bRetryGlobal)
static void iniDoSetAnnotations(AnnotationEntry *src, const int src_count,
AnnotationEntry *dest, int *dest_count)
{
AnnotationEntry *pSrc;
AnnotationEntry *pSrcEnd;
AnnotationEntry *pDest;
AnnotationEntry *pDestEnd;
pSrcEnd = src + src_count;
pDestEnd = dest + *dest_count;
for (pSrc=src; pSrc<pSrcEnd; pSrc++)
{
for (pDest=dest; pDest<pDestEnd; pDest++)
{
if (strcmp(pSrc->func_name, pDest->func_name) == 0)
{
break;
}
}
pDest->func_name = pSrc->func_name;
pDest->arg = pSrc->arg;
pDest->func_init = pSrc->func_init;
pDest->func_destroy = pSrc->func_destroy;
pDest->func_get = pSrc->func_get;
pDest->func_free = pSrc->func_free;
pDest->dlhandle = pSrc->dlhandle;
pDest->inited = false;
if (pDest == pDestEnd) //insert
{
++(*dest_count);
pDestEnd = dest + *dest_count;
}
}
}
static AnnotationEntry *iniFindAnnotation(AnnotationEntry *annotatios,
const char *func_name)
{
AnnotationEntry *pAnnoEntry;
if (annotatios == NULL)
{
return NULL;
}
pAnnoEntry = annotatios;
while (pAnnoEntry->func_name != NULL)
{
if (strcmp(func_name, pAnnoEntry->func_name) == 0)
{
return pAnnoEntry;
}
pAnnoEntry++;
}
return NULL;
}
static int iniAnnotationFuncLocalIpGet(IniContext *context,
struct ini_annotation_entry *annotation,
const IniItem *item, char **pOutValue, int max_values)
{
bool need_private_ip;
int count;
int index;
char param[FAST_INI_ITEM_VALUE_SIZE];
const char *next_ip;
char *square_start;
char name_part[16];
strcpy(param, item->value);
memset(name_part, 0, sizeof(name_part));
square_start = strchr(param, '[');
if (square_start != NULL && param[strlen(param) - 1] == ']') {
snprintf(name_part, sizeof(name_part) - 1, "%.*s",
(int)(square_start - param), param);
index = atoi(square_start + 1);
} else {
snprintf(name_part, sizeof(name_part) - 1, "%s", param);
index = -2;
}
need_private_ip = strcasecmp(name_part, "inner") == 0 ||
strcasecmp(name_part, "private") == 0;
next_ip = NULL;
count = 0;
while ((next_ip=get_next_local_ip(next_ip)) != NULL) {
if (count >= max_values) {
break;
}
if (is_private_ip(next_ip)) {
if (need_private_ip) {
pOutValue[count++] = (char *)next_ip;
}
} else {
if (!need_private_ip) {
pOutValue[count++] = (char *)next_ip;
}
}
}
if (count == 0) {
pOutValue[count++] = "";
} else if (index > -2) {
if (index == -1) { //get the last one
if (count > 1) {
pOutValue[0] = pOutValue[count - 1];
}
} else if (index >= count) { //index overflow
logWarning("file: "__FILE__", line: %d, "
"index: %d >= count: %d, set value to empty",
__LINE__, index, count);
pOutValue[0] = "";
} else if (index > 0) {
pOutValue[0] = pOutValue[index];
}
count = 1;
}
return count;
}
static int iniAnnotationFuncShellExec(IniContext *context,
struct ini_annotation_entry *annotation,
const IniItem *item, char **pOutValue, int max_values)
{
int count;
int result;
char *output;
count = 0;
output = (char *)fc_malloc(FAST_INI_ITEM_VALUE_SIZE);
if (output == NULL) {
return count;
}
if ((result=getExecResult(item->value, output, FAST_INI_ITEM_VALUE_SIZE)) != 0)
{
logWarning("file: "__FILE__", line: %d, "
"exec %s fail, errno: %d, error info: %s",
__LINE__, item->value, result, STRERROR(result));
free(output);
return count;
}
if (*output == '\