diff --git a/src/config.c b/src/config.c index 2c2a6a9..08cff81 100644 --- a/src/config.c +++ b/src/config.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "config.h" #include "logging.h" @@ -33,6 +34,9 @@ void replaceOrAdd(void** array, int* length, void* old, void* new) { } struct config* config_parse(FILE* file) { + // logger is not yet set but we need error messages + setLogging(stdout, ERROR, true); + void* toFree[LENGTH_OF_TOFREE_ARRAY]; int toFreeLength = 0; @@ -45,9 +49,12 @@ struct config* config_parse(FILE* file) { replaceOrAdd(toFree, &toFreeLength, NULL, config); config->nrBinds = 0; config->binds = NULL; + config->logging.accessLogfile = NULL; + config->logging.serverLogfile = NULL; + config->logging.serverVerbosity = CONFIG_DEFAULT_LOGLEVEL; - #define BIND (0) + #define ROOT (0) #define BIND_VALUE (10) #define BIND_BRACKETS_OPEN (11) #define BIND_CONTENT (12) @@ -70,7 +77,15 @@ struct config* config_parse(FILE* file) { #define TYPE_VALUE (1464) #define INDEX_EQUALS (1465) #define INDEX_VALUE (1466) - int state = BIND; + #define LOGGING_BRACKETS_OPEN (20) + #define LOGGING_CONTENT (21) + #define LOGGING_ACCESS_FILE_EQUALS (22) + #define LOGGING_ACCESS_FILE_VALUE (23) + #define LOGGING_SERVER_FILE_EQUALS (24) + #define LOGGING_SERVER_FILE_VALUE (25) + #define LOGGING_SERVER_VERBOSITY_EQUALS (26) + #define LOGGING_SERVER_VERBOSITY_VALUE (27) + int state = ROOT; struct config_bind* currentBind = NULL; struct config_site* currentSite = NULL; @@ -97,44 +112,46 @@ struct config* config_parse(FILE* file) { char** tmpArray; switch(state) { - case BIND: - if (strcmp(currentToken, "bind") != 0) { - error("config: Unexpected token '%s' on line %d. 'bind' expected", currentToken, currentLine); + case ROOT: + if (strcmp(currentToken, "bind") == 0) { + currentBind = malloc(sizeof(struct config_bind)); + if (currentBind == NULL) { + error("config: couldn't allocate for bind struct: %s", strerror(errno)); + freeEverything(toFree, toFreeLength); + return NULL; + } + replaceOrAdd(toFree, &toFreeLength, NULL, currentBind); + + config->nrBinds++; + struct config_bind** tmpBind = realloc(config->binds, config->nrBinds * sizeof(struct config_bind*)); + if (tmpBind == NULL) { + error("config: couldn't reallocate for bind array: %s", strerror(errno)); + freeEverything(toFree, toFreeLength); + return NULL; + } + + replaceOrAdd(toFree, &toFreeLength, config->binds, tmpBind); + config->binds = tmpBind; + config->binds[config->nrBinds - 1] = currentBind; + + currentBind->nrSites = 0; + currentBind->sites = NULL; + currentBind->addr = NULL; + currentBind->port = NULL; + + #ifdef SSL_SUPPORT + currentBind->ssl = NULL; + #endif + + state = BIND_VALUE; + } else if (strcmp(currentToken, "logging") == 0) { + state = LOGGING_BRACKETS_OPEN; + } else { + error("config: Unexpected token '%s' on line %d.", currentToken, currentLine); freeEverything(toFree, toFreeLength); return NULL; } - currentBind = malloc(sizeof(struct config_bind)); - if (currentBind == NULL) { - error("config: couldn't allocate for bind struct: %s", strerror(errno)); - freeEverything(toFree, toFreeLength); - return NULL; - } - replaceOrAdd(toFree, &toFreeLength, NULL, currentBind); - - config->nrBinds++; - struct config_bind** tmpBind = realloc(config->binds, config->nrBinds * sizeof(struct config_bind*)); - if (tmpBind == NULL) { - error("config: couldn't reallocate for bind array: %s", strerror(errno)); - freeEverything(toFree, toFreeLength); - return NULL; - } - - replaceOrAdd(toFree, &toFreeLength, config->binds, tmpBind); - config->binds = tmpBind; - config->binds[config->nrBinds - 1] = currentBind; - - currentBind->nrSites = 0; - currentBind->sites = NULL; - currentBind->addr = NULL; - currentBind->port = NULL; - - #ifdef SSL_SUPPORT - currentBind->ssl = NULL; - #endif - - state = BIND_VALUE; - break; case BIND_VALUE: ; char* seperator = strstr(currentToken, ":"); @@ -212,7 +229,7 @@ struct config* config_parse(FILE* file) { state = SITE_BRACKETS_OPEN; } else if (strcmp(currentToken, "}") == 0) { - state = BIND; + state = ROOT; } else if (strcmp(currentToken, "ssl") == 0) { #ifdef SSL_SUPPORT if (currentBind->ssl != NULL) { @@ -502,6 +519,84 @@ struct config* config_parse(FILE* file) { state = HANDLER_CONTENT; break; + case LOGGING_BRACKETS_OPEN: + if (strcmp(currentToken, "{") != 0) { + error("config: Unexpected token '%s' on line %d. '{' expected", currentToken, currentLine); + freeEverything(toFree, toFreeLength); + return NULL; + } + + state = LOGGING_CONTENT; + break; + case LOGGING_CONTENT: + if (strcmp(currentToken, "access") == 0) { + state = LOGGING_ACCESS_FILE_EQUALS; + } else if (strcmp(currentToken, "server") == 0) { + state = LOGGING_SERVER_FILE_EQUALS; + } else if (strcmp(currentToken, "verbosity") == 0) { + state = LOGGING_SERVER_VERBOSITY_EQUALS; + } else if (strcmp(currentToken, "}") == 0) { + state = ROOT; + } else { + error("config: Unknown property '%s' on line %d.", currentToken, currentLine); + freeEverything(toFree, toFreeLength); + return NULL; + } + break; + case LOGGING_ACCESS_FILE_EQUALS: + if (strcmp(currentToken, "=") != 0) { + error("config: Unexpected token '%s' on line %d. '=' expected", currentToken, currentLine); + freeEverything(toFree, toFreeLength); + return NULL; + } + state = LOGGING_ACCESS_FILE_VALUE; + break; + case LOGGING_ACCESS_FILE_VALUE: + config->logging.accessLogfile = strclone(currentToken); + if (config->logging.accessLogfile == NULL) { + error("config: error cloning access log file string"); + freeEverything(toFree, toFreeLength); + return NULL; + } + + state = LOGGING_CONTENT; + break; + case LOGGING_SERVER_FILE_EQUALS: + if (strcmp(currentToken, "=") != 0) { + error("config: Unexpected token '%s' on line %d. '=' expected", currentToken, currentLine); + freeEverything(toFree, toFreeLength); + return NULL; + } + state = LOGGING_SERVER_FILE_VALUE; + break; + case LOGGING_SERVER_FILE_VALUE: + config->logging.serverLogfile = strclone(currentToken); + if (config->logging.serverLogfile == NULL) { + error("config: error cloning server log file string"); + freeEverything(toFree, toFreeLength); + return NULL; + } + + state = LOGGING_CONTENT; + break; + case LOGGING_SERVER_VERBOSITY_EQUALS: + if (strcmp(currentToken, "=") != 0) { + error("config: Unexpected token '%s' on line %d. '=' expected", currentToken, currentLine); + freeEverything(toFree, toFreeLength); + return NULL; + } + state = LOGGING_SERVER_VERBOSITY_VALUE; + break; + case LOGGING_SERVER_VERBOSITY_VALUE: + config->logging.serverVerbosity = strtologlevel(currentToken); + + if (config->logging.serverVerbosity == UNKNOWN) { + error("config: Unexpected token '%s' on line %d.", currentToken, currentLine); + freeEverything(toFree, toFreeLength); + return NULL; + } + state = LOGGING_CONTENT; + break; default: assert(false); } @@ -516,7 +611,7 @@ struct config* config_parse(FILE* file) { } } - if (state != BIND) { + if (state != ROOT) { error("config: unexpected EOF"); freeEverything(toFree, toFreeLength); return NULL; @@ -582,6 +677,29 @@ struct networkingConfig config_getNetworkingConfig(struct config* config) { return networkingConfig; } + +void config_setLogging(struct config* config) { + setLogging(stdout, config->logging.serverVerbosity, true); + + if (config->logging.serverLogfile != NULL) { + FILE* file = fopen(config->logging.serverLogfile, "a"); + if (file == NULL) { + error("config: failed to open server log file %s: %s", config->logging.serverLogfile, strerror(errno)); + } else { + setLogging(file, config->logging.serverVerbosity, false); + } + } + + if (config->logging.accessLogfile != NULL) { + FILE* file = fopen(config->logging.accessLogfile, "a"); + if (file == NULL) { + error("config: failed to open access log file %s: %s", config->logging.accessLogfile, strerror(errno)); + } else { + setLogging(file, HTTP_ACCESS, false); + } + } +} + struct handler config_getHandler(struct config* config, struct metaData metaData, const char* host, struct bind* bind) { struct handler handler = {}; diff --git a/src/config.h b/src/config.h index 907278c..8ddcc9d 100644 --- a/src/config.h +++ b/src/config.h @@ -7,11 +7,14 @@ #include "misc.h" #include "files.h" #include "cgi.h" +#include "logging.h" #ifdef SSL_SUPPORT #include "ssl.h" #endif +#define CONFIG_DEFAULT_LOGLEVEL (DEFAULT_LOGLEVEL) + struct config { int nrBinds; struct config_bind { @@ -39,6 +42,11 @@ struct config { } **handlers; } **sites; } **binds; + struct config_logging { + char* accessLogfile; + char* serverLogfile; + loglevel_t serverVerbosity; + } logging; }; /* @@ -59,6 +67,11 @@ bind [addr]:[port] { } } } +logging { + access = file + server = file + verboseity = debug|info|warn|error +} */ @@ -66,6 +79,7 @@ bind [addr]:[port] { struct config* config_parse(FILE* file); struct networkingConfig config_getNetworkingConfig(struct config* config); +void config_setLogging(struct config* config); struct handler config_getHandler(struct config* config, struct metaData metaData, const char* host, struct bind* bind); void config_destroy(struct config* config); diff --git a/src/logging.c b/src/logging.c index 804a6a5..8476b34 100644 --- a/src/logging.c +++ b/src/logging.c @@ -290,3 +290,20 @@ void critical(const char* format, ...) { vlogging(CRITICAL, format, argptr); va_end(argptr); } + + +loglevel_t strtologlevel(const char* string) { + if (strcasecmp(string, "debug") == 0) { + return DEBUG; + } else if (strcasecmp(string, "info") == 0) { + return INFO; + } else if (strcasecmp(string, "warn") == 0) { + return WARN; + } else if (strcasecmp(string, "error") == 0) { + return ERROR; + } else if (strcasecmp(string, "critical") == 0) { + return CRITICAL; + } else { + return UNKNOWN; + } +} diff --git a/src/logging.h b/src/logging.h index e70f132..919ad40 100644 --- a/src/logging.h +++ b/src/logging.h @@ -7,6 +7,7 @@ typedef int loglevel_t; +#define UNKNOWN (-1) #define DEBUG (0) #define VERBOSE (1) #define INFO (2) @@ -35,4 +36,6 @@ void warn(const char* format, ...); void error(const char* format, ...); void critical(const char* format, ...); +loglevel_t strtologlevel(const char* string); + #endif diff --git a/src/test.c b/src/test.c index 736a542..181f036 100644 --- a/src/test.c +++ b/src/test.c @@ -269,6 +269,10 @@ void testConfig() { checkString(config->binds[0]->sites[0]->handlers[0]->settings.fileSettings.documentRoot, "/var/www", "handler settings root check"); checkInt(config->binds[0]->sites[0]->handlers[0]->settings.fileSettings.indexfiles.number, 1, "handler settings index no"); checkString(config->binds[0]->sites[0]->handlers[0]->settings.fileSettings.indexfiles.files[0], "index.html", "handler settings index check"); + checkString(config->logging.accessLogfile, "access.log", "access log file check"); + checkString(config->logging.serverLogfile, "server.log", "server log file check"); + printf("%s\n", config->logging.serverLogfile); + checkInt(config->logging.serverVerbosity, INFO, "server log verbosity check"); #ifdef SSL_SUPPORT checkString(config->binds[1]->addr, "0.0.0.0", "bind addr check"); @@ -306,15 +310,12 @@ void test(const char* name, void (*testFunction)()) { } int main(int argc, char** argv) { - /**test("logging", &testLogging); + test("config", &testConfig); test("util", &testUtil); test("linked lists", &testLinkedList); test("signals", &testTimers); - test("headers", &testHeaders);*/ - - setLogging(stderr, DEFAULT_LOGLEVEL, true); - - test("config", &testConfig); + test("headers", &testHeaders); + test("logging", &testLogging); printf("\nOverall: %s\n", overall ? "OK" : "FAILED"); diff --git a/test-with-ssl.conf b/test-with-ssl.conf index b2e28cc..8929070 100644 --- a/test-with-ssl.conf +++ b/test-with-ssl.conf @@ -22,3 +22,8 @@ bind 0.0.0.0:443 { } } } +logging { + access = access.log + server = server.log + verbosity = info +} diff --git a/test.conf b/test.conf index 2cdc5fd..504d574 100644 --- a/test.conf +++ b/test.conf @@ -8,3 +8,8 @@ bind 0.0.0.0:80 { } } } +logging { + access = access.log + server = server.log + verbosity = info +}