forked from Telodendria/Telodendria
Compare commits
2 commits
18488d463e
...
fbc4486930
Author | SHA1 | Date | |
---|---|---|---|
fbc4486930 | |||
74ab3f350f |
6 changed files with 177 additions and 481 deletions
93
Schema/Config.json
Normal file
93
Schema/Config.json
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
{
|
||||||
|
"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 <Config.h>
|
#include <Schema/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,378 +41,60 @@
|
||||||
#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));
|
||||||
|
|
||||||
CONFIG_REQUIRE("listen", JSON_ARRAY);
|
tConfig->maxCache = Int64Create(0, 0);
|
||||||
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 = "Error allocating memory for default config value 'baseUrl'.";
|
tConfig->err = "Couldn't allocate enough memory for '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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tConfig->err = "Config directive 'runAs' should be a JSON object that contains a 'uid' and 'gid'.";
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!tConfig->log.timestampFormat)
|
||||||
CONFIG_OPTIONAL_INTEGER(tConfig->maxCache, "maxCache", 0);
|
|
||||||
|
|
||||||
CONFIG_REQUIRE("federation", JSON_BOOLEAN);
|
|
||||||
if (JsonValueAsBoolean(value))
|
|
||||||
{
|
{
|
||||||
tConfig->flags |= CONFIG_FEDERATION;
|
tConfig->log.timestampFormat = StrDuplicate("default");
|
||||||
}
|
}
|
||||||
|
for (i = 0; i < ArraySize(tConfig->listen); i++)
|
||||||
CONFIG_REQUIRE("registration", JSON_BOOLEAN);
|
|
||||||
if (JsonValueAsBoolean(value))
|
|
||||||
{
|
{
|
||||||
tConfig->flags |= CONFIG_REGISTRATION;
|
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_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;
|
||||||
|
@ -435,41 +117,7 @@ ConfigCreateDefault(Db * db)
|
||||||
HashMap *json;
|
HashMap *json;
|
||||||
Array *listeners;
|
Array *listeners;
|
||||||
HashMap *listen;
|
HashMap *listen;
|
||||||
|
return 0;
|
||||||
char hostname[HOST_NAME_MAX + 1];
|
|
||||||
|
|
||||||
if (!db)
|
|
||||||
{
|
|
||||||
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 *
|
||||||
|
@ -493,6 +141,17 @@ 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)
|
||||||
{
|
{
|
||||||
|
@ -507,6 +166,6 @@ ConfigUnlock(Config * config)
|
||||||
db = config->db;
|
db = config->db;
|
||||||
dbRef = config->ref;
|
dbRef = config->ref;
|
||||||
|
|
||||||
ConfigFree(config);
|
ConfigFullyFree(config);
|
||||||
return DbUnlock(db, dbRef);
|
return DbUnlock(db, dbRef);
|
||||||
}
|
}
|
||||||
|
|
80
src/Main.c
80
src/Main.c
|
@ -277,17 +277,18 @@ start:
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tConfig->logTimestamp || !StrEquals(tConfig->logTimestamp, "default"))
|
if (!tConfig->log.timestampFormat || !StrEquals(tConfig->log.timestampFormat, "default"))
|
||||||
{
|
{
|
||||||
LogConfigTimeStampFormatSet(LogConfigGlobal(), tConfig->logTimestamp);
|
LogConfigTimeStampFormatSet(LogConfigGlobal(), tConfig->log.timestampFormat);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Free(tConfig->logTimestamp);
|
/* TODO */
|
||||||
tConfig->logTimestamp = NULL;
|
/*Free(tConfig->logTimestamp);
|
||||||
|
tConfig->logTimestamp = NULL;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tConfig->flags & CONFIG_LOG_COLOR)
|
if (tConfig->log.color)
|
||||||
{
|
{
|
||||||
LogConfigFlagSet(LogConfigGlobal(), LOG_FLAG_COLOR);
|
LogConfigFlagSet(LogConfigGlobal(), LOG_FLAG_COLOR);
|
||||||
}
|
}
|
||||||
|
@ -296,9 +297,10 @@ start:
|
||||||
LogConfigFlagClear(LogConfigGlobal(), LOG_FLAG_COLOR);
|
LogConfigFlagClear(LogConfigGlobal(), LOG_FLAG_COLOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogConfigLevelSet(LogConfigGlobal(), flags & ARG_VERBOSE ? LOG_DEBUG : tConfig->logLevel);
|
/* TODO: Set level properly here. */
|
||||||
|
/*LogConfigLevelSet(LogConfigGlobal(), flags & ARG_VERBOSE ? LOG_DEBUG : tConfig->logLevel);*/
|
||||||
|
|
||||||
if (tConfig->flags & CONFIG_LOG_FILE)
|
if (tConfig->log.output == CONFIG_LOG_OUTPUT_FILE)
|
||||||
{
|
{
|
||||||
logFile = StreamOpen("telodendria.log", "a");
|
logFile = StreamOpen("telodendria.log", "a");
|
||||||
|
|
||||||
|
@ -306,18 +308,14 @@ 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->flags & CONFIG_LOG_STDOUT)
|
else if (tConfig->log.output == CONFIG_LOG_OUTPUT_SYSLOG)
|
||||||
{
|
|
||||||
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);
|
||||||
|
@ -327,13 +325,6 @@ 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 */
|
||||||
|
@ -348,9 +339,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->uid, tConfig->gid);
|
Log(LOG_DEBUG, "Run As: %s:%s", tConfig->runAs.uid, tConfig->runAs.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();
|
||||||
|
@ -362,41 +353,50 @@ start:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bind servers before possibly dropping permissions. */
|
/* Bind servers before possibly dropping permissions. */
|
||||||
for (i = 0; i < ArraySize(tConfig->servers); i++)
|
for (i = 0; i < ArraySize(tConfig->listen); i++)
|
||||||
{
|
{
|
||||||
HttpServerConfig *serverCfg = ArrayGet(tConfig->servers, i);
|
ConfigListener *serverCfg = ArrayGet(tConfig->listen, 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->tlsCert);
|
Log(LOG_DEBUG, "TLS Cert: %s", serverCfg->tls.cert);
|
||||||
Log(LOG_DEBUG, "TLS Key: %s", serverCfg->tlsKey);
|
Log(LOG_DEBUG, "TLS Key: %s", serverCfg->tls.key);
|
||||||
LogConfigUnindent(LogConfigGlobal());
|
LogConfigUnindent(LogConfigGlobal());
|
||||||
|
|
||||||
serverCfg->handler = MatrixHttpHandler;
|
args.port = serverCfg->port;
|
||||||
serverCfg->handlerArgs = &matrixArgs;
|
args.threads = serverCfg->maxConnections;
|
||||||
|
args.maxConnections = serverCfg->maxConnections;
|
||||||
|
args.tlsCert = serverCfg->tls.cert;
|
||||||
|
args.tlsKey = serverCfg->tls.key;
|
||||||
|
|
||||||
if (serverCfg->flags & HTTP_FLAG_TLS)
|
args.handler = MatrixHttpHandler;
|
||||||
|
args.handlerArgs = &matrixArgs;
|
||||||
|
|
||||||
|
if (serverCfg->tls.cert && serverCfg->tls.key)
|
||||||
{
|
{
|
||||||
if (UInt64Eq(UtilLastModified(serverCfg->tlsCert), UInt64Create(0, 0)))
|
if (UInt64Eq(UtilLastModified(serverCfg->tls.cert), UInt64Create(0, 0)))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tlsCert);
|
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tls.cert);
|
||||||
exit = EXIT_FAILURE;
|
exit = EXIT_FAILURE;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UInt64Eq(UtilLastModified(serverCfg->tlsKey), UInt64Create(0, 0)))
|
if (UInt64Eq(UtilLastModified(serverCfg->tls.key), UInt64Create(0, 0)))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tlsKey);
|
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tls.key);
|
||||||
exit = EXIT_FAILURE;
|
exit = EXIT_FAILURE;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server = HttpServerCreate(serverCfg);
|
server = HttpServerCreate(&args);
|
||||||
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->uid && tConfig->gid)
|
if (tConfig->runAs.uid && tConfig->runAs.gid)
|
||||||
{
|
{
|
||||||
userInfo = getpwnam(tConfig->uid);
|
userInfo = getpwnam(tConfig->runAs.uid);
|
||||||
groupInfo = getgrnam(tConfig->gid);
|
groupInfo = getgrnam(tConfig->runAs.gid);
|
||||||
|
|
||||||
if (!userInfo || !groupInfo)
|
if (!userInfo || !groupInfo)
|
||||||
{
|
{
|
||||||
|
@ -450,7 +450,7 @@ start:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log(LOG_DEBUG, "Set uid/gid to %s:%s.", tConfig->uid, tConfig->gid);
|
Log(LOG_DEBUG, "Set uid/gid to %s:%s.", tConfig->runAs.uid, tConfig->runAs.gid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -462,7 +462,7 @@ start:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (tConfig->uid && tConfig->gid)
|
if (tConfig->runAs.uid && tConfig->runAs.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);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigFree(newConf);
|
ConfigFullyFree(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigFree(newConf);
|
ConfigFullyFree(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->flags & CONFIG_REGISTRATION)
|
if (config->registration)
|
||||||
{
|
{
|
||||||
ArrayAdd(uiaFlows, UiaDummyFlow());
|
ArrayAdd(uiaFlows, UiaDummyFlow());
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,68 +48,12 @@
|
||||||
* .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
|
||||||
|
@ -127,7 +71,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 ConfigFree(Config *);
|
extern void ConfigFullyFree(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