diff --git a/Schema/Config.json b/Schema/Config.json new file mode 100644 index 0000000..be4b16d --- /dev/null +++ b/Schema/Config.json @@ -0,0 +1,88 @@ +{ + "guard": "TELODENDRIA_SCHEMA_CONFIG_H", + "header": "Schema\/Config.h", + "include": [ "Cytoplasm\/Db.h", "Cytoplasm/HttpServer.h" ], + + "types": { + "ConfigTls": { + "fields": { + "cert": { "type": "string", "required": true }, + "key": { "type": "string", "required": true } + }, + "type": "struct" + }, + + "ConfigListener": { + "fields": { + "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 }, + "pid": { "type": "string", "required": false }, + + "maxCache": { "type": "integer", "required": false }, + + "federation": { "type": "boolean", "required": true }, + "registration": { "type": "boolean", "required": true } + }, + "type": "struct" + } + } +} diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6f62558..8ab1b23 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -31,6 +31,7 @@ The following endpoints were added: ### Bug Fixes & General Improvements +- Use `j2s` for parsing the configuration - Fixed a double-free in `RouteUserProfile()` that would cause errors with certain Matrix clients. (#35) - Improved compatibility with NetBSD on various platforms. @@ -43,6 +44,8 @@ parsing request bodies. ### New Features +- Implemented a `"pid"` option in the configuration, allowing Telodendria +to write its process ID to a specified file. - Moved all administrator API endpoints to `/_telodendria/admin/v1`, because later revisions of the administrator API may break clients, so we want a way to give those breaking revisions new endpoints. diff --git a/docs/user/config.md b/docs/user/config.md index f021633..ee19f62 100644 --- a/docs/user/config.md +++ b/docs/user/config.md @@ -19,8 +19,7 @@ key-value form: "serverName": "telodendria.io", "listen": [ { - "port": 8008, - "tls": false + "port": 8008 } ] @@ -51,7 +50,7 @@ Here are the top-level directives: this is a concern, a reverse-proxy such as `relayd` can be placed in front of Telodendria to block access to undesired APIs. - - **tls:** `Object|null|false` + - **tls:** `Object` Telodendria can be compiled with TLS support. If it is, then a particular listener can be set to use TLS for connections. If @@ -106,6 +105,9 @@ Here are the top-level directives: or you want to start over. **serverName** should be a DNS name that can be publicly resolved. This directive is required. +- **pid:** `String` + Configure the file Telodendria writes its PID to. + - **baseUrl:** `String` Set the server's base URL. **baseUrl** should be a valid URL, diff --git a/src/Config.c b/src/Config.c index 2f77365..1f7a9f7 100644 --- a/src/Config.c +++ b/src/Config.c @@ -22,405 +22,89 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include +#include #include #include #include #include #include #include -#include #include #include +#include +#include #include #include #include #include +#include +#include #ifndef HOST_NAME_MAX #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX #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) +void +ConfigParse(HashMap * config, Config *tConfig) { 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 * -ConfigParse(HashMap * config) -{ - Config *tConfig; - JsonValue *value; - if (!config) { - return NULL; - } - - tConfig = Malloc(sizeof(Config)); - if (!tConfig) - { - return NULL; + tConfig->ok = 0; + tConfig->err = "Invalid object given as config."; + return; } memset(tConfig, 0, sizeof(Config)); - CONFIG_REQUIRE("listen", JSON_ARRAY); - if (!ConfigParseListen(tConfig, JsonValueAsArray(value))) + tConfig->maxCache = Int64Create(0, 0); + + if (!ConfigFromJson(config, tConfig, &tConfig->err)) { + ConfigFree(tConfig); goto error; } - - CONFIG_REQUIRE("serverName", JSON_STRING); - CONFIG_COPY_STRING(tConfig->serverName); - - value = HashMapGet(config, "baseUrl"); - if (value) - { - CONFIG_COPY_STRING(tConfig->baseUrl); - } - else + if (!tConfig->baseUrl) { size_t len = strlen(tConfig->serverName) + 10; tConfig->baseUrl = Malloc(len); 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); + 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 (!tConfig->log.timestampFormat) { - if (JsonValueType(value) == JSON_OBJECT) + tConfig->log.timestampFormat = StrDuplicate("default"); + } + for (i = 0; i < ArraySize(tConfig->listen); i++) + { + ConfigListener *listener = ArrayGet(tConfig->listen, i); + if (Int64Eq(listener->maxConnections, Int64Create(0, 0))) { - if (!ConfigParseRunAs(tConfig, JsonValueAsObject(value))) - { - goto error; - } + listener->maxConnections = Int64Create(0, 32); } - else + if (Int64Eq(listener->threads, Int64Create(0, 0))) { - tConfig->err = "Config directive 'runAs' should be a JSON object that contains a 'uid' and 'gid'."; - goto error; + 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->err = NULL; - return tConfig; + return; error: tConfig->ok = 0; - return tConfig; + return; } int @@ -432,75 +116,91 @@ ConfigExists(Db * db) int ConfigCreateDefault(Db * db) { - DbRef *ref; + Config config; + ConfigListener *listener; + HashMap *json; - Array *listeners; - HashMap *listen; + JsonValue *val; - char hostname[HOST_NAME_MAX + 1]; + DbRef *ref; - if (!db) - { - return 0; - } + size_t len; + + memset(&config, 0, sizeof(Config)); + + + config.log.output = CONFIG_LOG_OUTPUT_FILE; + + config.runAs.gid = StrDuplicate(getgrgid(getgid())->gr_name); + config.runAs.uid = StrDuplicate(getpwuid(getuid())->pw_name); + + config.registration = 0; + config.federation = 1; + + /* Create serverName and baseUrl. */ + config.serverName = Malloc(HOST_NAME_MAX + 1); + memset(config.serverName, 0, HOST_NAME_MAX + 1); + gethostname(config.serverName, HOST_NAME_MAX); + len = strlen(config.serverName) + 10; + config.baseUrl = Malloc(len); + snprintf(config.baseUrl, len, "https://%s/", config.serverName); + + /* Add simple listener without TLS. */ + config.listen = ArrayCreate(); + listener = Malloc(sizeof(ConfigListener)); + listener->maxConnections = Int64Create(0, 32); + listener->port = Int64Create(0, 8008); + listener->threads = Int64Create(0, 4); + + ArrayAdd(config.listen, listener); + + /* Write it all out to the configuration file. */ + json = ConfigToJson(&config); + val = JsonGet(json, 1, "listen"); + val = ArrayGet(JsonValueAsArray(val), 0); + JsonValueFree(HashMapDelete(JsonValueAsObject(val), "tls")); ref = DbCreate(db, 1, "config"); if (!ref) { + ConfigFree(&config); return 0; } + DbJsonSet(ref, json); + DbUnlock(db, ref); - json = DbJson(ref); + ConfigFree(&config); + JsonFree(json); - 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); + return 1; } -Config * -ConfigLock(Db * db) +void +ConfigLock(Db * db, Config *config) { - Config *config; DbRef *ref = DbLock(db, 1, "config"); if (!ref) { - return NULL; + config->ok = 0; + config->err = "Couldn't lock configuration."; } - config = ConfigParse(DbJson(ref)); - if (config) + ConfigParse(DbJson(ref), config); + if (config->ok) { config->db = db; config->ref = ref; } - - return config; } int -ConfigUnlock(Config * config) +ConfigUnlock(Config *config) { Db *db; DbRef *dbRef; - if (!config) + if (!config->ok) { return 0; } @@ -509,5 +209,25 @@ ConfigUnlock(Config * config) dbRef = config->ref; ConfigFree(config); + config->ok = 0; + return DbUnlock(db, dbRef); } +int +ConfigLogLevelToSyslog(ConfigLogLevel level) +{ + switch (level) + { + case CONFIG_LOG_LEVEL_NOTICE: + return LOG_NOTICE; + case CONFIG_LOG_LEVEL_ERROR: + return LOG_ERR; + case CONFIG_LOG_LEVEL_MESSAGE: + return LOG_INFO; + case CONFIG_LOG_LEVEL_DEBUG: + return LOG_DEBUG; + case CONFIG_LOG_LEVEL_WARNING: + return LOG_WARNING; + } + return LOG_INFO; +} diff --git a/src/Main.c b/src/Main.c index d1406f1..a7d7f3c 100644 --- a/src/Main.c +++ b/src/Main.c @@ -103,8 +103,11 @@ Main(Array * args) char *dbPath; /* Program configuration */ - Config *tConfig; + Config tConfig; Stream *logFile; + Stream *pidFile = NULL; + + char *pidPath = NULL; /* User validation */ struct passwd *userInfo; @@ -133,7 +136,6 @@ start: exit = EXIT_SUCCESS; flags = 0; dbPath = NULL; - tConfig = NULL; logFile = NULL; userInfo = NULL; groupInfo = NULL; @@ -262,33 +264,20 @@ start: Log(LOG_NOTICE, "Loading configuration..."); - tConfig = ConfigLock(matrixArgs.db); - if (!tConfig) + ConfigLock(matrixArgs.db, &tConfig); + if (!tConfig.ok) { - Log(LOG_ERR, "Error locking the configuration."); - Log(LOG_ERR, "The configuration object is corrupted or otherwise invalid."); - Log(LOG_ERR, "Please restore from a backup."); - exit = EXIT_FAILURE; - goto finish; - } - else if (!tConfig->ok) - { - Log(LOG_ERR, tConfig->err); + Log(LOG_ERR, tConfig.err); exit = EXIT_FAILURE; goto finish; } - if (!tConfig->logTimestamp || !StrEquals(tConfig->logTimestamp, "default")) + if (!tConfig.log.timestampFormat || !StrEquals(tConfig.log.timestampFormat, "default")) { - LogConfigTimeStampFormatSet(LogConfigGlobal(), tConfig->logTimestamp); - } - else - { - Free(tConfig->logTimestamp); - tConfig->logTimestamp = NULL; + LogConfigTimeStampFormatSet(LogConfigGlobal(), tConfig.log.timestampFormat); } - if (tConfig->flags & CONFIG_LOG_COLOR) + if (tConfig.log.color) { LogConfigFlagSet(LogConfigGlobal(), LOG_FLAG_COLOR); } @@ -297,9 +286,13 @@ start: LogConfigFlagClear(LogConfigGlobal(), LOG_FLAG_COLOR); } - LogConfigLevelSet(LogConfigGlobal(), flags & ARG_VERBOSE ? LOG_DEBUG : tConfig->logLevel); + LogConfigLevelSet( + LogConfigGlobal(), + flags & ARG_VERBOSE ? + LOG_DEBUG : + ConfigLogLevelToSyslog(tConfig.log.level)); - if (tConfig->flags & CONFIG_LOG_FILE) + if (tConfig.log.output == CONFIG_LOG_OUTPUT_FILE) { logFile = StreamOpen("telodendria.log", "a"); @@ -307,18 +300,18 @@ start: { Log(LOG_ERR, "Unable to open log file for appending."); exit = EXIT_FAILURE; - tConfig->flags &= CONFIG_LOG_STDOUT; + tConfig.log.output = CONFIG_LOG_OUTPUT_STDOUT; goto finish; } Log(LOG_INFO, "Logging to the log file. Check there for all future messages."); LogConfigOutputSet(LogConfigGlobal(), logFile); } - else if (tConfig->flags & CONFIG_LOG_STDOUT) + else if (tConfig.log.output == CONFIG_LOG_OUTPUT_STDOUT) { Log(LOG_DEBUG, "Already logging to standard output."); } - else if (tConfig->flags & CONFIG_LOG_SYSLOG) + else if (tConfig.log.output == CONFIG_LOG_OUTPUT_SYSLOG) { Log(LOG_INFO, "Logging to the syslog. Check there for all future messages."); LogConfigFlagSet(LogConfigGlobal(), LOG_FLAG_SYSLOG); @@ -328,13 +321,6 @@ start: * messages get passed to the syslog */ 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 * log */ @@ -344,14 +330,30 @@ start: Free(token); } + if (tConfig.pid) + { + pidFile = StreamOpen(tConfig.pid, "w+"); + if (!pidFile) + { + char *msg = "Couldn't lock PID file at '%s'"; + Log(LOG_ERR, msg, tConfig.pid); + exit = EXIT_FAILURE; + goto finish; + } + pidPath = StrDuplicate(tConfig.pid); + StreamPrintf(pidFile, "%ld", (long) getpid()); + StreamClose(pidFile); + } + Log(LOG_DEBUG, "Configuration:"); LogConfigIndent(LogConfigGlobal()); - Log(LOG_DEBUG, "Server Name: %s", tConfig->serverName); - Log(LOG_DEBUG, "Base URL: %s", tConfig->baseUrl); - Log(LOG_DEBUG, "Identity Server: %s", tConfig->identityServer); - Log(LOG_DEBUG, "Run As: %s:%s", tConfig->uid, tConfig->gid); - Log(LOG_DEBUG, "Max Cache: %ld", tConfig->maxCache); - Log(LOG_DEBUG, "Flags: %x", tConfig->flags); + Log(LOG_DEBUG, "Server Name: %s", tConfig.serverName); + Log(LOG_DEBUG, "Base URL: %s", tConfig.baseUrl); + Log(LOG_DEBUG, "Identity Server: %s", tConfig.identityServer); + Log(LOG_DEBUG, "Run As: %s:%s", tConfig.runAs.uid, tConfig.runAs.gid); + Log(LOG_DEBUG, "Max Cache: %ld", tConfig.maxCache); + Log(LOG_DEBUG, "Registration: %s", tConfig.registration ? "true" : "false"); + Log(LOG_DEBUG, "Federation: %s", tConfig.federation ? "true" : "false"); LogConfigUnindent(LogConfigGlobal()); httpServers = ArrayCreate(); @@ -363,41 +365,51 @@ start: } /* 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); + + HttpServerConfig args; + + args.port = serverCfg->port; + args.threads = serverCfg->maxConnections; + args.maxConnections = serverCfg->maxConnections; + args.tlsCert = serverCfg->tls.cert; + args.tlsKey = serverCfg->tls.key; + args.flags = args.tlsCert && args.tlsKey ? HTTP_FLAG_TLS : HTTP_FLAG_NONE; Log(LOG_DEBUG, "HTTP listener: %lu", i); LogConfigIndent(LogConfigGlobal()); Log(LOG_DEBUG, "Port: %hu", serverCfg->port); Log(LOG_DEBUG, "Threads: %u", serverCfg->threads); Log(LOG_DEBUG, "Max Connections: %u", serverCfg->maxConnections); - Log(LOG_DEBUG, "Flags: %d", serverCfg->flags); - Log(LOG_DEBUG, "TLS Cert: %s", serverCfg->tlsCert); - Log(LOG_DEBUG, "TLS Key: %s", serverCfg->tlsKey); + Log(LOG_DEBUG, "Flags: %d", args.flags); + Log(LOG_DEBUG, "TLS Cert: %s", serverCfg->tls.cert); + Log(LOG_DEBUG, "TLS Key: %s", serverCfg->tls.key); LogConfigUnindent(LogConfigGlobal()); - serverCfg->handler = MatrixHttpHandler; - serverCfg->handlerArgs = &matrixArgs; - if (serverCfg->flags & HTTP_FLAG_TLS) + args.handler = MatrixHttpHandler; + args.handlerArgs = &matrixArgs; + + if (args.flags & HTTP_FLAG_TLS) { - 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; 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; goto finish; } } - server = HttpServerCreate(serverCfg); + server = HttpServerCreate(&args); if (!server) { Log(LOG_ERR, "Unable to create HTTP server on port %d: %s", @@ -418,10 +430,10 @@ start: 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); - groupInfo = getgrnam(tConfig->gid); + userInfo = getpwnam(tConfig.runAs.uid); + groupInfo = getgrnam(tConfig.runAs.gid); if (!userInfo || !groupInfo) { @@ -451,7 +463,7 @@ start: } 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 @@ -463,7 +475,7 @@ start: } else { - if (tConfig->uid && tConfig->gid) + if (tConfig.runAs.uid && tConfig.runAs.gid) { if (getuid() != userInfo->pw_uid || getgid() != groupInfo->gr_gid) { @@ -476,17 +488,16 @@ start: } } - if (!tConfig->maxCache) + if (!tConfig.maxCache) { Log(LOG_WARNING, "Database caching is disabled."); Log(LOG_WARNING, "If this is not what you intended, check the config file"); Log(LOG_WARNING, "and ensure that maxCache is a valid number of bytes."); } - DbMaxCacheSet(matrixArgs.db, tConfig->maxCache); + DbMaxCacheSet(matrixArgs.db, tConfig.maxCache); - ConfigUnlock(tConfig); - tConfig = NULL; + ConfigUnlock(&tConfig); cron = CronCreate(60 * 1000); /* 1-minute tick */ if (!cron) @@ -593,7 +604,7 @@ finish: Log(LOG_DEBUG, "Stopped and freed job scheduler."); } - ConfigUnlock(tConfig); + ConfigUnlock(&tConfig); Log(LOG_DEBUG, "Unlocked configuration."); DbClose(matrixArgs.db); @@ -602,6 +613,12 @@ finish: HttpRouterFree(matrixArgs.router); Log(LOG_DEBUG, "Freed routing tree."); + if (pidPath) + { + remove(pidPath); + Free(pidPath); + } + /* * Uninstall the memory hook because it uses the Log * API, whose configuration is being freed now, so it diff --git a/src/Routes/RouteChangePwd.c b/src/Routes/RouteChangePwd.c index f8ad988..60272cb 100644 --- a/src/Routes/RouteChangePwd.c +++ b/src/Routes/RouteChangePwd.c @@ -68,13 +68,14 @@ ROUTE_IMPL(RouteChangePwd, path, argp) char *msg; - Config *config = ConfigLock(db); + Config config; - if (!config) + ConfigLock(db, &config); + if (!config.ok) { - Log(LOG_ERR, "Password endpoint failed to lock configuration."); + Log(LOG_ERR, "%s", config.err); HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); - return MatrixErrorCreate(M_UNKNOWN, NULL); + return MatrixErrorCreate(M_UNKNOWN, config.err); } (void) path; @@ -157,7 +158,7 @@ ROUTE_IMPL(RouteChangePwd, path, argp) response = HashMapCreate(); finish: - ConfigUnlock(config); + ConfigUnlock(&config); UserUnlock(user); JsonFree(request); return response; diff --git a/src/Routes/RouteConfig.c b/src/Routes/RouteConfig.c index 66b031a..2c9fdfd 100644 --- a/src/Routes/RouteConfig.c +++ b/src/Routes/RouteConfig.c @@ -37,10 +37,10 @@ ROUTE_IMPL(RouteConfig, path, argp) char *msg; User *user = NULL; - Config *config = NULL; + Config config; HashMap *request = NULL; - Config *newConf; + Config newConf; HashMap *newJson = NULL; (void) path; @@ -67,20 +67,19 @@ ROUTE_IMPL(RouteConfig, path, argp) goto finish; } - config = ConfigLock(args->matrixArgs->db); - if (!config) + ConfigLock(args->matrixArgs->db, &config); + if (!config.ok) { - msg = "Internal server error while locking configuration."; - Log(LOG_ERR, "Config endpoint failed to lock configuration."); + Log(LOG_ERR, "%s", config.err); HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); - response = MatrixErrorCreate(M_UNKNOWN, msg); + response = MatrixErrorCreate(M_UNKNOWN, config.err); goto finish; } switch (HttpRequestMethodGet(args->context)) { case HTTP_GET: - response = JsonDuplicate(DbJson(config->ref)); + response = JsonDuplicate(DbJson(config.ref)); break; case HTTP_POST: request = JsonDecode(HttpServerStream(args->context)); @@ -91,18 +90,10 @@ ROUTE_IMPL(RouteConfig, path, argp) break; } - newConf = ConfigParse(request); - if (!newConf) + ConfigParse(request, &newConf); + if (newConf.ok) { - msg = "Internal server error while parsing config."; - HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); - response = MatrixErrorCreate(M_UNKNOWN, msg); - break; - } - - if (newConf->ok) - { - if (DbJsonSet(config->ref, request)) + if (DbJsonSet(config.ref, request)) { response = HashMapCreate(); /* @@ -121,10 +112,10 @@ ROUTE_IMPL(RouteConfig, path, argp) else { HttpResponseStatus(args->context, HTTP_BAD_REQUEST); - response = MatrixErrorCreate(M_BAD_JSON, newConf->err); + response = MatrixErrorCreate(M_BAD_JSON, newConf.err); } - ConfigFree(newConf); + ConfigFree(&newConf); JsonFree(request); break; case HTTP_PUT: @@ -136,22 +127,14 @@ ROUTE_IMPL(RouteConfig, path, argp) break; } - newJson = JsonDuplicate(DbJson(config->ref)); + newJson = JsonDuplicate(DbJson(config.ref)); JsonMerge(newJson, request); - newConf = ConfigParse(newJson); + ConfigParse(newJson, &newConf); - if (!newConf) + if (newConf.ok) { - msg = "Internal server error while parsing config."; - HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); - response = MatrixErrorCreate(M_UNKNOWN, msg); - break; - } - - if (newConf->ok) - { - if (DbJsonSet(config->ref, newJson)) + if (DbJsonSet(config.ref, newJson)) { response = HashMapCreate(); /* @@ -170,10 +153,10 @@ ROUTE_IMPL(RouteConfig, path, argp) else { HttpResponseStatus(args->context, HTTP_BAD_REQUEST); - response = MatrixErrorCreate(M_BAD_JSON, newConf->err); + response = MatrixErrorCreate(M_BAD_JSON, newConf.err); } - ConfigFree(newConf); + ConfigFree(&newConf); JsonFree(request); JsonFree(newJson); break; @@ -186,6 +169,6 @@ ROUTE_IMPL(RouteConfig, path, argp) finish: UserUnlock(user); - ConfigUnlock(config); + ConfigUnlock(&config); return response; } diff --git a/src/Routes/RouteDeactivate.c b/src/Routes/RouteDeactivate.c index 6a21f70..3c0bf74 100644 --- a/src/Routes/RouteDeactivate.c +++ b/src/Routes/RouteDeactivate.c @@ -46,17 +46,19 @@ ROUTE_IMPL(RouteDeactivate, path, argp) Db *db = args->matrixArgs->db; User *user = NULL; - Config *config = ConfigLock(db); + Config config; + char *msg; (void) path; - if (!config) + ConfigLock(db, &config); + if (!config.ok) { - Log(LOG_ERR, "Deactivate endpoint failed to lock configuration."); + Log(LOG_ERR, "%s", config.err); HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); - response = MatrixErrorCreate(M_UNKNOWN, NULL); + response = MatrixErrorCreate(M_UNKNOWN, config.err); goto finish; } @@ -149,6 +151,6 @@ ROUTE_IMPL(RouteDeactivate, path, argp) finish: JsonFree(request); UserUnlock(user); - ConfigUnlock(config); + ConfigUnlock(&config); return response; } diff --git a/src/Routes/RouteFilter.c b/src/Routes/RouteFilter.c index 13e482a..c2ead37 100644 --- a/src/Routes/RouteFilter.c +++ b/src/Routes/RouteFilter.c @@ -40,16 +40,17 @@ GetServerName(Db * db) { char *name; - Config *config = ConfigLock(db); + Config config; - if (!config) + ConfigLock(db, &config); + if (!config.ok) { return NULL; } - name = StrDuplicate(config->serverName); + name = StrDuplicate(config.serverName); - ConfigUnlock(config); + ConfigUnlock(&config); return name; } diff --git a/src/Routes/RouteLogin.c b/src/Routes/RouteLogin.c index d689cd2..158629d 100644 --- a/src/Routes/RouteLogin.c +++ b/src/Routes/RouteLogin.c @@ -65,13 +65,14 @@ ROUTE_IMPL(RouteLogin, path, argp) char *msg; - Config *config = ConfigLock(db); + Config config; - if (!config) + ConfigLock(db, &config); + if (!config.ok) { - Log(LOG_ERR, "Login endpoint failed to lock configuration."); + Log(LOG_ERR, "%s", config.err); HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); - return MatrixErrorCreate(M_UNKNOWN, NULL); + return MatrixErrorCreate(M_UNKNOWN, config.err); } (void) path; @@ -150,7 +151,7 @@ ROUTE_IMPL(RouteLogin, path, argp) } - userId = UserIdParse(userIdentifier.user, config->serverName); + userId = UserIdParse(userIdentifier.user, config.serverName); if (!userId) { msg = "Invalid user ID."; @@ -159,7 +160,7 @@ ROUTE_IMPL(RouteLogin, path, argp) break; } - if (!StrEquals(userId->server, config->serverName) + if (!StrEquals(userId->server, config.serverName) || !UserExists(db, userId->localpart)) { msg = "Unknown user ID."; @@ -222,13 +223,13 @@ ROUTE_IMPL(RouteLogin, path, argp) } fullUsername = StrConcat(4, "@", UserGetName(user), ":", - config->serverName); + config.serverName); HashMapSet(response, "user_id", JsonValueString(fullUsername)); Free(fullUsername); HashMapSet(response, "well_known", JsonValueObject( - MatrixClientWellKnown(config->baseUrl, config->identityServer))); + MatrixClientWellKnown(config.baseUrl, config.identityServer))); UserAccessTokenFree(loginInfo->accessToken); Free(loginInfo->refreshToken); @@ -246,7 +247,7 @@ ROUTE_IMPL(RouteLogin, path, argp) UserIdFree(userId); JsonFree(request); - ConfigUnlock(config); + ConfigUnlock(&config); LoginRequestFree(&loginRequest); LoginRequestUserIdentifierFree(&userIdentifier); diff --git a/src/Routes/RouteRegister.c b/src/Routes/RouteRegister.c index 6dbf859..49c0104 100644 --- a/src/Routes/RouteRegister.c +++ b/src/Routes/RouteRegister.c @@ -73,7 +73,7 @@ ROUTE_IMPL(RouteRegister, path, argp) char *session; DbRef *sessionRef; - Config *config = ConfigLock(db); + Config config; regReq.username = NULL; regReq.password = NULL; @@ -82,15 +82,12 @@ ROUTE_IMPL(RouteRegister, path, argp) regReq.refresh_token = 0; regReq.inhibit_login = 0; - - - - if (!config) + ConfigLock(db, &config); + if (!config.ok) { - msg = "Internal server error while locking configuration."; - Log(LOG_ERR, "Registration endpoint failed to lock configuration."); + Log(LOG_ERR, "%s", config.err); HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); - return MatrixErrorCreate(M_UNKNOWN, msg); + return MatrixErrorCreate(M_UNKNOWN, config.err); } if (ArraySize(path) == 0) @@ -118,7 +115,7 @@ ROUTE_IMPL(RouteRegister, path, argp) if (regReq.username) { - if (!UserValidate(regReq.username, config->serverName)) + if (!UserValidate(regReq.username, config.serverName)) { HttpResponseStatus(args->context, HTTP_BAD_REQUEST); response = MatrixErrorCreate(M_INVALID_USERNAME, NULL); @@ -136,7 +133,7 @@ ROUTE_IMPL(RouteRegister, path, argp) uiaFlows = ArrayCreate(); ArrayAdd(uiaFlows, RouteRegisterRegFlow()); - if (config->flags & CONFIG_REGISTRATION) + if (config.registration) { ArrayAdd(uiaFlows, UiaDummyFlow()); } @@ -183,7 +180,7 @@ ROUTE_IMPL(RouteRegister, path, argp) response = HashMapCreate(); fullUsername = StrConcat(4, - "@", UserGetName(user), ":", config->serverName); + "@", UserGetName(user), ":", config.serverName); HashMapSet(response, "user_id", JsonValueString(fullUsername)); Free(fullUsername); @@ -260,7 +257,7 @@ finish: HttpResponseStatus(args->context, HTTP_BAD_REQUEST); response = MatrixErrorCreate(M_MISSING_PARAM, msg); } - else if (!UserValidate(username, config->serverName)) + else if (!UserValidate(username, config.serverName)) { HttpResponseStatus(args->context, HTTP_BAD_REQUEST); response = MatrixErrorCreate(M_INVALID_USERNAME, NULL); @@ -284,6 +281,6 @@ finish: } end: - ConfigUnlock(config); + ConfigUnlock(&config); return response; } diff --git a/src/Routes/RouteUiaFallback.c b/src/Routes/RouteUiaFallback.c index 71ee1ed..3ae6d44 100644 --- a/src/Routes/RouteUiaFallback.c +++ b/src/Routes/RouteUiaFallback.c @@ -52,23 +52,22 @@ ROUTE_IMPL(RouteUiaFallback, path, argp) HashMap *request; HashMap *response; int uiaResult; - Config *config; + Config config; Array *flows; Array *flow; - config = ConfigLock(args->matrixArgs->db); - if (!config) + ConfigLock(args->matrixArgs->db, &config); + if (!config.ok) { - msg = "Internal server error: failed to lock configuration."; - Log(LOG_ERR, "UIA fallback failed to lock configuration."); + Log(LOG_ERR, "%s", config.err); HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); - return MatrixErrorCreate(M_UNKNOWN, msg); + return MatrixErrorCreate(M_UNKNOWN, config.err); } request = JsonDecode(HttpServerStream(args->context)); if (!request) { - ConfigUnlock(config); + ConfigUnlock(&config); HttpResponseStatus(args->context, HTTP_BAD_REQUEST); return MatrixErrorCreate(M_NOT_JSON, NULL); } @@ -92,7 +91,7 @@ ROUTE_IMPL(RouteUiaFallback, path, argp) } JsonFree(request); - ConfigUnlock(config); + ConfigUnlock(&config); return response; } else if (HttpRequestMethodGet(args->context) != HTTP_GET) diff --git a/src/Routes/RouteUserProfile.c b/src/Routes/RouteUserProfile.c index 16a310c..5c1af7b 100644 --- a/src/Routes/RouteUserProfile.c +++ b/src/Routes/RouteUserProfile.c @@ -51,16 +51,18 @@ ROUTE_IMPL(RouteUserProfile, path, argp) char *msg; - Config *config = ConfigLock(db); + Config config; - if (!config) + ConfigLock(db, &config); + + if (!config.ok) { - Log(LOG_ERR, "User profile endpoint failed to lock configuration."); + Log(LOG_ERR, "%s", config.err); HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); - return MatrixErrorCreate(M_UNKNOWN, NULL); + return MatrixErrorCreate(M_UNKNOWN, config.err); } - serverName = config->serverName; + serverName = config.serverName; username = ArrayGet(path, 0); userId = UserIdParse(username, serverName); @@ -181,11 +183,9 @@ ROUTE_IMPL(RouteUserProfile, path, argp) break; } finish: - ConfigUnlock(config); + ConfigUnlock(&config); - /* Username is handled by the router, freeing it *will* cause issues - * (see #33). I honestly don't know how it didn't come to bite us sooner. - Free(username); */ + /* Username is handled by the router, freeing it would cause issues. */ Free(entry); UserIdFree(userId); UserUnlock(user); diff --git a/src/Routes/RouteWellKnown.c b/src/Routes/RouteWellKnown.c index a11aaa8..19c0f63 100644 --- a/src/Routes/RouteWellKnown.c +++ b/src/Routes/RouteWellKnown.c @@ -36,21 +36,19 @@ ROUTE_IMPL(RouteWellKnown, path, argp) RouteArgs *args = argp; HashMap *response; - Config *config = ConfigLock(args->matrixArgs->db); + Config config; - char *msg; - - if (!config) + ConfigLock(args->matrixArgs->db, &config); + if (!config.ok) { - Log(LOG_ERR, "Well-known endpoint failed to lock configuration."); - msg = "Internal server error: couldn't lock database."; + Log(LOG_ERR, "%s", config.err); HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); - return MatrixErrorCreate(M_UNKNOWN, msg); + return MatrixErrorCreate(M_UNKNOWN, config.err); } if (StrEquals(ArrayGet(path, 0), "client")) { - response = MatrixClientWellKnown(config->baseUrl, config->identityServer); + response = MatrixClientWellKnown(config.baseUrl, config.identityServer); } else { @@ -58,6 +56,6 @@ ROUTE_IMPL(RouteWellKnown, path, argp) response = MatrixErrorCreate(M_NOT_FOUND, NULL); } - ConfigUnlock(config); + ConfigUnlock(&config); return response; } diff --git a/src/Routes/RouteWhoami.c b/src/Routes/RouteWhoami.c index 31eade1..b98df15 100644 --- a/src/Routes/RouteWhoami.c +++ b/src/Routes/RouteWhoami.c @@ -45,14 +45,15 @@ ROUTE_IMPL(RouteWhoami, path, argp) char *deviceID; char *msg; - Config *config = ConfigLock(db); + Config config; - if (!config) + ConfigLock(db, &config); + + if (!config.ok) { - msg = "Internal server error: couldn't lock database."; - Log(LOG_ERR, "Who am I endpoint failed to lock configuration."); + Log(LOG_ERR, "%s", config.err); HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); - return MatrixErrorCreate(M_UNKNOWN, msg); + return MatrixErrorCreate(M_UNKNOWN, config.err); } (void) path; @@ -76,7 +77,7 @@ ROUTE_IMPL(RouteWhoami, path, argp) response = HashMapCreate(); - userID = StrConcat(4, "@", UserGetName(user), ":", config->serverName); + userID = StrConcat(4, "@", UserGetName(user), ":", config.serverName); deviceID = StrDuplicate(UserGetDeviceId(user)); UserUnlock(user); @@ -88,6 +89,6 @@ ROUTE_IMPL(RouteWhoami, path, argp) Free(deviceID); finish: - ConfigUnlock(config); + ConfigUnlock(&config); return response; } diff --git a/src/Uia.c b/src/Uia.c index 3a71d24..2c0abb8 100644 --- a/src/Uia.c +++ b/src/Uia.c @@ -206,7 +206,7 @@ UiaStageBuild(char *type, HashMap * params) int UiaComplete(Array * flows, HttpServerContext * context, Db * db, - HashMap * request, HashMap ** response, Config * config) + HashMap * request, HashMap ** response, Config config) { JsonValue *val; HashMap *auth; @@ -363,10 +363,10 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db, type = JsonValueAsString(HashMapGet(identifier, "type")); userId = UserIdParse(JsonValueAsString(HashMapGet(identifier, "user")), - config->serverName); + config.serverName); if (!type || !StrEquals(type, "m.id.user") - || !userId || !StrEquals(userId->server, config->serverName)) + || !userId || !StrEquals(userId->server, config.serverName)) { HttpResponseStatus(context, HTTP_UNAUTHORIZED); ret = BuildResponse(flows, db, response, session, dbRef); diff --git a/src/include/Config.h b/src/include/Config.h index 73052cd..6d925b7 100644 --- a/src/include/Config.h +++ b/src/include/Config.h @@ -49,68 +49,12 @@ * .Xr telodendria-config 7 . */ +#include + #include #include #include -/** - * 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 * them, and adding them to the configuration structure for use by the @@ -121,14 +65,7 @@ typedef struct Config * set the ok flag to 0. The caller should always check the ok flag, * and if there is an error, it should display the error to the user. */ -extern Config * ConfigParse(HashMap *); - -/** - * Free all the values inside of the given configuration structure, - * as well as the structure itself, such that it is completely invalid - * when this function returns. - */ -extern void ConfigFree(Config *); +extern void ConfigParse(HashMap *, Config *); /** * Check whether or not the configuration exists in the database, @@ -151,7 +88,7 @@ extern int ConfigCreateDefault(Db *); * The return value of this function is the same as * .Fn ConfigParse . */ -extern Config * ConfigLock(Db *); +extern void ConfigLock(Db *, Config *); /** * Unlock the specified configuration, returning it back to the @@ -161,4 +98,9 @@ extern Config * ConfigLock(Db *); */ extern int ConfigUnlock(Config *); +/** + * Converts a ConfigLogLevel into a valid syslog level. + */ +extern int ConfigLogLevelToSyslog(ConfigLogLevel); + #endif /* TELODENDRIA_CONFIG_H */ diff --git a/src/include/Uia.h b/src/include/Uia.h index 268ec77..91b4e0a 100644 --- a/src/include/Uia.h +++ b/src/include/Uia.h @@ -133,7 +133,7 @@ extern void UiaCleanup(MatrixHttpHandlerArgs *); * the caller proceed with its logic. */ extern int - UiaComplete(Array *, HttpServerContext *, Db *, HashMap *, HashMap **, Config *); + UiaComplete(Array *, HttpServerContext *, Db *, HashMap *, HashMap **, Config); /** * Free an array of flows, as described above. Even though the caller