forked from Telodendria/Telodendria
Compare commits
No commits in common. "fbc4486930111e6de80cc9dc66efea711f773253" and "18488d463e06f88c1c467b1ff71ed566168d3524" have entirely different histories.
fbc4486930
...
18488d463e
6 changed files with 481 additions and 177 deletions
|
@ -1,93 +0,0 @@
|
||||||
{
|
|
||||||
"guard": "TELODENDRIA_SCHEMA_CONFIG_H",
|
|
||||||
"header": "Schema\/Config.h",
|
|
||||||
"include": [ "Cytoplasm\/Db.h", "Cytoplasm/HttpServer.h" ],
|
|
||||||
|
|
||||||
"types": {
|
|
||||||
"ConfigTls": {
|
|
||||||
"fields": {
|
|
||||||
"cert": { "type": "string" },
|
|
||||||
"key": { "type": "string" }
|
|
||||||
},
|
|
||||||
"type": "struct"
|
|
||||||
},
|
|
||||||
|
|
||||||
"HttpHandler *": { "type": "extern" },
|
|
||||||
"void *": { "type": "extern" },
|
|
||||||
|
|
||||||
"ConfigListener": {
|
|
||||||
"fields": {
|
|
||||||
"handler": { "type": "HttpHandler *", "ignore": true },
|
|
||||||
"handlerArgs": { "type": "void *", "ignore": true },
|
|
||||||
|
|
||||||
"port": { "type": "integer", "required": true },
|
|
||||||
"threads": { "type": "integer", "required": false },
|
|
||||||
"maxConnections": { "type": "integer", "required": false },
|
|
||||||
"tls": { "type": "ConfigTls", "required": false }
|
|
||||||
},
|
|
||||||
"type": "struct"
|
|
||||||
},
|
|
||||||
"ConfigRunAs": {
|
|
||||||
"fields": {
|
|
||||||
"uid": { "type": "string", "required": false },
|
|
||||||
"gid": { "type": "string", "required": true }
|
|
||||||
},
|
|
||||||
"type": "struct"
|
|
||||||
},
|
|
||||||
"ConfigLogOutput": {
|
|
||||||
"fields": {
|
|
||||||
"stdout": { "name": "CONFIG_LOG_OUTPUT_STDOUT" },
|
|
||||||
"file": { "name": "CONFIG_LOG_OUTPUT_FILE" },
|
|
||||||
"syslog": { "name": "CONFIG_LOG_OUTPUT_SYSLOG" }
|
|
||||||
},
|
|
||||||
"type": "enum"
|
|
||||||
},
|
|
||||||
"ConfigLogLevel": {
|
|
||||||
"fields": {
|
|
||||||
"message": { "name": "CONFIG_LOG_LEVEL_MESSAGE" },
|
|
||||||
"debug": { "name": "CONFIG_LOG_LEVEL_DEBUG" },
|
|
||||||
"notice": { "name": "CONFIG_LOG_LEVEL_NOTICE" },
|
|
||||||
"warning": { "name": "CONFIG_LOG_LEVEL_WARNING" },
|
|
||||||
"error": { "name": "CONFIG_LOG_LEVEL_ERROR" }
|
|
||||||
},
|
|
||||||
"type": "enum"
|
|
||||||
},
|
|
||||||
"ConfigLogConfig": {
|
|
||||||
"fields": {
|
|
||||||
"output": { "type": "ConfigLogOutput", "required": true },
|
|
||||||
"level": { "type": "ConfigLogLevel", "required": false },
|
|
||||||
"timestampFormat":{ "type": "string", "required": false },
|
|
||||||
"color": { "type": "boolean", "required": false }
|
|
||||||
},
|
|
||||||
"type": "struct"
|
|
||||||
},
|
|
||||||
|
|
||||||
"Db *": { "type": "extern" },
|
|
||||||
"DbRef *": { "type": "extern" },
|
|
||||||
"char *": { "type": "extern" },
|
|
||||||
|
|
||||||
"Config": {
|
|
||||||
"fields": {
|
|
||||||
"db": { "type": "Db *", "ignore": true },
|
|
||||||
"ref": { "type": "DbRef *", "ignore": true },
|
|
||||||
|
|
||||||
"ok": { "type": "boolean", "ignore": true },
|
|
||||||
"err": { "type": "char *", "ignore": true },
|
|
||||||
|
|
||||||
"listen": { "type": "[ConfigListener]", "required": true },
|
|
||||||
"runAs": { "type": "ConfigRunAs", "required": false },
|
|
||||||
"log": { "type": "ConfigLogConfig", "required": true },
|
|
||||||
|
|
||||||
"serverName": { "type": "string", "required": true },
|
|
||||||
"baseUrl": { "type": "string", "required": false },
|
|
||||||
"identityServer": { "type": "string", "required": false },
|
|
||||||
|
|
||||||
"maxCache": { "type": "integer", "required": false },
|
|
||||||
|
|
||||||
"federation": { "type": "boolean", "required": true },
|
|
||||||
"registration": { "type": "boolean", "required": true }
|
|
||||||
},
|
|
||||||
"type": "struct"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
417
src/Config.c
417
src/Config.c
|
@ -21,7 +21,7 @@
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include <Schema/Config.h>
|
#include <Config.h>
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
#include <Cytoplasm/HashMap.h>
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
@ -41,60 +41,378 @@
|
||||||
#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
|
#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define CONFIG_REQUIRE(key, type) \
|
||||||
|
value = HashMapGet(config, key); \
|
||||||
|
if (!value) \
|
||||||
|
{ \
|
||||||
|
tConfig->err = "Missing required " key " directive."; \
|
||||||
|
goto error; \
|
||||||
|
} \
|
||||||
|
if (JsonValueType(value) == JSON_NULL) \
|
||||||
|
{ \
|
||||||
|
tConfig->err = "Missing value for " key " directive."; \
|
||||||
|
goto error; \
|
||||||
|
} \
|
||||||
|
if (JsonValueType(value) != type) \
|
||||||
|
{ \
|
||||||
|
tConfig->err = "Expected " key " to be of type " #type; \
|
||||||
|
goto error; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CONFIG_COPY_STRING(into) \
|
||||||
|
into = StrDuplicate(JsonValueAsString(value));
|
||||||
|
|
||||||
|
#define CONFIG_OPTIONAL_STRING(into, key, default) \
|
||||||
|
value = HashMapGet(config, key); \
|
||||||
|
if (value && JsonValueType(value) != JSON_NULL) \
|
||||||
|
{ \
|
||||||
|
if (JsonValueType(value) != JSON_STRING) \
|
||||||
|
{ \
|
||||||
|
tConfig->err = "Expected " key " to be of type JSON_STRING"; \
|
||||||
|
goto error; \
|
||||||
|
} \
|
||||||
|
into = StrDuplicate(JsonValueAsString(value)); \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
into = default ? StrDuplicate(default) : NULL; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CONFIG_OPTIONAL_INTEGER(into, key, default) \
|
||||||
|
value = HashMapGet(config, key); \
|
||||||
|
if (value && JsonValueType(value) != JSON_NULL) \
|
||||||
|
{ \
|
||||||
|
if (JsonValueType(value) != JSON_INTEGER) \
|
||||||
|
{ \
|
||||||
|
tConfig->err = "Expected " key " to be of type JSON_INTEGER"; \
|
||||||
|
goto error; \
|
||||||
|
} \
|
||||||
|
into = Int64Low(JsonValueAsInteger(value)); \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
into = default; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ConfigParseRunAs(Config * 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ConfigParseListen(Config * tConfig, Array * listen)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (!ArraySize(listen))
|
||||||
|
{
|
||||||
|
tConfig->err = "Listen array cannot be empty; you must specify at least one listener.";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tConfig->servers)
|
||||||
|
{
|
||||||
|
tConfig->servers = ArrayCreate();
|
||||||
|
if (!tConfig->servers)
|
||||||
|
{
|
||||||
|
tConfig->err = "Unable to allocate memory for listener configurations.";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ArraySize(listen); i++)
|
||||||
|
{
|
||||||
|
JsonValue *val = ArrayGet(listen, i);
|
||||||
|
HashMap *obj;
|
||||||
|
HttpServerConfig *serverCfg = Malloc(sizeof(HttpServerConfig));
|
||||||
|
|
||||||
|
if (!serverCfg)
|
||||||
|
{
|
||||||
|
tConfig->err = "Unable to allocate memory for listener configuration.";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JsonValueType(val) != JSON_OBJECT)
|
||||||
|
{
|
||||||
|
tConfig->err = "Invalid value in listener array. All listeners must be objects.";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = JsonValueAsObject(val);
|
||||||
|
|
||||||
|
serverCfg->port = Int64Low(JsonValueAsInteger(HashMapGet(obj, "port")));
|
||||||
|
serverCfg->threads = Int64Low(JsonValueAsInteger(HashMapGet(obj, "threads")));
|
||||||
|
serverCfg->maxConnections = Int64Low(JsonValueAsInteger(HashMapGet(obj, "maxConnections")));
|
||||||
|
|
||||||
|
if (!serverCfg->port)
|
||||||
|
{
|
||||||
|
Free(serverCfg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!serverCfg->threads)
|
||||||
|
{
|
||||||
|
serverCfg->threads = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!serverCfg->maxConnections)
|
||||||
|
{
|
||||||
|
serverCfg->maxConnections = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = HashMapGet(obj, "tls");
|
||||||
|
if ((JsonValueType(val) == JSON_BOOLEAN && !JsonValueAsBoolean(val)) || JsonValueType(val) == JSON_NULL)
|
||||||
|
{
|
||||||
|
serverCfg->flags = HTTP_FLAG_NONE;
|
||||||
|
serverCfg->tlsCert = NULL;
|
||||||
|
serverCfg->tlsKey = NULL;
|
||||||
|
}
|
||||||
|
else if (JsonValueType(val) != JSON_OBJECT)
|
||||||
|
{
|
||||||
|
tConfig->err = "Invalid value for listener.tls. It must be an object.";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
serverCfg->flags = HTTP_FLAG_TLS;
|
||||||
|
|
||||||
|
obj = JsonValueAsObject(val);
|
||||||
|
serverCfg->tlsCert = StrDuplicate(JsonValueAsString(HashMapGet(obj, "cert")));
|
||||||
|
serverCfg->tlsKey = StrDuplicate(JsonValueAsString(HashMapGet(obj, "key")));
|
||||||
|
|
||||||
|
if (!serverCfg->tlsCert || !serverCfg->tlsKey)
|
||||||
|
{
|
||||||
|
tConfig->err = "TLS cert and key must both be valid file names.";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArrayAdd(tConfig->servers, serverCfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
error:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ConfigParseLog(Config * tConfig, HashMap * config)
|
||||||
|
{
|
||||||
|
JsonValue *value;
|
||||||
|
char *str;
|
||||||
|
|
||||||
|
CONFIG_REQUIRE("output", JSON_STRING);
|
||||||
|
str = JsonValueAsString(value);
|
||||||
|
|
||||||
|
if (StrEquals(str, "stdout"))
|
||||||
|
{
|
||||||
|
tConfig->flags |= CONFIG_LOG_STDOUT;
|
||||||
|
}
|
||||||
|
else if (StrEquals(str, "file"))
|
||||||
|
{
|
||||||
|
tConfig->flags |= CONFIG_LOG_FILE;
|
||||||
|
}
|
||||||
|
else if (StrEquals(str, "syslog"))
|
||||||
|
{
|
||||||
|
tConfig->flags |= CONFIG_LOG_SYSLOG;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tConfig->err = "Invalid value for log.output";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG_OPTIONAL_STRING(str, "level", "message");
|
||||||
|
|
||||||
|
if (StrEquals(str, "message"))
|
||||||
|
{
|
||||||
|
tConfig->logLevel = LOG_INFO;
|
||||||
|
}
|
||||||
|
else if (StrEquals(str, "debug"))
|
||||||
|
{
|
||||||
|
tConfig->logLevel = LOG_DEBUG;
|
||||||
|
}
|
||||||
|
else if (StrEquals(str, "notice"))
|
||||||
|
{
|
||||||
|
tConfig->logLevel = LOG_NOTICE;
|
||||||
|
}
|
||||||
|
else if (StrEquals(str, "warning"))
|
||||||
|
{
|
||||||
|
tConfig->logLevel = LOG_WARNING;
|
||||||
|
}
|
||||||
|
else if (StrEquals(str, "error"))
|
||||||
|
{
|
||||||
|
tConfig->logLevel = LOG_ERR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tConfig->err = "Invalid value for log.level.";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(str);
|
||||||
|
|
||||||
|
CONFIG_OPTIONAL_STRING(tConfig->logTimestamp, "timestampFormat", "default");
|
||||||
|
|
||||||
|
if (StrEquals(tConfig->logTimestamp, "none"))
|
||||||
|
{
|
||||||
|
Free(tConfig->logTimestamp);
|
||||||
|
tConfig->logTimestamp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = HashMapGet(config, "color");
|
||||||
|
if (value && JsonValueType(value) != JSON_NULL)
|
||||||
|
{
|
||||||
|
if (JsonValueType(value) != JSON_BOOLEAN)
|
||||||
|
{
|
||||||
|
tConfig->err = "Expected type JSON_BOOLEAN for log.color.";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JsonValueAsBoolean(value))
|
||||||
|
{
|
||||||
|
tConfig->flags |= CONFIG_LOG_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
error:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConfigFree(Config * tConfig)
|
||||||
|
{
|
||||||
|
if (!tConfig)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(tConfig->serverName);
|
||||||
|
Free(tConfig->baseUrl);
|
||||||
|
Free(tConfig->identityServer);
|
||||||
|
|
||||||
|
Free(tConfig->uid);
|
||||||
|
Free(tConfig->gid);
|
||||||
|
|
||||||
|
Free(tConfig->logTimestamp);
|
||||||
|
|
||||||
|
if (tConfig->servers)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < ArraySize(tConfig->servers); i++)
|
||||||
|
{
|
||||||
|
HttpServerConfig *serverCfg = ArrayGet(tConfig->servers, i);
|
||||||
|
|
||||||
|
Free(serverCfg->tlsCert);
|
||||||
|
Free(serverCfg->tlsKey);
|
||||||
|
Free(serverCfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayFree(tConfig->servers);
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(tConfig);
|
||||||
|
}
|
||||||
|
|
||||||
Config *
|
Config *
|
||||||
ConfigParse(HashMap * config)
|
ConfigParse(HashMap * config)
|
||||||
{
|
{
|
||||||
Config *tConfig;
|
Config *tConfig;
|
||||||
JsonValue *value;
|
JsonValue *value;
|
||||||
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (!config)
|
if (!config)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
tConfig = Malloc(sizeof(Config));
|
tConfig = Malloc(sizeof(Config));
|
||||||
|
if (!tConfig)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
memset(tConfig, 0, sizeof(Config));
|
memset(tConfig, 0, sizeof(Config));
|
||||||
|
|
||||||
tConfig->maxCache = Int64Create(0, 0);
|
CONFIG_REQUIRE("listen", JSON_ARRAY);
|
||||||
|
if (!ConfigParseListen(tConfig, JsonValueAsArray(value)))
|
||||||
if (!ConfigFromJson(config, tConfig, &tConfig->err))
|
|
||||||
{
|
{
|
||||||
ConfigFree(tConfig);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (!tConfig->baseUrl)
|
|
||||||
|
CONFIG_REQUIRE("serverName", JSON_STRING);
|
||||||
|
CONFIG_COPY_STRING(tConfig->serverName);
|
||||||
|
|
||||||
|
value = HashMapGet(config, "baseUrl");
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
CONFIG_COPY_STRING(tConfig->baseUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
size_t len = strlen(tConfig->serverName) + 10;
|
size_t len = strlen(tConfig->serverName) + 10;
|
||||||
|
|
||||||
tConfig->baseUrl = Malloc(len);
|
tConfig->baseUrl = Malloc(len);
|
||||||
if (!tConfig->baseUrl)
|
if (!tConfig->baseUrl)
|
||||||
{
|
{
|
||||||
tConfig->err = "Couldn't allocate enough memory for 'baseUrl'.";
|
tConfig->err = "Error allocating memory for default config value 'baseUrl'.";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(tConfig->baseUrl, len, "https://%s", tConfig->serverName);
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG_OPTIONAL_STRING(tConfig->identityServer, "identityServer", NULL);
|
||||||
|
|
||||||
|
value = HashMapGet(config, "runAs");
|
||||||
|
if (value && JsonValueType(value) != JSON_NULL)
|
||||||
|
{
|
||||||
|
if (JsonValueType(value) == JSON_OBJECT)
|
||||||
|
{
|
||||||
|
if (!ConfigParseRunAs(tConfig, JsonValueAsObject(value)))
|
||||||
|
{
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!tConfig->log.timestampFormat)
|
else
|
||||||
{
|
{
|
||||||
tConfig->log.timestampFormat = StrDuplicate("default");
|
tConfig->err = "Config directive 'runAs' should be a JSON object that contains a 'uid' and 'gid'.";
|
||||||
}
|
goto error;
|
||||||
for (i = 0; i < ArraySize(tConfig->listen); i++)
|
|
||||||
{
|
|
||||||
ConfigListener *listener = ArrayGet(tConfig->listen, i);
|
|
||||||
if (Int64Eq(listener->maxConnections, Int64Create(0, 0)))
|
|
||||||
{
|
|
||||||
listener->maxConnections = Int64Create(0, 32);
|
|
||||||
}
|
|
||||||
if (Int64Eq(listener->threads, Int64Create(0, 0)))
|
|
||||||
{
|
|
||||||
listener->threads = Int64Create(0, 4);
|
|
||||||
}
|
|
||||||
if (Int64Eq(listener->port, Int64Create(0, 0)))
|
|
||||||
{
|
|
||||||
listener->port = Int64Create(0, 8008);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CONFIG_OPTIONAL_INTEGER(tConfig->maxCache, "maxCache", 0);
|
||||||
|
|
||||||
|
CONFIG_REQUIRE("federation", JSON_BOOLEAN);
|
||||||
|
if (JsonValueAsBoolean(value))
|
||||||
|
{
|
||||||
|
tConfig->flags |= CONFIG_FEDERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG_REQUIRE("registration", JSON_BOOLEAN);
|
||||||
|
if (JsonValueAsBoolean(value))
|
||||||
|
{
|
||||||
|
tConfig->flags |= CONFIG_REGISTRATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG_REQUIRE("log", JSON_OBJECT);
|
||||||
|
if (!ConfigParseLog(tConfig, JsonValueAsObject(value)))
|
||||||
|
{
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
tConfig->ok = 1;
|
tConfig->ok = 1;
|
||||||
tConfig->err = NULL;
|
tConfig->err = NULL;
|
||||||
return tConfig;
|
return tConfig;
|
||||||
|
@ -117,7 +435,41 @@ ConfigCreateDefault(Db * db)
|
||||||
HashMap *json;
|
HashMap *json;
|
||||||
Array *listeners;
|
Array *listeners;
|
||||||
HashMap *listen;
|
HashMap *listen;
|
||||||
|
|
||||||
|
char hostname[HOST_NAME_MAX + 1];
|
||||||
|
|
||||||
|
if (!db)
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref = DbCreate(db, 1, "config");
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
json = DbJson(ref);
|
||||||
|
|
||||||
|
JsonSet(json, JsonValueString("file"), 2, "log", "output");
|
||||||
|
|
||||||
|
listeners = ArrayCreate();
|
||||||
|
listen = HashMapCreate();
|
||||||
|
HashMapSet(listen, "port", JsonValueInteger(Int64Create(0, 8008)));
|
||||||
|
HashMapSet(listen, "tls", JsonValueBoolean(0));
|
||||||
|
ArrayAdd(listeners, JsonValueObject(listen));
|
||||||
|
HashMapSet(json, "listen", JsonValueArray(listeners));
|
||||||
|
|
||||||
|
if (gethostname(hostname, HOST_NAME_MAX + 1) < 0)
|
||||||
|
{
|
||||||
|
strncpy(hostname, "localhost", HOST_NAME_MAX);
|
||||||
|
}
|
||||||
|
HashMapSet(json, "serverName", JsonValueString(hostname));
|
||||||
|
|
||||||
|
HashMapSet(json, "federation", JsonValueBoolean(1));
|
||||||
|
HashMapSet(json, "registration", JsonValueBoolean(0));
|
||||||
|
|
||||||
|
return DbUnlock(db, ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
Config *
|
Config *
|
||||||
|
@ -141,17 +493,6 @@ ConfigLock(Db * db)
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ConfigFullyFree(Config * config)
|
|
||||||
{
|
|
||||||
if (!config)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigFree(config);
|
|
||||||
Free(config);
|
|
||||||
}
|
|
||||||
int
|
int
|
||||||
ConfigUnlock(Config * config)
|
ConfigUnlock(Config * config)
|
||||||
{
|
{
|
||||||
|
@ -166,6 +507,6 @@ ConfigUnlock(Config * config)
|
||||||
db = config->db;
|
db = config->db;
|
||||||
dbRef = config->ref;
|
dbRef = config->ref;
|
||||||
|
|
||||||
ConfigFullyFree(config);
|
ConfigFree(config);
|
||||||
return DbUnlock(db, dbRef);
|
return DbUnlock(db, dbRef);
|
||||||
}
|
}
|
||||||
|
|
80
src/Main.c
80
src/Main.c
|
@ -277,18 +277,17 @@ start:
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tConfig->log.timestampFormat || !StrEquals(tConfig->log.timestampFormat, "default"))
|
if (!tConfig->logTimestamp || !StrEquals(tConfig->logTimestamp, "default"))
|
||||||
{
|
{
|
||||||
LogConfigTimeStampFormatSet(LogConfigGlobal(), tConfig->log.timestampFormat);
|
LogConfigTimeStampFormatSet(LogConfigGlobal(), tConfig->logTimestamp);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* TODO */
|
Free(tConfig->logTimestamp);
|
||||||
/*Free(tConfig->logTimestamp);
|
tConfig->logTimestamp = NULL;
|
||||||
tConfig->logTimestamp = NULL;*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tConfig->log.color)
|
if (tConfig->flags & CONFIG_LOG_COLOR)
|
||||||
{
|
{
|
||||||
LogConfigFlagSet(LogConfigGlobal(), LOG_FLAG_COLOR);
|
LogConfigFlagSet(LogConfigGlobal(), LOG_FLAG_COLOR);
|
||||||
}
|
}
|
||||||
|
@ -297,10 +296,9 @@ start:
|
||||||
LogConfigFlagClear(LogConfigGlobal(), LOG_FLAG_COLOR);
|
LogConfigFlagClear(LogConfigGlobal(), LOG_FLAG_COLOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Set level properly here. */
|
LogConfigLevelSet(LogConfigGlobal(), flags & ARG_VERBOSE ? LOG_DEBUG : tConfig->logLevel);
|
||||||
/*LogConfigLevelSet(LogConfigGlobal(), flags & ARG_VERBOSE ? LOG_DEBUG : tConfig->logLevel);*/
|
|
||||||
|
|
||||||
if (tConfig->log.output == CONFIG_LOG_OUTPUT_FILE)
|
if (tConfig->flags & CONFIG_LOG_FILE)
|
||||||
{
|
{
|
||||||
logFile = StreamOpen("telodendria.log", "a");
|
logFile = StreamOpen("telodendria.log", "a");
|
||||||
|
|
||||||
|
@ -308,14 +306,18 @@ start:
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "Unable to open log file for appending.");
|
Log(LOG_ERR, "Unable to open log file for appending.");
|
||||||
exit = EXIT_FAILURE;
|
exit = EXIT_FAILURE;
|
||||||
/*tConfig->flags &= CONFIG_LOG_STDOUT;*/
|
tConfig->flags &= CONFIG_LOG_STDOUT;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log(LOG_INFO, "Logging to the log file. Check there for all future messages.");
|
Log(LOG_INFO, "Logging to the log file. Check there for all future messages.");
|
||||||
LogConfigOutputSet(LogConfigGlobal(), logFile);
|
LogConfigOutputSet(LogConfigGlobal(), logFile);
|
||||||
}
|
}
|
||||||
else if (tConfig->log.output == CONFIG_LOG_OUTPUT_SYSLOG)
|
else if (tConfig->flags & CONFIG_LOG_STDOUT)
|
||||||
|
{
|
||||||
|
Log(LOG_DEBUG, "Already logging to standard output.");
|
||||||
|
}
|
||||||
|
else if (tConfig->flags & CONFIG_LOG_SYSLOG)
|
||||||
{
|
{
|
||||||
Log(LOG_INFO, "Logging to the syslog. Check there for all future messages.");
|
Log(LOG_INFO, "Logging to the syslog. Check there for all future messages.");
|
||||||
LogConfigFlagSet(LogConfigGlobal(), LOG_FLAG_SYSLOG);
|
LogConfigFlagSet(LogConfigGlobal(), LOG_FLAG_SYSLOG);
|
||||||
|
@ -325,6 +327,13 @@ start:
|
||||||
* messages get passed to the syslog */
|
* messages get passed to the syslog */
|
||||||
setlogmask(LOG_UPTO(LOG_DEBUG));
|
setlogmask(LOG_UPTO(LOG_DEBUG));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(LOG_ERR, "Unknown logging method in flags: '%d'", tConfig->flags);
|
||||||
|
Log(LOG_ERR, "This is a programmer error; please report it.");
|
||||||
|
exit = EXIT_FAILURE;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
/* If a token was created with a default config, print it to the
|
/* If a token was created with a default config, print it to the
|
||||||
* log */
|
* log */
|
||||||
|
@ -339,9 +348,9 @@ start:
|
||||||
Log(LOG_DEBUG, "Server Name: %s", tConfig->serverName);
|
Log(LOG_DEBUG, "Server Name: %s", tConfig->serverName);
|
||||||
Log(LOG_DEBUG, "Base URL: %s", tConfig->baseUrl);
|
Log(LOG_DEBUG, "Base URL: %s", tConfig->baseUrl);
|
||||||
Log(LOG_DEBUG, "Identity Server: %s", tConfig->identityServer);
|
Log(LOG_DEBUG, "Identity Server: %s", tConfig->identityServer);
|
||||||
Log(LOG_DEBUG, "Run As: %s:%s", tConfig->runAs.uid, tConfig->runAs.gid);
|
Log(LOG_DEBUG, "Run As: %s:%s", tConfig->uid, tConfig->gid);
|
||||||
Log(LOG_DEBUG, "Max Cache: %ld", tConfig->maxCache);
|
Log(LOG_DEBUG, "Max Cache: %ld", tConfig->maxCache);
|
||||||
/*Log(LOG_DEBUG, "Flags: %x", tConfig->flags);*/
|
Log(LOG_DEBUG, "Flags: %x", tConfig->flags);
|
||||||
LogConfigUnindent(LogConfigGlobal());
|
LogConfigUnindent(LogConfigGlobal());
|
||||||
|
|
||||||
httpServers = ArrayCreate();
|
httpServers = ArrayCreate();
|
||||||
|
@ -353,50 +362,41 @@ start:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bind servers before possibly dropping permissions. */
|
/* Bind servers before possibly dropping permissions. */
|
||||||
for (i = 0; i < ArraySize(tConfig->listen); i++)
|
for (i = 0; i < ArraySize(tConfig->servers); i++)
|
||||||
{
|
{
|
||||||
ConfigListener *serverCfg = ArrayGet(tConfig->listen, i);
|
HttpServerConfig *serverCfg = ArrayGet(tConfig->servers, i);
|
||||||
|
|
||||||
/* TODO: Think of a nicer solution. */
|
|
||||||
HttpServerConfig args;
|
|
||||||
|
|
||||||
Log(LOG_DEBUG, "HTTP listener: %lu", i);
|
Log(LOG_DEBUG, "HTTP listener: %lu", i);
|
||||||
LogConfigIndent(LogConfigGlobal());
|
LogConfigIndent(LogConfigGlobal());
|
||||||
Log(LOG_DEBUG, "Port: %hu", serverCfg->port);
|
Log(LOG_DEBUG, "Port: %hu", serverCfg->port);
|
||||||
Log(LOG_DEBUG, "Threads: %u", serverCfg->threads);
|
Log(LOG_DEBUG, "Threads: %u", serverCfg->threads);
|
||||||
Log(LOG_DEBUG, "Max Connections: %u", serverCfg->maxConnections);
|
Log(LOG_DEBUG, "Max Connections: %u", serverCfg->maxConnections);
|
||||||
/*Log(LOG_DEBUG, "Flags: %d", serverCfg->flags);*/
|
Log(LOG_DEBUG, "Flags: %d", serverCfg->flags);
|
||||||
Log(LOG_DEBUG, "TLS Cert: %s", serverCfg->tls.cert);
|
Log(LOG_DEBUG, "TLS Cert: %s", serverCfg->tlsCert);
|
||||||
Log(LOG_DEBUG, "TLS Key: %s", serverCfg->tls.key);
|
Log(LOG_DEBUG, "TLS Key: %s", serverCfg->tlsKey);
|
||||||
LogConfigUnindent(LogConfigGlobal());
|
LogConfigUnindent(LogConfigGlobal());
|
||||||
|
|
||||||
args.port = serverCfg->port;
|
serverCfg->handler = MatrixHttpHandler;
|
||||||
args.threads = serverCfg->maxConnections;
|
serverCfg->handlerArgs = &matrixArgs;
|
||||||
args.maxConnections = serverCfg->maxConnections;
|
|
||||||
args.tlsCert = serverCfg->tls.cert;
|
|
||||||
args.tlsKey = serverCfg->tls.key;
|
|
||||||
|
|
||||||
args.handler = MatrixHttpHandler;
|
if (serverCfg->flags & HTTP_FLAG_TLS)
|
||||||
args.handlerArgs = &matrixArgs;
|
|
||||||
|
|
||||||
if (serverCfg->tls.cert && serverCfg->tls.key)
|
|
||||||
{
|
{
|
||||||
if (UInt64Eq(UtilLastModified(serverCfg->tls.cert), UInt64Create(0, 0)))
|
if (UInt64Eq(UtilLastModified(serverCfg->tlsCert), UInt64Create(0, 0)))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tls.cert);
|
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tlsCert);
|
||||||
exit = EXIT_FAILURE;
|
exit = EXIT_FAILURE;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UInt64Eq(UtilLastModified(serverCfg->tls.key), UInt64Create(0, 0)))
|
if (UInt64Eq(UtilLastModified(serverCfg->tlsKey), UInt64Create(0, 0)))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tls.key);
|
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tlsKey);
|
||||||
exit = EXIT_FAILURE;
|
exit = EXIT_FAILURE;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server = HttpServerCreate(&args);
|
server = HttpServerCreate(serverCfg);
|
||||||
if (!server)
|
if (!server)
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "Unable to create HTTP server on port %d: %s",
|
Log(LOG_ERR, "Unable to create HTTP server on port %d: %s",
|
||||||
|
@ -417,10 +417,10 @@ start:
|
||||||
|
|
||||||
Log(LOG_DEBUG, "Running as uid:gid: %d:%d.", getuid(), getgid());
|
Log(LOG_DEBUG, "Running as uid:gid: %d:%d.", getuid(), getgid());
|
||||||
|
|
||||||
if (tConfig->runAs.uid && tConfig->runAs.gid)
|
if (tConfig->uid && tConfig->gid)
|
||||||
{
|
{
|
||||||
userInfo = getpwnam(tConfig->runAs.uid);
|
userInfo = getpwnam(tConfig->uid);
|
||||||
groupInfo = getgrnam(tConfig->runAs.gid);
|
groupInfo = getgrnam(tConfig->gid);
|
||||||
|
|
||||||
if (!userInfo || !groupInfo)
|
if (!userInfo || !groupInfo)
|
||||||
{
|
{
|
||||||
|
@ -450,7 +450,7 @@ start:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log(LOG_DEBUG, "Set uid/gid to %s:%s.", tConfig->runAs.uid, tConfig->runAs.gid);
|
Log(LOG_DEBUG, "Set uid/gid to %s:%s.", tConfig->uid, tConfig->gid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -462,7 +462,7 @@ start:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (tConfig->runAs.uid && tConfig->runAs.gid)
|
if (tConfig->uid && tConfig->gid)
|
||||||
{
|
{
|
||||||
if (getuid() != userInfo->pw_uid || getgid() != groupInfo->gr_gid)
|
if (getuid() != userInfo->pw_uid || getgid() != groupInfo->gr_gid)
|
||||||
{
|
{
|
||||||
|
|
|
@ -118,7 +118,7 @@ ROUTE_IMPL(RouteConfig, path, argp)
|
||||||
response = MatrixErrorCreate(M_BAD_JSON, newConf->err);
|
response = MatrixErrorCreate(M_BAD_JSON, newConf->err);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigFullyFree(newConf);
|
ConfigFree(newConf);
|
||||||
JsonFree(request);
|
JsonFree(request);
|
||||||
break;
|
break;
|
||||||
case HTTP_PUT:
|
case HTTP_PUT:
|
||||||
|
@ -165,7 +165,7 @@ ROUTE_IMPL(RouteConfig, path, argp)
|
||||||
response = MatrixErrorCreate(M_BAD_JSON, newConf->err);
|
response = MatrixErrorCreate(M_BAD_JSON, newConf->err);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigFullyFree(newConf);
|
ConfigFree(newConf);
|
||||||
JsonFree(request);
|
JsonFree(request);
|
||||||
JsonFree(newJson);
|
JsonFree(newJson);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -134,7 +134,7 @@ ROUTE_IMPL(RouteRegister, path, argp)
|
||||||
uiaFlows = ArrayCreate();
|
uiaFlows = ArrayCreate();
|
||||||
ArrayAdd(uiaFlows, RouteRegisterRegFlow());
|
ArrayAdd(uiaFlows, RouteRegisterRegFlow());
|
||||||
|
|
||||||
if (config->registration)
|
if (config->flags & CONFIG_REGISTRATION)
|
||||||
{
|
{
|
||||||
ArrayAdd(uiaFlows, UiaDummyFlow());
|
ArrayAdd(uiaFlows, UiaDummyFlow());
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,12 +48,68 @@
|
||||||
* .Xr telodendria-config 7 .
|
* .Xr telodendria-config 7 .
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Schema/Config.h>
|
|
||||||
|
|
||||||
#include <Cytoplasm/HashMap.h>
|
#include <Cytoplasm/HashMap.h>
|
||||||
#include <Cytoplasm/Array.h>
|
#include <Cytoplasm/Array.h>
|
||||||
#include <Cytoplasm/Db.h>
|
#include <Cytoplasm/Db.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bit flags that can be set in the flags field of the configuration
|
||||||
|
* structure.
|
||||||
|
*/
|
||||||
|
typedef enum ConfigFlag
|
||||||
|
{
|
||||||
|
CONFIG_FEDERATION = (1 << 0),
|
||||||
|
CONFIG_REGISTRATION = (1 << 1),
|
||||||
|
CONFIG_LOG_COLOR = (1 << 2),
|
||||||
|
CONFIG_LOG_FILE = (1 << 3),
|
||||||
|
CONFIG_LOG_STDOUT = (1 << 4),
|
||||||
|
CONFIG_LOG_SYSLOG = (1 << 5)
|
||||||
|
} ConfigFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration structure is not opaque like many of the other
|
||||||
|
* structures present in the other public APIs. This is intentional;
|
||||||
|
* defining functions for all of the fields would simply add too much
|
||||||
|
* unnecessary overhead.
|
||||||
|
*/
|
||||||
|
typedef struct Config
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* These are used internally and should not be touched outside of
|
||||||
|
* the functions defined in this API.
|
||||||
|
*/
|
||||||
|
Db *db;
|
||||||
|
DbRef *ref;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Whether or not the parsing was successful. If this boolean
|
||||||
|
* value is 0, then read the error message and assume that all
|
||||||
|
* other fields are invalid.
|
||||||
|
*/
|
||||||
|
int ok;
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
char *serverName;
|
||||||
|
char *baseUrl;
|
||||||
|
char *identityServer;
|
||||||
|
|
||||||
|
char *uid;
|
||||||
|
char *gid;
|
||||||
|
|
||||||
|
unsigned int flags;
|
||||||
|
|
||||||
|
size_t maxCache;
|
||||||
|
|
||||||
|
char *logTimestamp;
|
||||||
|
int logLevel;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An array of HttpServerConfig structures. Consult the HttpServer
|
||||||
|
* API.
|
||||||
|
*/
|
||||||
|
Array *servers;
|
||||||
|
} Config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a JSON object, extracting the necessary values, validating
|
* Parse a JSON object, extracting the necessary values, validating
|
||||||
* them, and adding them to the configuration structure for use by the
|
* them, and adding them to the configuration structure for use by the
|
||||||
|
@ -71,7 +127,7 @@ extern Config * ConfigParse(HashMap *);
|
||||||
* as well as the structure itself, such that it is completely invalid
|
* as well as the structure itself, such that it is completely invalid
|
||||||
* when this function returns.
|
* when this function returns.
|
||||||
*/
|
*/
|
||||||
extern void ConfigFullyFree(Config *);
|
extern void ConfigFree(Config *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether or not the configuration exists in the database,
|
* Check whether or not the configuration exists in the database,
|
||||||
|
|
Loading…
Reference in a new issue