Skip to content
Snippets Groups Projects
Commit faeb77c5 authored by Jan Včelák's avatar Jan Včelák :rocket:
Browse files

Merge branch 'include_dir' into 'master'

Include Dir
parents f3ac4bd9 8a044fc7
No related branches found
No related tags found
No related merge requests found
......@@ -1132,8 +1132,8 @@ log @{
@stindex include
The @code{include} statement is a special statement which can be used almost
anywhere on any level in the configuration file. It makes inclusion of another
file possible.
anywhere on any level in the configuration file. It allows inclusion of another
file or all files in the given directory.
The path of the included file can be either absolute or relative to a
configuration file currently being processed.
......@@ -1148,6 +1148,7 @@ configuration file currently being processed.
@example
@code{include} "filename"@code{;}
@code{include} "dirname"@code{;}
@end example
@node include Examples
......@@ -1163,4 +1164,6 @@ remotes @{
@}
include "remotes.conf";
@}
include "zones";
@end example
......@@ -25,10 +25,13 @@
%{
#include <config.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "common/sockaddr.h"
#include "knot/conf/conf.h"
......@@ -313,11 +316,7 @@ hmac-sha512 { lval.alg = KNOT_TSIG_ALG_HMAC_SHA512; return TSIG_ALGO_NAME; }
: /* Optional : in assignments. */;
<<EOF>> {
conf_include_t *inc = conf_includes_pop(yyextra->includes);
free(inc->filename);
if (inc->handle) {
fclose(inc->handle);
}
conf_includes_remove(yyextra->includes);
yypop_buffer_state(yyscanner);
......@@ -345,7 +344,7 @@ hmac-sha512 { lval.alg = KNOT_TSIG_ALG_HMAC_SHA512; return TSIG_ALGO_NAME; }
yytext[yyleng] = '\0';
if (!conf_includes_push(yyextra->includes, yytext)) {
cf_error(yyscanner, "includes nested too deeply");
cf_error(yyscanner, "include loop");
return END;
}
......@@ -355,16 +354,84 @@ hmac-sha512 { lval.alg = KNOT_TSIG_ALG_HMAC_SHA512; return TSIG_ALGO_NAME; }
FILE *included = fopen(inc->filename, "r");
if (!included) {
cf_error(yyscanner, "cannot open file '%s'", inc->filename);
conf_includes_pop(yyextra->includes);
free(inc->filename);
conf_includes_remove(yyextra->includes);
return END;
}
inc->handle = included;
YY_BUFFER_STATE bs = yy_create_buffer(included, YY_BUF_SIZE, yyscanner);
yypush_buffer_state(bs, yyscanner);
}
struct stat file_stat;
if (fstat(fileno(included), &file_stat) == -1) {
cf_error(yyscanner, "cannot stat file '%s'", yytext);
conf_includes_remove(yyextra->includes);
return END;
}
if (S_ISDIR(file_stat.st_mode)) {
// Store dirname statically to reduce deallocation.
char dirname[256] = { 0 };
int ret = snprintf(dirname, sizeof(dirname), "%s",
inc->filename);
if (ret <= 0 || ret >= sizeof(dirname)) {
cf_error(yyscanner, "dir name is too long '%s'",
inc->filename);
return END;
}
// Remove include directory from the stack.
conf_includes_remove(yyextra->includes);
DIR *dp = opendir(dirname);
if (dp == NULL) {
cf_error(yyscanner, "cannot open directory '%s'",
dirname);
return END;
}
struct dirent *ep;
while ((ep = readdir(dp)) != NULL) {
// Skip names with leading dot.
if (*ep->d_name == '.') {
continue;
}
char infile[256] = { 0 };
int ret = snprintf(infile, sizeof(infile), "%s/%s",
dirname, ep->d_name);
if (ret <= 0 || ret >= sizeof(infile)) {
cf_error(yyscanner, "cannot open file '%s/%s'",
dirname, ep->d_name);
return END;
}
if (!conf_includes_push(yyextra->includes, infile)) {
cf_error(yyscanner, "include loop");
return END;
}
// retrieved relative to previous config
conf_include_t *inc = conf_includes_top(yyextra->includes);
FILE *included = fopen(inc->filename, "r");
if (!included) {
cf_error(yyscanner, "cannot open file '%s'",
inc->filename);
conf_includes_remove(yyextra->includes);
return END;
}
inc->handle = included;
YY_BUFFER_STATE bs = yy_create_buffer(included,
YY_BUF_SIZE,
yyscanner);
yypush_buffer_state(bs, yyscanner);
}
(void)closedir(dp);
} else {
YY_BUFFER_STATE bs = yy_create_buffer(included, YY_BUF_SIZE,
yyscanner);
yypush_buffer_state(bs, yyscanner);
}
}
<include>["][^"\n]*\n cf_error(yyscanner, "Unterminated string.");
%%
......@@ -40,9 +40,8 @@ static const char *DEFAULT_CONFIG[] = {
CONFIG_DIR "/" "knot.conf",
};
#define DEFAULT_CONF_COUNT 1 /*!< \brief Number of default config paths. */
#define ERROR_BUFFER_SIZE 512 /*!< \brief Error buffer size. */
#define INCLUDES_MAX_DEPTH 8 /*!< \brief Max depth of config inclusion. */
#define DEFAULT_CONF_COUNT 1 /*!< \brief Number of default config paths. */
#define ERROR_BUFFER_SIZE 512 /*!< \brief Error buffer size. */
/*
* Utilities.
......@@ -543,7 +542,7 @@ static int conf_fparser(conf_t *conf)
_parser_res = KNOT_EOK;
new_config->filename = conf->filename;
void *sc = NULL;
conf_extra_t *extra = conf_extra_init(conf->filename, INCLUDES_MAX_DEPTH);
conf_extra_t *extra = conf_extra_init(conf->filename);
cf_lex_init_extra(extra, &sc);
cf_set_in(f, sc);
cf_parse(sc);
......@@ -577,7 +576,7 @@ static int conf_strparser(conf_t *conf, const char *src)
char *oldfn = new_config->filename;
new_config->filename = "(stdin)";
void *sc = NULL;
conf_extra_t *extra = conf_extra_init("", INCLUDES_MAX_DEPTH);
conf_extra_t *extra = conf_extra_init("");
cf_lex_init_extra(extra, &sc);
switch_input(src, sc);
cf_parse(sc);
......
......@@ -26,14 +26,14 @@
/*!
* \brief Init structure with custom data for config parser.
*/
conf_extra_t *conf_extra_init(const char *file, int includes_max_depth)
conf_extra_t *conf_extra_init(const char *file)
{
conf_extra_t *extra = calloc(1, sizeof(conf_extra_t));
if (!extra) {
return NULL;
}
conf_includes_t *includes = conf_includes_init(includes_max_depth);
conf_includes_t *includes = conf_includes_init();
if (!includes) {
free(extra);
return NULL;
......
......@@ -43,11 +43,10 @@ typedef struct {
* \brief Init structure with custom data for config parser.
*
* \param file Name of the main configuration file.
* \param includes_max_depth Max depth of file inclusions.
*
* \return Initialized stucture or NULL.
*/
conf_extra_t *conf_extra_init(const char *file, int includes_max_depth);
conf_extra_t *conf_extra_init(const char *file);
/*!
* \brief Free structure with custom data for config parser.
......
......@@ -23,33 +23,38 @@
#include "knot/conf/includes.h"
#define INCLUDES_CAPACITY_BLOCK 128 // Size of included files block.
/*!
* \brief Structure to store names of files included into the config.
*/
struct conf_includes {
int free_index; //!< First free index in 'names'.
int capacity; //!< Maximal capacity.
conf_include_t files[0]; //!< Stored includes.
conf_include_t *files; //!< Stored includes.
};
/*!
* \brief Initialize structure for storing names of included files.
*/
conf_includes_t *conf_includes_init(int capacity)
conf_includes_t *conf_includes_init()
{
if (capacity <= 0) {
conf_includes_t *includes = calloc(1, sizeof(conf_includes_t));
if (!includes) {
return NULL;
}
size_t size = sizeof(conf_includes_t)
+ (capacity * sizeof(conf_include_t *));
conf_includes_t *result = calloc(1, size);
if (!result) {
conf_include_t *files = calloc(INCLUDES_CAPACITY_BLOCK,
sizeof(conf_include_t));
if (!files) {
free(includes);
return NULL;
}
result->capacity = capacity;
return result;
includes->capacity = INCLUDES_CAPACITY_BLOCK;
includes->files = files;
return includes;
}
/*!
......@@ -61,25 +66,12 @@ void conf_includes_free(conf_includes_t *includes)
return;
}
for (int i = 0; i < includes->free_index; i++) {
free(includes->files[i].filename);
}
while (conf_includes_remove(includes));
free(includes->files);
free(includes);
}
/**
* \brief Check if there is a capacity to insert new file..
*/
bool conf_includes_can_push(conf_includes_t *includes)
{
if (!includes) {
return false;
}
return includes->free_index < includes->capacity;
}
/**
* \brief Constructs a path relative to a reference file.
*
......@@ -120,10 +112,6 @@ bool conf_includes_push(conf_includes_t *includes, const char *filename)
return false;
}
if (!conf_includes_can_push(includes)) {
return false;
}
char *store = NULL;
if (includes->free_index == 0 || filename[0] == '/') {
......@@ -133,6 +121,27 @@ bool conf_includes_push(conf_includes_t *includes, const char *filename)
store = path_relative_to(filename, previous->filename);
}
for (int i = 0; i < includes->free_index; i++) {
// Check for include loop.
if (strcmp(includes->files[i].filename, store) == 0) {
free(store);
return false;
}
}
// Extend the stack if full.
if (includes->free_index >= includes->capacity) {
size_t new_size = (includes->capacity + INCLUDES_CAPACITY_BLOCK) *
sizeof(conf_include_t);
conf_include_t *new_files = realloc(includes->files, new_size);
if (new_files == NULL) {
free(store);
return false;
}
includes->capacity = new_size;
includes->files = new_files;
}
conf_include_t new_include = {
.filename = store,
.handle = NULL
......@@ -152,7 +161,7 @@ conf_include_t *conf_includes_top(conf_includes_t *includes)
return NULL;
}
return &includes->files[includes->free_index - 1];
return includes->files + includes->free_index - 1;
}
/**
......@@ -167,3 +176,25 @@ conf_include_t *conf_includes_pop(conf_includes_t *includes)
return result;
}
/**
* \brief Returns an include on the top of the stack and removes it.
*/
bool conf_includes_remove(conf_includes_t *includes)
{
conf_include_t *top = conf_includes_pop(includes);
if (top) {
if (top->filename) {
free(top->filename);
top->filename = NULL;
}
if (top->handle) {
fclose(top->handle);
top->handle = NULL;
}
return true;
}
return false;
}
......@@ -46,21 +46,14 @@ typedef struct conf_includes conf_includes_t;
/*!
* \brief Initialize structure for storing names of included files.
*
* \param capacity Max depth of the inclusion.
*/
conf_includes_t *conf_includes_init(int capacity);
conf_includes_t *conf_includes_init();
/*!
* \brief Free structure for storing the names of included files.
*/
void conf_includes_free(conf_includes_t *includes);
/**
* \brief Check if there is a capacity to insert new file.
*/
bool conf_includes_can_push(conf_includes_t *includes);
/**
* \brief Pushes a file name onto the stack of files.
*
......@@ -87,6 +80,13 @@ conf_include_t *conf_includes_top(conf_includes_t *includes);
*/
conf_include_t *conf_includes_pop(conf_includes_t *includes);
/**
* \brief Remove the include on the top.
*
* \return True if the include was removed.
*/
bool conf_includes_remove(conf_includes_t *includes);
#endif /* _KNOT_CONF_INCLUDES_H_ */
/*! @} */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment