forked from lda/telodendria
Convert configuration file to JSON
This commit is contained in:
parent
c0534b0e05
commit
fb5a8e4587
8 changed files with 260 additions and 1154 deletions
3
TODO.txt
3
TODO.txt
|
@ -28,9 +28,6 @@ Due: January 1, 2023
|
||||||
[x] Array
|
[x] Array
|
||||||
[x] Base64
|
[x] Base64
|
||||||
[x] CanonicalJson
|
[x] CanonicalJson
|
||||||
[ ] Config
|
|
||||||
[ ] API (Config.3)
|
|
||||||
[ ] File format (Config.5)
|
|
||||||
[x] HashMap
|
[x] HashMap
|
||||||
[ ] Http
|
[ ] Http
|
||||||
[ ] HttpServer
|
[ ] HttpServer
|
||||||
|
|
|
@ -1,20 +1,14 @@
|
||||||
#
|
{
|
||||||
# Telodendria development configuration file.
|
"serverName": "localhost",
|
||||||
#
|
"baseUrl": "http://localhost:8008",
|
||||||
|
"dataDir": "./data",
|
||||||
server-name "localhost";
|
"federation": true,
|
||||||
base-url "http://localhost:8008";
|
"registration": true,
|
||||||
|
"threads": 2,
|
||||||
# Make this directory if Telodendria complains that it's missing.
|
"log": {
|
||||||
data-dir "./data";
|
"output": "stdout",
|
||||||
|
"level": "debug",
|
||||||
federation "true";
|
"timestampFormat": "none",
|
||||||
registration "true";
|
"color": true
|
||||||
log "stdout" {
|
}
|
||||||
# level "debug";
|
}
|
||||||
timestampFormat "none";
|
|
||||||
color "true";
|
|
||||||
};
|
|
||||||
threads "4";
|
|
||||||
max-connections "32";
|
|
||||||
max-cache "1K";
|
|
||||||
|
|
|
@ -1,33 +1,13 @@
|
||||||
#
|
{
|
||||||
# Telodendria configuration file.
|
"serverName": "example.com",
|
||||||
#
|
"baseUrl": "https://matrix.example.com",
|
||||||
# The following man pages document the configuration:
|
"identityServer": "https://identity.example.com",
|
||||||
#
|
"dataDir": "/var/telodendria",
|
||||||
# - telodendria.conf(5)
|
"federation": true,
|
||||||
# - Config(5)
|
"registration": false,
|
||||||
#
|
"threads": 4,
|
||||||
# Alternatively, find the man pages online at the
|
"maxCache": 512000000,
|
||||||
# following URL:
|
"log": {
|
||||||
#
|
"output": "file"
|
||||||
# https://telodendria.io/#documentation
|
}
|
||||||
#
|
}
|
||||||
|
|
||||||
listen "8008";
|
|
||||||
|
|
||||||
server-name "example.com";
|
|
||||||
base-url "https://matrix.example.com";
|
|
||||||
identity-server "https://identity.example.com";
|
|
||||||
|
|
||||||
data-dir "/var/telodendria";
|
|
||||||
|
|
||||||
federation "true";
|
|
||||||
registration "false";
|
|
||||||
|
|
||||||
log "file" {
|
|
||||||
level "message";
|
|
||||||
timestampFormat "default";
|
|
||||||
};
|
|
||||||
|
|
||||||
threads "4";
|
|
||||||
max-connections "32";
|
|
||||||
max-cache "512M";
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.Dd $Mdocdate: November 8 2022 $
|
.Dd $Mdocdate: December 9 2022 $
|
||||||
.Dt TELODENDRIA.CONF 5
|
.Dt TELODENDRIA.CONF 5
|
||||||
.Os Telodendria Project
|
.Os Telodendria Project
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
@ -16,21 +16,13 @@ option, and is typically located at
|
||||||
.Pa /etc/telodendria.conf
|
.Pa /etc/telodendria.conf
|
||||||
.sp
|
.sp
|
||||||
.Nm
|
.Nm
|
||||||
uses OpenBSD-style syntax, though it is a little more rigid in its
|
uses JSON for its configuration file syntax, which should be
|
||||||
parser. All values must be surrounded by quotes, and each directive
|
familiar. Very early versions of
|
||||||
must be ended with a semicolon.
|
.Nm
|
||||||
.Sh MACROS
|
used a custom OpenBSD-style configuration file, but this was
|
||||||
Macros can be defined that will later be expanded in context.
|
not as versatile or familiar as JSON.
|
||||||
Macro names must start with a letter, digit, or underscore, and may
|
.Sh DIRECTIVES
|
||||||
contain only those characters. Macros are not expanded inside quotes.
|
Here are the top-level directives:
|
||||||
.sp
|
|
||||||
For example:
|
|
||||||
.Bd -literal -offset indent
|
|
||||||
macro1 = "value1";
|
|
||||||
directive $macro1;
|
|
||||||
.Ed
|
|
||||||
.Sh GLOBAL OPTIONS
|
|
||||||
Here are the settings that can be set globally:
|
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Ic listen Ar port
|
.It Ic listen Ar port
|
||||||
The port to listen on. Telodendria will bind to all interfaces, but it
|
The port to listen on. Telodendria will bind to all interfaces, but it
|
||||||
|
@ -47,7 +39,7 @@ the internet.
|
||||||
.Ar port
|
.Ar port
|
||||||
should be a decimal port number. This directive is entirely optional. If
|
should be a decimal port number. This directive is entirely optional. If
|
||||||
it is omitted, then Telodendria will listen on port 8008 by default.
|
it is omitted, then Telodendria will listen on port 8008 by default.
|
||||||
.It Ic server-name Ar name
|
.It Ic serverName Ar name
|
||||||
Configure the domain name of your homeserver. Note that Matrix servers
|
Configure the domain name of your homeserver. Note that Matrix servers
|
||||||
cannot be migrated to other domains, so once this is set, it should never
|
cannot be migrated to other domains, so once this is set, it should never
|
||||||
change unless you want unexpected things to happen, or you want to start
|
change unless you want unexpected things to happen, or you want to start
|
||||||
|
@ -55,7 +47,7 @@ over.
|
||||||
.Ar name
|
.Ar name
|
||||||
should be a DNS name that can be publically resolved. This directive
|
should be a DNS name that can be publically resolved. This directive
|
||||||
is required.
|
is required.
|
||||||
.It Ic base-url Ar url
|
.It Ic baseUrl Ar url
|
||||||
Set the server's base URL.
|
Set the server's base URL.
|
||||||
.Ar url
|
.Ar url
|
||||||
should be a valid URL, complete with the protocol. It does not need to
|
should be a valid URL, complete with the protocol. It does not need to
|
||||||
|
@ -69,16 +61,16 @@ manifest.
|
||||||
.Pp
|
.Pp
|
||||||
This directive is optional. If it is not specified, it is automatically
|
This directive is optional. If it is not specified, it is automatically
|
||||||
deduced from the server name.
|
deduced from the server name.
|
||||||
.It Ic identity-server Ar url
|
.It Ic identityServer Ar url
|
||||||
The identity server that clients should use to perform identity lookups.
|
The identity server that clients should use to perform identity lookups.
|
||||||
.Pp
|
.Pp
|
||||||
.Ar url
|
.Ar url
|
||||||
follows the same rules as
|
follows the same rules as
|
||||||
.Ic base-url .
|
.Ic baseUrl .
|
||||||
.Pp
|
.Pp
|
||||||
This directive is optional. If it is not specified, it is automatically
|
This directive is optional. If it is not specified, it is automatically
|
||||||
set to be the same as the base URL.
|
set to be the same as the base URL.
|
||||||
.It Ic id Ar uid Ar gid
|
.It Ic runAs Ar uidObj
|
||||||
The effective UNIX user and group to drop to after binding to the socket
|
The effective UNIX user and group to drop to after binding to the socket
|
||||||
and changing the filesystem root for the process. This only works if
|
and changing the filesystem root for the process. This only works if
|
||||||
Telodendria is running as the root user, and is used as a security mechanism.
|
Telodendria is running as the root user, and is used as a security mechanism.
|
||||||
|
@ -86,7 +78,20 @@ If this option is set and Telodendria is started as a non-priviledged user,
|
||||||
then a warning is printed to the log if that user does not match what's
|
then a warning is printed to the log if that user does not match what's
|
||||||
specified here. This directive is optional, but should be used as a sanity
|
specified here. This directive is optional, but should be used as a sanity
|
||||||
check, if nothing more, to make sure the permissions are working properly.
|
check, if nothing more, to make sure the permissions are working properly.
|
||||||
.It Ic data-dir Ar directory
|
.Pp
|
||||||
|
This directive takes an object with the following directives:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Ic uid Ar user
|
||||||
|
The UNIX username to drop to. If
|
||||||
|
.Ic runAs
|
||||||
|
is specified, this directive is required.
|
||||||
|
.It Ic gid Ar group
|
||||||
|
The UNIX group to drop to. This directive is optional; if it is not
|
||||||
|
specified, then the value of
|
||||||
|
.Ic uid
|
||||||
|
is copied.
|
||||||
|
.El
|
||||||
|
.It Ic dataDir Ar directory
|
||||||
The data directory into which Telodendria will write all user and event
|
The data directory into which Telodendria will write all user and event
|
||||||
information. Telodendria doesn't use a database like other Matrix homeserver
|
information. Telodendria doesn't use a database like other Matrix homeserver
|
||||||
implementations; it uses a flat-file directory structure, similar to how an
|
implementations; it uses a flat-file directory structure, similar to how an
|
||||||
|
@ -122,27 +127,27 @@ to run their own homeserver, you can aset this to
|
||||||
which will allow anyone to create an account. Telodendria should be capable of handling
|
which will allow anyone to create an account. Telodendria should be capable of handling
|
||||||
a large amount of users without difficulty or security issues. This directive is
|
a large amount of users without difficulty or security issues. This directive is
|
||||||
required.
|
required.
|
||||||
.It Ic log Ar stdout|file|syslog
|
.It Ic log Ar logObj
|
||||||
The log configuration. Telodendria uses its own logging facility, which can output
|
The log file configuration. Telodendria uses its own logging facility, which can
|
||||||
logs to standard output, a file, or the syslog. If set to
|
output logs to standard output, a file, or the syslog. This directive is required,
|
||||||
|
and it takes an object with the following directives:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Ic output Ar stdout|file|syslog
|
||||||
|
The lot output destination. If set to
|
||||||
.Ar file ,
|
.Ar file ,
|
||||||
Telodendria will log to
|
Telodendria will log to
|
||||||
.Pa telodendria.log
|
.Pa telodendria.log
|
||||||
inside the
|
inside the
|
||||||
.Ic data-dir .
|
.Ic dataDir .
|
||||||
.Pp
|
.It Ic level Ar error|warning|notice|message|debug
|
||||||
A number of child directives can
|
|
||||||
be added to this directive to customize the log output:
|
|
||||||
.Bl -tag -width Ds
|
|
||||||
.It Ic level Ar error|warning|task|message|debug
|
|
||||||
The level of messages to log at. Each level shows all the levels above it. For
|
The level of messages to log at. Each level shows all the levels above it. For
|
||||||
example, setting the level to
|
example, setting the level to
|
||||||
.Ar error
|
.Ar error
|
||||||
will show only errors, while setting the level to
|
will show only errors, while setting the level to
|
||||||
.Ar warning
|
.Ar warning
|
||||||
will show warnings and errors.
|
will show warnings and errors.
|
||||||
.Ar task
|
.Ar notice
|
||||||
shows tasks, warnings, and errors, and so on. The
|
shows notices, warnings, and errors, and so on. The
|
||||||
.Ar debug
|
.Ar debug
|
||||||
level shows all messages.
|
level shows all messages.
|
||||||
.It Ic timestampFormat Ar format|none|default
|
.It Ic timestampFormat Ar format|none|default
|
||||||
|
@ -171,11 +176,11 @@ less than the total CPU core count, to prevent overloading the system. The most
|
||||||
efficient number of threads ultimately depends on the configuration of the
|
efficient number of threads ultimately depends on the configuration of the
|
||||||
machine running Telodendria, so you may just have to play around with different
|
machine running Telodendria, so you may just have to play around with different
|
||||||
values here to see which gives the best performance.
|
values here to see which gives the best performance.
|
||||||
.It Ic max-connections Ar count
|
.It Ic maxConnections Ar count
|
||||||
The maximum number of simultanious connections to allow to the daemon. This option
|
The maximum number of simultanious connections to allow to the daemon. This option
|
||||||
prevents the daemon from allocating large amounts of memory in the even that it
|
prevents the daemon from allocating large amounts of memory in the even that it
|
||||||
undergoes a denial of service attack. It typically does not need to be adjusted.
|
undergoes a denial of service attack. It typically does not need to be adjusted.
|
||||||
.It Ic max-cache Ar bytes
|
.It Ic maxCache Ar bytes
|
||||||
The maximum size of the cache. Telodendria relies heavily on caching to speed
|
The maximum size of the cache. Telodendria relies heavily on caching to speed
|
||||||
things up. The cache grows as data is loaded from the data directory. All cache
|
things up. The cache grows as data is loaded from the data directory. All cache
|
||||||
is stored in memory. This option limits the size of the memory cache. If you have
|
is stored in memory. This option limits the size of the memory cache. If you have
|
||||||
|
|
544
src/Config.c
544
src/Config.c
|
@ -1,544 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person
|
|
||||||
* obtaining a copy of this software and associated documentation files
|
|
||||||
* (the "Software"), to deal in the Software without restriction,
|
|
||||||
* including without limitation the rights to use, copy, modify, merge,
|
|
||||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
||||||
* and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
#include <Config.h>
|
|
||||||
|
|
||||||
#include <Memory.h>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifndef CONFIG_BUFFER_BLOCK
|
|
||||||
#define CONFIG_BUFFER_BLOCK 32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct ConfigDirective
|
|
||||||
{
|
|
||||||
Array *values;
|
|
||||||
HashMap *children;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ConfigParseResult
|
|
||||||
{
|
|
||||||
unsigned int ok:1;
|
|
||||||
union
|
|
||||||
{
|
|
||||||
size_t lineNumber;
|
|
||||||
HashMap *confMap;
|
|
||||||
} data;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum ConfigToken
|
|
||||||
{
|
|
||||||
TOKEN_UNKNOWN,
|
|
||||||
TOKEN_NAME,
|
|
||||||
TOKEN_MACRO_ASSIGNMENT,
|
|
||||||
TOKEN_VALUE,
|
|
||||||
TOKEN_SEMICOLON,
|
|
||||||
TOKEN_BLOCK_OPEN,
|
|
||||||
TOKEN_BLOCK_CLOSE,
|
|
||||||
TOKEN_MACRO,
|
|
||||||
TOKEN_EOF
|
|
||||||
} ConfigToken;
|
|
||||||
|
|
||||||
typedef struct ConfigParserState
|
|
||||||
{
|
|
||||||
FILE *stream;
|
|
||||||
unsigned int line;
|
|
||||||
|
|
||||||
char *token;
|
|
||||||
size_t tokenSize;
|
|
||||||
size_t tokenLen;
|
|
||||||
ConfigToken tokenType;
|
|
||||||
|
|
||||||
HashMap *macroMap;
|
|
||||||
|
|
||||||
} ConfigParserState;
|
|
||||||
|
|
||||||
unsigned int
|
|
||||||
ConfigParseResultOk(ConfigParseResult * result)
|
|
||||||
{
|
|
||||||
return result ? result->ok : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
ConfigParseResultLineNumber(ConfigParseResult * result)
|
|
||||||
{
|
|
||||||
return result && !result->ok ? result->data.lineNumber : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMap *
|
|
||||||
ConfigParseResultGet(ConfigParseResult * result)
|
|
||||||
{
|
|
||||||
return result && result->ok ? result->data.confMap : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ConfigParseResultFree(ConfigParseResult * result)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Note that if the parse was valid, the hash map
|
|
||||||
* needs to be freed separately.
|
|
||||||
*/
|
|
||||||
Free(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
Array *
|
|
||||||
ConfigValuesGet(ConfigDirective * directive)
|
|
||||||
{
|
|
||||||
return directive ? directive->values : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMap *
|
|
||||||
ConfigChildrenGet(ConfigDirective * directive)
|
|
||||||
{
|
|
||||||
return directive ? directive->children : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
ConfigDirectiveFree(ConfigDirective * directive)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (!directive)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ArraySize(directive->values); i++)
|
|
||||||
{
|
|
||||||
Free(ArrayGet(directive->values, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayFree(directive->values);
|
|
||||||
|
|
||||||
ConfigFree(directive->children);
|
|
||||||
|
|
||||||
Free(directive);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ConfigFree(HashMap * conf)
|
|
||||||
{
|
|
||||||
char *key;
|
|
||||||
void *value;
|
|
||||||
|
|
||||||
while (HashMapIterate(conf, &key, &value))
|
|
||||||
{
|
|
||||||
ConfigDirectiveFree((ConfigDirective *) value);
|
|
||||||
Free(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMapFree(conf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConfigParserState *
|
|
||||||
ConfigParserStateCreate(FILE * stream)
|
|
||||||
{
|
|
||||||
ConfigParserState *state = Malloc(sizeof(ConfigParserState));
|
|
||||||
|
|
||||||
if (!state)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->macroMap = HashMapCreate();
|
|
||||||
|
|
||||||
if (!state->macroMap)
|
|
||||||
{
|
|
||||||
Free(state);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->stream = stream;
|
|
||||||
state->line = 1;
|
|
||||||
state->token = NULL;
|
|
||||||
state->tokenSize = 0;
|
|
||||||
state->tokenLen = 0;
|
|
||||||
state->tokenType = TOKEN_UNKNOWN;
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
ConfigParserStateFree(ConfigParserState * state)
|
|
||||||
{
|
|
||||||
char *key;
|
|
||||||
void *value;
|
|
||||||
|
|
||||||
if (!state)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Free(state->token);
|
|
||||||
|
|
||||||
while (HashMapIterate(state->macroMap, &key, &value))
|
|
||||||
{
|
|
||||||
Free(key);
|
|
||||||
Free(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMapFree(state->macroMap);
|
|
||||||
|
|
||||||
Free(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
ConfigIsNameChar(int c)
|
|
||||||
{
|
|
||||||
return isdigit(c) || isalpha(c) || (c == '-' || c == '_');
|
|
||||||
}
|
|
||||||
|
|
||||||
static char
|
|
||||||
ConfigConsumeWhitespace(ConfigParserState * state)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
|
|
||||||
while (isspace(c = fgetc(state->stream)))
|
|
||||||
{
|
|
||||||
if (c == '\n')
|
|
||||||
{
|
|
||||||
state->line++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
ConfigConsumeLine(ConfigParserState * state)
|
|
||||||
{
|
|
||||||
while (fgetc(state->stream) != '\n');
|
|
||||||
state->line++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
ConfigTokenSeek(ConfigParserState * state)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
|
|
||||||
/* If we already hit EOF, don't do anything */
|
|
||||||
if (state->tokenType == TOKEN_EOF)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while ((c = ConfigConsumeWhitespace(state)) == '#')
|
|
||||||
{
|
|
||||||
ConfigConsumeLine(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* After all whitespace and comments are consumed, identify the
|
|
||||||
* token by looking at the next character
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (feof(state->stream))
|
|
||||||
{
|
|
||||||
state->tokenType = TOKEN_EOF;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ConfigIsNameChar(c))
|
|
||||||
{
|
|
||||||
state->tokenLen = 0;
|
|
||||||
|
|
||||||
/* Read the key/macro into state->token */
|
|
||||||
if (!state->token)
|
|
||||||
{
|
|
||||||
state->tokenSize = CONFIG_BUFFER_BLOCK;
|
|
||||||
state->token = Malloc(CONFIG_BUFFER_BLOCK);
|
|
||||||
}
|
|
||||||
state->token[state->tokenLen] = c;
|
|
||||||
state->tokenLen++;
|
|
||||||
|
|
||||||
while (ConfigIsNameChar((c = fgetc(state->stream))))
|
|
||||||
{
|
|
||||||
state->token[state->tokenLen] = c;
|
|
||||||
state->tokenLen++;
|
|
||||||
|
|
||||||
if (state->tokenLen >= state->tokenSize)
|
|
||||||
{
|
|
||||||
state->tokenSize += CONFIG_BUFFER_BLOCK;
|
|
||||||
state->token = Realloc(state->token,
|
|
||||||
state->tokenSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state->token[state->tokenLen] = '\0';
|
|
||||||
state->tokenLen++;
|
|
||||||
|
|
||||||
if (!isspace(c))
|
|
||||||
{
|
|
||||||
state->tokenType = TOKEN_UNKNOWN;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->tokenType = TOKEN_NAME;
|
|
||||||
|
|
||||||
if (c == '\n')
|
|
||||||
{
|
|
||||||
state->line++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case '=':
|
|
||||||
state->tokenType = TOKEN_MACRO_ASSIGNMENT;
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
state->tokenLen = 0;
|
|
||||||
state->tokenType = TOKEN_VALUE;
|
|
||||||
|
|
||||||
/* read the value into state->curtok */
|
|
||||||
while ((c = fgetc(state->stream)) != '"')
|
|
||||||
{
|
|
||||||
if (c == '\n')
|
|
||||||
{
|
|
||||||
state->line++;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* End of the stream reached without finding
|
|
||||||
* a closing quote
|
|
||||||
*/
|
|
||||||
if (feof(state->stream))
|
|
||||||
{
|
|
||||||
state->tokenType = TOKEN_EOF;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
state->token[state->tokenLen] = c;
|
|
||||||
state->tokenLen++;
|
|
||||||
|
|
||||||
if (state->tokenLen >= state->tokenSize)
|
|
||||||
{
|
|
||||||
state->tokenSize += CONFIG_BUFFER_BLOCK;
|
|
||||||
state->token = Realloc(state->token,
|
|
||||||
state->tokenSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->token[state->tokenLen] = '\0';
|
|
||||||
state->tokenLen++;
|
|
||||||
break;
|
|
||||||
case ';':
|
|
||||||
state->tokenType = TOKEN_SEMICOLON;
|
|
||||||
break;
|
|
||||||
case '{':
|
|
||||||
state->tokenType = TOKEN_BLOCK_OPEN;
|
|
||||||
break;
|
|
||||||
case '}':
|
|
||||||
state->tokenType = TOKEN_BLOCK_CLOSE;
|
|
||||||
break;
|
|
||||||
case '$':
|
|
||||||
state->tokenLen = 0;
|
|
||||||
/* read the macro name into state->curtok */
|
|
||||||
while (ConfigIsNameChar(c = fgetc(state->stream)))
|
|
||||||
{
|
|
||||||
state->token[state->tokenLen] = c;
|
|
||||||
state->tokenLen++;
|
|
||||||
|
|
||||||
if (state->tokenLen >= state->tokenSize)
|
|
||||||
{
|
|
||||||
state->tokenSize += CONFIG_BUFFER_BLOCK;
|
|
||||||
state->token = Realloc(state->token,
|
|
||||||
state->tokenSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->token[state->tokenLen] = '\0';
|
|
||||||
state->tokenLen++;
|
|
||||||
state->tokenType = TOKEN_MACRO;
|
|
||||||
|
|
||||||
ungetc(c, state->stream);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
state->tokenType = TOKEN_UNKNOWN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resize curtok to only use the bytes it needs */
|
|
||||||
if (state->tokenLen)
|
|
||||||
{
|
|
||||||
state->tokenSize = state->tokenLen;
|
|
||||||
state->token = Realloc(state->token, state->tokenSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
ConfigExpect(ConfigParserState * state, ConfigToken tokenType)
|
|
||||||
{
|
|
||||||
return state->tokenType == tokenType;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static HashMap *
|
|
||||||
ConfigParseBlock(ConfigParserState * state, int level)
|
|
||||||
{
|
|
||||||
HashMap *block = HashMapCreate();
|
|
||||||
|
|
||||||
ConfigTokenSeek(state);
|
|
||||||
|
|
||||||
while (ConfigExpect(state, TOKEN_NAME))
|
|
||||||
{
|
|
||||||
char *name = Malloc(state->tokenLen + 1);
|
|
||||||
|
|
||||||
strcpy(name, state->token);
|
|
||||||
|
|
||||||
ConfigTokenSeek(state);
|
|
||||||
if (ConfigExpect(state, TOKEN_VALUE) || ConfigExpect(state, TOKEN_MACRO))
|
|
||||||
{
|
|
||||||
ConfigDirective *directive;
|
|
||||||
|
|
||||||
directive = Malloc(sizeof(ConfigDirective));
|
|
||||||
directive->children = NULL;
|
|
||||||
directive->values = ArrayCreate();
|
|
||||||
|
|
||||||
while (ConfigExpect(state, TOKEN_VALUE) ||
|
|
||||||
ConfigExpect(state, TOKEN_MACRO))
|
|
||||||
{
|
|
||||||
|
|
||||||
char *dval;
|
|
||||||
char *dvalCpy;
|
|
||||||
|
|
||||||
if (ConfigExpect(state, TOKEN_VALUE))
|
|
||||||
{
|
|
||||||
dval = state->token;
|
|
||||||
}
|
|
||||||
else if (ConfigExpect(state, TOKEN_MACRO))
|
|
||||||
{
|
|
||||||
dval = HashMapGet(state->macroMap, state->token);
|
|
||||||
if (!dval)
|
|
||||||
{
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dval = NULL; /* Should never happen */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* dval is a pointer which is overwritten with the next
|
|
||||||
* token. */
|
|
||||||
dvalCpy = Malloc(strlen(dval) + 1);
|
|
||||||
strcpy(dvalCpy, dval);
|
|
||||||
|
|
||||||
ArrayAdd(directive->values, dvalCpy);
|
|
||||||
ConfigTokenSeek(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ConfigExpect(state, TOKEN_BLOCK_OPEN))
|
|
||||||
{
|
|
||||||
/* token_seek(state); */
|
|
||||||
directive->children = ConfigParseBlock(state, level + 1);
|
|
||||||
if (!directive->children)
|
|
||||||
{
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Append this directive to the current block,
|
|
||||||
* overwriting a directive at this level with the same name.
|
|
||||||
*
|
|
||||||
* Note that if a value already exists with this name, it is
|
|
||||||
* returned by HashMapSet() and then immediately passed to
|
|
||||||
* ConfigDirectiveFree(). If the value does not exist, then
|
|
||||||
* NULL is sent to ConfigDirectiveFree(), making it a no-op.
|
|
||||||
*/
|
|
||||||
ConfigDirectiveFree(HashMapSet(block, name, directive));
|
|
||||||
}
|
|
||||||
else if (ConfigExpect(state, TOKEN_MACRO_ASSIGNMENT))
|
|
||||||
{
|
|
||||||
ConfigTokenSeek(state);
|
|
||||||
if (ConfigExpect(state, TOKEN_VALUE))
|
|
||||||
{
|
|
||||||
char *valueCopy = Malloc(strlen(state->token) + 1);
|
|
||||||
|
|
||||||
strcpy(valueCopy, state->token);
|
|
||||||
Free(HashMapSet(state->macroMap, name, valueCopy));
|
|
||||||
ConfigTokenSeek(state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ConfigExpect(state, TOKEN_SEMICOLON))
|
|
||||||
{
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
ConfigTokenSeek(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ConfigExpect(state, level ? TOKEN_BLOCK_CLOSE : TOKEN_EOF))
|
|
||||||
{
|
|
||||||
ConfigTokenSeek(state);
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
error:
|
|
||||||
/* Only free the very top level, because this will recurse */
|
|
||||||
if (!level)
|
|
||||||
{
|
|
||||||
ConfigFree(block);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigParseResult *
|
|
||||||
ConfigParse(FILE * stream)
|
|
||||||
{
|
|
||||||
ConfigParseResult *result;
|
|
||||||
HashMap *conf;
|
|
||||||
ConfigParserState *state;
|
|
||||||
|
|
||||||
result = Malloc(sizeof(ConfigParseResult));
|
|
||||||
state = ConfigParserStateCreate(stream);
|
|
||||||
conf = ConfigParseBlock(state, 0);
|
|
||||||
|
|
||||||
if (!conf)
|
|
||||||
{
|
|
||||||
result->ok = 0;
|
|
||||||
result->data.lineNumber = state->line;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result->ok = 1;
|
|
||||||
result->data.confMap = conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigParserStateFree(state);
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -35,7 +35,7 @@
|
||||||
#include <TelodendriaConfig.h>
|
#include <TelodendriaConfig.h>
|
||||||
#include <Log.h>
|
#include <Log.h>
|
||||||
#include <HashMap.h>
|
#include <HashMap.h>
|
||||||
#include <Config.h>
|
#include <Json.h>
|
||||||
#include <HttpServer.h>
|
#include <HttpServer.h>
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
#include <Db.h>
|
#include <Db.h>
|
||||||
|
@ -136,7 +136,6 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
/* Config file */
|
/* Config file */
|
||||||
FILE *configFile = NULL;
|
FILE *configFile = NULL;
|
||||||
ConfigParseResult *configParseResult = NULL;
|
|
||||||
HashMap *config = NULL;
|
HashMap *config = NULL;
|
||||||
|
|
||||||
/* Program configuration */
|
/* Program configuration */
|
||||||
|
@ -237,29 +236,25 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
Log(lc, LOG_NOTICE, "Processing configuration file '%s'.", configArg);
|
Log(lc, LOG_NOTICE, "Processing configuration file '%s'.", configArg);
|
||||||
|
|
||||||
configParseResult = ConfigParse(configFile);
|
config = JsonDecode(configFile);
|
||||||
if (!ConfigParseResultOk(configParseResult))
|
fclose(configFile);
|
||||||
|
|
||||||
|
if (!config)
|
||||||
{
|
{
|
||||||
Log(lc, LOG_ERR, "Syntax error on line %d.",
|
Log(lc, LOG_ERR, "Syntax error in configuration file.");
|
||||||
ConfigParseResultLineNumber(configParseResult));
|
|
||||||
exit = EXIT_FAILURE;
|
exit = EXIT_FAILURE;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
config = ConfigParseResultGet(configParseResult);
|
|
||||||
ConfigParseResultFree(configParseResult);
|
|
||||||
|
|
||||||
fclose(configFile);
|
|
||||||
|
|
||||||
tConfig = TelodendriaConfigParse(config, lc);
|
tConfig = TelodendriaConfigParse(config, lc);
|
||||||
|
JsonFree(config);
|
||||||
|
|
||||||
if (!tConfig)
|
if (!tConfig)
|
||||||
{
|
{
|
||||||
exit = EXIT_FAILURE;
|
exit = EXIT_FAILURE;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigFree(config);
|
|
||||||
|
|
||||||
if (flags & ARG_CONFIGTEST)
|
if (flags & ARG_CONFIGTEST)
|
||||||
{
|
{
|
||||||
Log(lc, LOG_INFO, "Configuration is OK.");
|
Log(lc, LOG_INFO, "Configuration is OK.");
|
||||||
|
@ -277,7 +272,10 @@ main(int argc, char **argv)
|
||||||
unveil(NULL, NULL); /* Done with unveil(), so disable it */
|
unveil(NULL, NULL); /* Done with unveil(), so disable it */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!tConfig->logTimestamp || strcmp(tConfig->logTimestamp, "default") != 0)
|
||||||
|
{
|
||||||
LogConfigTimeStampFormatSet(lc, tConfig->logTimestamp);
|
LogConfigTimeStampFormatSet(lc, tConfig->logTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
if (tConfig->flags & TELODENDRIA_LOG_COLOR)
|
if (tConfig->flags & TELODENDRIA_LOG_COLOR)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,61 +23,179 @@
|
||||||
*/
|
*/
|
||||||
#include <TelodendriaConfig.h>
|
#include <TelodendriaConfig.h>
|
||||||
#include <Memory.h>
|
#include <Memory.h>
|
||||||
#include <Config.h>
|
#include <Json.h>
|
||||||
#include <HashMap.h>
|
#include <HashMap.h>
|
||||||
#include <Log.h>
|
#include <Log.h>
|
||||||
#include <Array.h>
|
#include <Array.h>
|
||||||
#include <Util.h>
|
#include <Util.h>
|
||||||
|
#include <Db.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static int
|
#define CONFIG_REQUIRE(key, type) \
|
||||||
IsInteger(char *str)
|
value = HashMapGet(config, key); \
|
||||||
{
|
if (!value) \
|
||||||
while (*str)
|
{ \
|
||||||
{
|
Log(lc, LOG_ERR, "Missing required " key " directive."); \
|
||||||
if (!isdigit((unsigned char) *str))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
str++;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GET_DIRECTIVE(name) \
|
|
||||||
directive = (ConfigDirective *) HashMapGet(config, name); \
|
|
||||||
if (!directive) { \
|
|
||||||
Log(lc, LOG_ERR, "Missing required configuration directive: '%s'.", name); \
|
|
||||||
goto error; \
|
goto error; \
|
||||||
} \
|
} \
|
||||||
children = ConfigChildrenGet(directive); \
|
if (JsonValueType(value) == JSON_NULL) \
|
||||||
value = ConfigValuesGet(directive); \
|
{ \
|
||||||
|
Log(lc, LOG_ERR, "Missing value for " key " directive."); \
|
||||||
#define ASSERT_NO_CHILDREN(name) if (children) { \
|
|
||||||
Log(lc, LOG_ERR, "Unexpected child values in directive: '%s'.", name); \
|
|
||||||
goto error; \
|
goto error; \
|
||||||
|
} \
|
||||||
|
if (JsonValueType(value) != type) \
|
||||||
|
{ \
|
||||||
|
Log(lc, LOG_ERR, "Expected " key " to be of type " #type); \
|
||||||
|
goto error; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CONFIG_COPY_STRING(into) \
|
||||||
|
into = UtilStringDuplicate(JsonValueAsString(value));
|
||||||
|
|
||||||
|
#define CONFIG_OPTIONAL_STRING(into, key, default) \
|
||||||
|
value = HashMapGet(config, key); \
|
||||||
|
if (value && JsonValueType(value) != JSON_NULL) \
|
||||||
|
{ \
|
||||||
|
if (JsonValueType(value) != JSON_STRING) \
|
||||||
|
{ \
|
||||||
|
Log(lc, LOG_ERR, "Expected " key " to be of type JSON_STRING"); \
|
||||||
|
goto error; \
|
||||||
|
} \
|
||||||
|
into = UtilStringDuplicate(JsonValueAsString(value)); \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
Log(lc, LOG_INFO, "Using default value " #default " for " key "."); \
|
||||||
|
into = default ? UtilStringDuplicate(default) : NULL; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CONFIG_OPTIONAL_INTEGER(into, key, default) \
|
||||||
|
value = HashMapGet(config, key); \
|
||||||
|
if (value && JsonValueType(value) != JSON_NULL) \
|
||||||
|
{ \
|
||||||
|
if (JsonValueType(value) != JSON_INTEGER) \
|
||||||
|
{ \
|
||||||
|
Log(lc, LOG_ERR, "Expected " key " to be of type JSON_INTEGER"); \
|
||||||
|
goto error; \
|
||||||
|
} \
|
||||||
|
into = JsonValueAsInteger(value); \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
Log(lc, LOG_INFO, "Using default value " #default " for " key "."); \
|
||||||
|
into = default; \
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ConfigParseRunAs(LogConfig * lc, TelodendriaConfig * tConfig, HashMap * config)
|
||||||
|
{
|
||||||
|
JsonValue *value;
|
||||||
|
|
||||||
|
CONFIG_REQUIRE("uid", JSON_STRING);
|
||||||
|
CONFIG_COPY_STRING(tConfig->uid);
|
||||||
|
|
||||||
|
CONFIG_OPTIONAL_STRING(tConfig->gid, "gid", tConfig->uid);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
error:
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ASSERT_VALUES(name, expected) if (ArraySize(value) != expected) { \
|
int
|
||||||
Log(lc, LOG_ERR, \
|
ConfigParseLog(LogConfig * lc, TelodendriaConfig * tConfig, HashMap * config)
|
||||||
"Wrong value count in directive '%s': got '%d', but expected '%d'.", \
|
{
|
||||||
name, ArraySize(value), expected); \
|
JsonValue *value;
|
||||||
goto error; \
|
char *str;
|
||||||
}
|
|
||||||
|
|
||||||
#define COPY_VALUE(into, index) into = UtilStringDuplicate(ArrayGet(value, index))
|
CONFIG_REQUIRE("output", JSON_STRING);
|
||||||
|
str = JsonValueAsString(value);
|
||||||
|
|
||||||
|
if (strcmp(str, "stdout") == 0)
|
||||||
|
{
|
||||||
|
tConfig->flags |= TELODENDRIA_LOG_STDOUT;
|
||||||
|
}
|
||||||
|
else if (strcmp(str, "file") == 0)
|
||||||
|
{
|
||||||
|
tConfig->flags |= TELODENDRIA_LOG_FILE;
|
||||||
|
}
|
||||||
|
else if (strcmp(str, "syslog") == 0)
|
||||||
|
{
|
||||||
|
tConfig->flags |= TELODENDRIA_LOG_SYSLOG;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(lc, LOG_ERR, "Invalid value for log.output: '%s'.", str);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG_OPTIONAL_STRING(str, "level", "message");
|
||||||
|
|
||||||
|
if (strcmp(str, "message") == 0)
|
||||||
|
{
|
||||||
|
tConfig->logLevel = LOG_INFO;
|
||||||
|
}
|
||||||
|
else if (strcmp(str, "debug") == 0)
|
||||||
|
{
|
||||||
|
tConfig->logLevel = LOG_DEBUG;
|
||||||
|
}
|
||||||
|
else if (strcmp(str, "notice") == 0)
|
||||||
|
{
|
||||||
|
tConfig->logLevel = LOG_NOTICE;
|
||||||
|
}
|
||||||
|
else if (strcmp(str, "warning") == 0)
|
||||||
|
{
|
||||||
|
tConfig->logLevel = LOG_WARNING;
|
||||||
|
}
|
||||||
|
else if (strcmp(str, "error") == 0)
|
||||||
|
{
|
||||||
|
tConfig->logLevel = LOG_ERR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(lc, LOG_ERR, "Invalid value for log.level: '%s'.", tConfig->logLevel);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(str);
|
||||||
|
|
||||||
|
CONFIG_OPTIONAL_STRING(tConfig->logTimestamp, "timestampFormat", "default");
|
||||||
|
|
||||||
|
if (strcmp(tConfig->logTimestamp, "none") == 0)
|
||||||
|
{
|
||||||
|
Free(tConfig->logTimestamp);
|
||||||
|
tConfig->logTimestamp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = HashMapGet(config, "color");
|
||||||
|
if (value && JsonValueType(value) != JSON_NULL)
|
||||||
|
{
|
||||||
|
if (JsonValueType(value) != JSON_BOOLEAN)
|
||||||
|
{
|
||||||
|
Log(lc, LOG_ERR, "Expected type JSON_BOOLEAN for log.color.");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JsonValueAsBoolean(value))
|
||||||
|
{
|
||||||
|
tConfig->flags |= TELODENDRIA_LOG_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
error:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
TelodendriaConfig *
|
TelodendriaConfig *
|
||||||
TelodendriaConfigParse(HashMap * config, LogConfig * lc)
|
TelodendriaConfigParse(HashMap * config, LogConfig * lc)
|
||||||
{
|
{
|
||||||
TelodendriaConfig *tConfig;
|
TelodendriaConfig *tConfig;
|
||||||
|
JsonValue *value;
|
||||||
ConfigDirective *directive;
|
|
||||||
Array *value;
|
|
||||||
HashMap *children;
|
|
||||||
|
|
||||||
if (!config || !lc)
|
if (!config || !lc)
|
||||||
{
|
{
|
||||||
|
@ -92,41 +210,15 @@ TelodendriaConfigParse(HashMap * config, LogConfig * lc)
|
||||||
|
|
||||||
memset(tConfig, 0, sizeof(TelodendriaConfig));
|
memset(tConfig, 0, sizeof(TelodendriaConfig));
|
||||||
|
|
||||||
directive = (ConfigDirective *) HashMapGet(config, "listen");
|
CONFIG_OPTIONAL_INTEGER(tConfig->listenPort, "listen", 8008);
|
||||||
children = ConfigChildrenGet(directive);
|
|
||||||
value = ConfigValuesGet(directive);
|
|
||||||
|
|
||||||
if (!directive)
|
CONFIG_REQUIRE("serverName", JSON_STRING);
|
||||||
|
CONFIG_COPY_STRING(tConfig->serverName);
|
||||||
|
|
||||||
|
value = HashMapGet(config, "baseUrl");
|
||||||
|
if (value)
|
||||||
{
|
{
|
||||||
tConfig->listenPort = 8008;
|
CONFIG_COPY_STRING(tConfig->baseUrl);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ASSERT_NO_CHILDREN("listen");
|
|
||||||
ASSERT_VALUES("listen", 1);
|
|
||||||
|
|
||||||
tConfig->listenPort = (unsigned short) atoi(ArrayGet(value, 0));
|
|
||||||
if (!tConfig->listenPort)
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR, "Expected numeric value for listen port, got '%s'.", ArrayGet(value, 1));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GET_DIRECTIVE("server-name");
|
|
||||||
ASSERT_NO_CHILDREN("server-name");
|
|
||||||
ASSERT_VALUES("server-name", 1);
|
|
||||||
COPY_VALUE(tConfig->serverName, 0);
|
|
||||||
|
|
||||||
directive = (ConfigDirective *) HashMapGet(config, "base-url");
|
|
||||||
children = ConfigChildrenGet(directive);
|
|
||||||
value = ConfigValuesGet(directive);
|
|
||||||
|
|
||||||
if (directive)
|
|
||||||
{
|
|
||||||
ASSERT_NO_CHILDREN("base-url");
|
|
||||||
ASSERT_VALUES("base-url", 1);
|
|
||||||
COPY_VALUE(tConfig->baseUrl, 0);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -134,273 +226,65 @@ TelodendriaConfigParse(HashMap * config, LogConfig * lc)
|
||||||
tConfig->baseUrl = Malloc(strlen(tConfig->serverName) + 10);
|
tConfig->baseUrl = Malloc(strlen(tConfig->serverName) + 10);
|
||||||
if (!tConfig->baseUrl)
|
if (!tConfig->baseUrl)
|
||||||
{
|
{
|
||||||
Log(lc, LOG_ERR, "Error allocating memory for default config value 'base-url'.");
|
Log(lc, LOG_ERR, "Error allocating memory for default config value 'baseUrl'.");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(tConfig->baseUrl, "https://%s", tConfig->serverName);
|
sprintf(tConfig->baseUrl, "https://%s", tConfig->serverName);
|
||||||
}
|
}
|
||||||
|
|
||||||
directive = (ConfigDirective *) HashMapGet(config, "identity-server");
|
CONFIG_OPTIONAL_STRING(tConfig->identityServer, "identityServer", NULL);
|
||||||
children = ConfigChildrenGet(directive);
|
|
||||||
value = ConfigValuesGet(directive);
|
|
||||||
|
|
||||||
if (directive)
|
value = HashMapGet(config, "runAs");
|
||||||
|
if (value && JsonValueType(value) != JSON_NULL)
|
||||||
{
|
{
|
||||||
ASSERT_NO_CHILDREN("identity-server");
|
if (JsonValueType(value) == JSON_OBJECT)
|
||||||
ASSERT_VALUES("identity-server", 1);
|
|
||||||
COPY_VALUE(tConfig->identityServer, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
Log(lc, LOG_WARNING, "Identity server not specified. No identity server will be advertised.");
|
if (!ConfigParseRunAs(lc, tConfig, JsonValueAsObject(value)))
|
||||||
tConfig->identityServer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
directive = (ConfigDirective *) HashMapGet(config, "id");
|
|
||||||
children = ConfigChildrenGet(directive);
|
|
||||||
value = ConfigValuesGet(directive);
|
|
||||||
|
|
||||||
ASSERT_NO_CHILDREN("id");
|
|
||||||
|
|
||||||
if (directive)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
switch (ArraySize(value))
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
Log(lc, LOG_WARNING, "No run group specified; assuming it's the same as the user.");
|
|
||||||
COPY_VALUE(tConfig->uid, 0);
|
|
||||||
tConfig->gid = UtilStringDuplicate(tConfig->uid);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
COPY_VALUE(tConfig->uid, 0);
|
|
||||||
COPY_VALUE(tConfig->gid, 1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log(lc, LOG_ERR,
|
|
||||||
"Wrong value count in directive 'id': got '%d', but expected 1 or 2.",
|
|
||||||
ArraySize(value));
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tConfig->uid = NULL;
|
Log(lc, LOG_ERR, "Config directive 'runAs' should be a JSON object");
|
||||||
tConfig->gid = NULL;
|
Log(lc, LOG_ERR, "that contains a 'uid' and 'gid'.");
|
||||||
}
|
|
||||||
|
|
||||||
GET_DIRECTIVE("data-dir");
|
|
||||||
ASSERT_NO_CHILDREN("data-dir");
|
|
||||||
ASSERT_VALUES("data-dir", 1);
|
|
||||||
COPY_VALUE(tConfig->dataDir, 0);
|
|
||||||
|
|
||||||
GET_DIRECTIVE("threads");
|
|
||||||
ASSERT_NO_CHILDREN("threads");
|
|
||||||
ASSERT_VALUES("threads", 1);
|
|
||||||
|
|
||||||
if (IsInteger(ArrayGet(value, 0)))
|
|
||||||
{
|
|
||||||
tConfig->threads = atoi(ArrayGet(value, 0));
|
|
||||||
if (!tConfig->threads)
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR, "threads must be greater than zero");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR,
|
|
||||||
"Expected integer for directive 'threads', "
|
|
||||||
"but got '%s'.", ArrayGet(value, 0));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
directive = (ConfigDirective *) HashMapGet(config, "max-connections");
|
|
||||||
if (!directive)
|
|
||||||
{
|
|
||||||
Log(lc, LOG_WARNING, "max-connections not specified; using defaults, which may change");
|
|
||||||
tConfig->maxConnections = 32;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ASSERT_NO_CHILDREN("max-connections");
|
|
||||||
ASSERT_VALUES("max-connections", 1);
|
|
||||||
if (IsInteger(ArrayGet(value, 0)))
|
|
||||||
{
|
|
||||||
tConfig->maxConnections = atoi(ArrayGet(value, 0));
|
|
||||||
if (!tConfig->maxConnections)
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR, "max-connections must be greater than zero.");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR, "Expected integer for max-connections, got '%s'", ArrayGet(value, 0));
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_DIRECTIVE("max-cache");
|
CONFIG_REQUIRE("dataDir", JSON_STRING);
|
||||||
ASSERT_NO_CHILDREN("max-cache");
|
CONFIG_COPY_STRING(tConfig->dataDir);
|
||||||
ASSERT_VALUES("max-cache", 1);
|
|
||||||
tConfig->maxCache = UtilParseBytes(ArrayGet(value, 0));
|
|
||||||
|
|
||||||
GET_DIRECTIVE("federation");
|
CONFIG_OPTIONAL_INTEGER(tConfig->threads, "threads", 1);
|
||||||
ASSERT_NO_CHILDREN("federation");
|
CONFIG_OPTIONAL_INTEGER(tConfig->maxConnections, "maxConnections", 32);
|
||||||
ASSERT_VALUES("federation", 1);
|
CONFIG_OPTIONAL_INTEGER(tConfig->maxCache, "maxCache", DB_MIN_CACHE);
|
||||||
|
|
||||||
if (strcmp(ArrayGet(value, 0), "true") == 0)
|
CONFIG_REQUIRE("federation", JSON_BOOLEAN);
|
||||||
|
if (JsonValueAsBoolean(value))
|
||||||
{
|
{
|
||||||
tConfig->flags |= TELODENDRIA_FEDERATION;
|
tConfig->flags |= TELODENDRIA_FEDERATION;
|
||||||
}
|
}
|
||||||
else if (strcmp(ArrayGet(value, 0), "false") != 0)
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR,
|
|
||||||
"Expected boolean value for directive 'federation', "
|
|
||||||
"but got '%s'.", ArrayGet(value, 0));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
GET_DIRECTIVE("registration");
|
CONFIG_REQUIRE("registration", JSON_BOOLEAN);
|
||||||
ASSERT_NO_CHILDREN("registration");
|
if (JsonValueAsBoolean(value))
|
||||||
ASSERT_VALUES("registration", 1);
|
|
||||||
if (strcmp(ArrayGet(value, 0), "true") == 0)
|
|
||||||
{
|
{
|
||||||
tConfig->flags |= TELODENDRIA_REGISTRATION;
|
tConfig->flags |= TELODENDRIA_REGISTRATION;
|
||||||
}
|
}
|
||||||
else if (strcmp(ArrayGet(value, 0), "false") != 0)
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR,
|
|
||||||
"Expected boolean value for directive 'registration', "
|
|
||||||
"but got '%s'.", ArrayGet(value, 0));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
GET_DIRECTIVE("log");
|
CONFIG_REQUIRE("log", JSON_OBJECT);
|
||||||
ASSERT_VALUES("log", 1);
|
if (!ConfigParseLog(lc, tConfig, JsonValueAsObject(value)))
|
||||||
|
|
||||||
if (children)
|
|
||||||
{
|
{
|
||||||
ConfigDirective *cDirective;
|
|
||||||
char *cVal;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
cDirective = HashMapGet(children, "level");
|
|
||||||
if (cDirective)
|
|
||||||
{
|
|
||||||
size = ArraySize(ConfigValuesGet(cDirective));
|
|
||||||
if (size > 1)
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR, "Expected 1 value for log.level, got %d.", size);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
cVal = ArrayGet(ConfigValuesGet(cDirective), 0);
|
|
||||||
if (strcmp(cVal, "message") == 0)
|
|
||||||
{
|
|
||||||
tConfig->logLevel = LOG_INFO;
|
|
||||||
}
|
|
||||||
else if (strcmp(cVal, "debug") == 0)
|
|
||||||
{
|
|
||||||
tConfig->logLevel = LOG_DEBUG;
|
|
||||||
}
|
|
||||||
else if (strcmp(cVal, "task") == 0)
|
|
||||||
{
|
|
||||||
tConfig->logLevel = LOG_NOTICE;
|
|
||||||
}
|
|
||||||
else if (strcmp(cVal, "warning") == 0)
|
|
||||||
{
|
|
||||||
tConfig->logLevel = LOG_WARNING;
|
|
||||||
}
|
|
||||||
else if (strcmp(cVal, "error") == 0)
|
|
||||||
{
|
|
||||||
tConfig->logLevel = LOG_ERR;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR, "Invalid value for log.level: '%s'.", cVal);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cDirective = HashMapGet(children, "timestampFormat");
|
|
||||||
if (cDirective)
|
|
||||||
{
|
|
||||||
size = ArraySize(ConfigValuesGet(cDirective));
|
|
||||||
if (size > 1)
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR, "Expected 1 value for log.level, got %d.", size);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
cVal = ArrayGet(ConfigValuesGet(cDirective), 0);
|
|
||||||
|
|
||||||
if (strcmp(cVal, "none") == 0)
|
|
||||||
{
|
|
||||||
tConfig->logTimestamp = NULL;
|
|
||||||
}
|
|
||||||
else if (strcmp(cVal, "default") != 0)
|
|
||||||
{
|
|
||||||
tConfig->logTimestamp = UtilStringDuplicate(cVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cDirective = HashMapGet(children, "color");
|
|
||||||
if (cDirective)
|
|
||||||
{
|
|
||||||
size = ArraySize(ConfigValuesGet(cDirective));
|
|
||||||
if (size > 1)
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR, "Expected 1 value for log.level, got %d.", size);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
cVal = ArrayGet(ConfigValuesGet(cDirective), 0);
|
|
||||||
|
|
||||||
if (strcmp(cVal, "true") == 0)
|
|
||||||
{
|
|
||||||
tConfig->flags |= TELODENDRIA_LOG_COLOR;
|
|
||||||
}
|
|
||||||
else if (strcmp(cVal, "false") != 0)
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR, "Expected boolean value for log.color, got '%s'.", cVal);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the actual log output file last */
|
|
||||||
if (strcmp(ArrayGet(value, 0), "stdout") == 0)
|
|
||||||
{
|
|
||||||
tConfig->flags |= TELODENDRIA_LOG_STDOUT;
|
|
||||||
}
|
|
||||||
else if (strcmp(ArrayGet(value, 0), "file") == 0)
|
|
||||||
{
|
|
||||||
tConfig->flags |= TELODENDRIA_LOG_FILE;
|
|
||||||
}
|
|
||||||
else if (strcmp(ArrayGet(value, 0), "syslog") == 0)
|
|
||||||
{
|
|
||||||
tConfig->flags |= TELODENDRIA_LOG_SYSLOG;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log(lc, LOG_ERR, "Unknown log value '%s', expected 'stdout', 'file', or 'syslog'.",
|
|
||||||
ArrayGet(value, 0));
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tConfig;
|
return tConfig;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
TelodendriaConfigFree(tConfig);
|
TelodendriaConfigFree(tConfig);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef GET_DIRECTIVE
|
|
||||||
#undef ASSERT_NO_CHILDREN
|
|
||||||
#undef ASSERT_VALUES
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TelodendriaConfigFree(TelodendriaConfig * tConfig)
|
TelodendriaConfigFree(TelodendriaConfig * tConfig)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,208 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person
|
|
||||||
* obtaining a copy of this software and associated documentation files
|
|
||||||
* (the "Software"), to deal in the Software without restriction,
|
|
||||||
* including without limitation the rights to use, copy, modify, merge,
|
|
||||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
||||||
* and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
||||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Config.h: A heavily-modified version of Conifer2, a configuration
|
|
||||||
* file format specification and C parsing library written by Jordan
|
|
||||||
* Bancino. This library differs from Conifer2 in that the function
|
|
||||||
* naming convention has been updated to be consistent with Telodendria,
|
|
||||||
* and the underlying data structures have been overhauled to use the
|
|
||||||
* data structure libraries provided by Telodendria.
|
|
||||||
*
|
|
||||||
* Conifer2 was originally a learning project. It was very thoroughly
|
|
||||||
* debugged, however, and the configuration syntax was elegant,
|
|
||||||
* certainly more elegant than using JSON for a configuration file,
|
|
||||||
* so it was chosen to be the format for Telodendria's configuration
|
|
||||||
* file. The original Conifer2 project is now dead; Conifer2 lives on
|
|
||||||
* only as Telodendria's Config parsing library.
|
|
||||||
*/
|
|
||||||
#ifndef TELODENDRIA_CONFIG_H
|
|
||||||
#define TELODENDRIA_CONFIG_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include <HashMap.h>
|
|
||||||
#include <Array.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A configuration directive is a single key that may have at least one
|
|
||||||
* value, and any number of children.
|
|
||||||
*/
|
|
||||||
typedef struct ConfigDirective ConfigDirective;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The parser returns a parse result object. This stores whether or
|
|
||||||
* not the parse was successful, and then also additional information
|
|
||||||
* about the parse, such as the line number on which parsing failed,
|
|
||||||
* or the collection of directives if the parsing succeeded.
|
|
||||||
*
|
|
||||||
* There are a number of ConfigParseResult methods that can be used
|
|
||||||
* to query the result of parsing.
|
|
||||||
*/
|
|
||||||
typedef struct ConfigParseResult ConfigParseResult;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse a configuration file, and generate the structures needed to
|
|
||||||
* make it easy to read.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
*
|
|
||||||
* (FILE *) The input stream to read from.
|
|
||||||
*
|
|
||||||
* Return: A ConfigParseResult, which can be used to check whether or
|
|
||||||
* not the parsing was successful. If the parsing was sucessful, then
|
|
||||||
* this object contains the root directive, which can be used to
|
|
||||||
* retrieve configuration values out of. If the parsing failed, then
|
|
||||||
* this object contains the line number at which the parsing was
|
|
||||||
* aborted.
|
|
||||||
*/
|
|
||||||
extern ConfigParseResult *
|
|
||||||
ConfigParse(FILE *);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get whether or not a parse result indicates that parsing was
|
|
||||||
* successful or not. This function should be used to determine what
|
|
||||||
* to do next. If the parsing failed, your program should terminate
|
|
||||||
* with an error, otherwise, you can proceed to parse the configuration
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
*
|
|
||||||
* (ConfigParseResult *) The output of ConfigParse() to check.
|
|
||||||
*
|
|
||||||
* Return: 0 if the configuration file is malformed, or otherwise
|
|
||||||
* could not be parsed. Any non-zero return value indicates that the
|
|
||||||
* configuration file was successfully parsed.
|
|
||||||
*/
|
|
||||||
extern unsigned int
|
|
||||||
ConfigParseResultOk(ConfigParseResult *);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If, and only if, the configuration file parsing failed, then this
|
|
||||||
* function can be used to get the line number it failed at. Typically,
|
|
||||||
* this will be reported to the user and then the program will be
|
|
||||||
* terminated.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
*
|
|
||||||
* (ConfigParseResult *) The output of ConfigParse() to get the
|
|
||||||
* line number from.
|
|
||||||
*
|
|
||||||
* Return: The line number on which the configuration file parser
|
|
||||||
* choked, or 0 if the parsing was actually successful.
|
|
||||||
*/
|
|
||||||
extern size_t
|
|
||||||
ConfigParseResultLineNumber(ConfigParseResult *);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert a ConfigParseResult into a HashMap containing the entire
|
|
||||||
* configuration file, if, and only if, the parsing was successful.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
*
|
|
||||||
* (ConfigParseResult *) The output of ConfigParse() to get the
|
|
||||||
* actual configuration data from.
|
|
||||||
*
|
|
||||||
* Return: A HashMap containing all the configuration data, or NULL
|
|
||||||
* if the parsing was not successful. This HashMap is a map of string
|
|
||||||
* keys to ConfigDirective objects. Use the standard HashMap methods
|
|
||||||
* to get ConfigDirectives, and then use the ConfigDirective functions
|
|
||||||
* to get information out of them.
|
|
||||||
*/
|
|
||||||
extern HashMap *
|
|
||||||
ConfigParseResultGet(ConfigParseResult *);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Free the memory being used by the given ConfigParseResult. Note that
|
|
||||||
* it is safe to free the ConfigParseResult immediately after you have
|
|
||||||
* retrieved either the line number or the configuration data from it.
|
|
||||||
* Freeing the parse result does not free the configuration data.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
*
|
|
||||||
* (ConfigParseResult *) The output of ConfigParse() to free. This
|
|
||||||
* object will be invalidated, but pointers to
|
|
||||||
* the actual configuration data will still be
|
|
||||||
* valid.
|
|
||||||
*/
|
|
||||||
extern void
|
|
||||||
ConfigParseResultFree(ConfigParseResult *);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get an array of values associated with the given configuration
|
|
||||||
* directive. Directives can have any number of values, which are
|
|
||||||
* made accessible via the Array API.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
*
|
|
||||||
* (ConfigDirective *) The configuration directive to get the values
|
|
||||||
* for.
|
|
||||||
*
|
|
||||||
* Return: An array that contains at least 1 value. Configuration files
|
|
||||||
* cannot have value-less directives. If the passed directive is NULL,
|
|
||||||
* or there is an error allocating memory for an array, then NULL is
|
|
||||||
* returned.
|
|
||||||
*/
|
|
||||||
extern Array *
|
|
||||||
ConfigValuesGet(ConfigDirective *);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get a map of children associated with the given configuration
|
|
||||||
* directive. Configuration files can recurse with no practical limit,
|
|
||||||
* so directives can have any number of child directives.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
*
|
|
||||||
* (ConfigDirective *) The configuratio ndirective to get the
|
|
||||||
* children of.
|
|
||||||
*
|
|
||||||
* Return: A HashMap containing child directives, or NULL if the passed
|
|
||||||
* directive is NULL or has no children.
|
|
||||||
*/
|
|
||||||
extern HashMap *
|
|
||||||
ConfigChildrenGet(ConfigDirective *);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Free all the memory associated with the given configuration hash
|
|
||||||
* map. Note: this will free *everything*. All Arrays, HashMaps,
|
|
||||||
* ConfigDirectives, and even strings will be invalidated. As such,
|
|
||||||
* this should be done after you either copy the values you want, or
|
|
||||||
* are done using them. It is highly recommended to use this function
|
|
||||||
* near the end of your program's execution during cleanup, otherwise
|
|
||||||
* copy any values you need into your own buffers.
|
|
||||||
*
|
|
||||||
* Note that this should only be run on the root configuration object,
|
|
||||||
* not any children. Running on children will produce undefined
|
|
||||||
* behavior. This function is recursive; it will get all the children
|
|
||||||
* under it.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
*
|
|
||||||
* (HashMap *) The configuration data to free.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
extern void
|
|
||||||
ConfigFree(HashMap *);
|
|
||||||
|
|
||||||
#endif /* TELODENDRIA_CONFIG_H */
|
|
Loading…
Reference in a new issue