src/server/daemon/config.c

Wed, 02 Jan 2013 16:03:50 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 02 Jan 2013 16:03:50 +0100
changeset 45
a24aa388f02f
parent 44
3da1f7b6847f
child 46
636e05eb48f6
permissions
-rw-r--r--

added access log

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2013 Olaf Wintermann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "../public/nsapi.h"

#include <stdio.h>
#include <stdlib.h>

#include <fcntl.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include "../ucx/string.h"

#include "httplistener.h"
#include "config.h"
#include "func.h"
#include "log.h"
#include "event.h"
#include "threadpools.h"
#include "ldap_auth.h"
#include "configmanager.h"

#include "vserver.h"
#include "../util/pblock.h"
#include "../util/util.h"
#include "../util/atomic.h"

pool_handle_t *cfg_pool;

// TODO: Funktion für ConfigDirective -> directive
// TODO: Funktion für UcxList parameter list -> pblock

void load_init_conf(char *file) {
    printf("load_init_conf\n");

    InitConfig *cfg = load_init_config(file);
    UcxMempool *mp = cfg->parser.mp;
    if(cfg == NULL) {
        return;
    }

    cfg_pool = pool_create(); // one pool for one Configuration
    UcxDlist *dirs = cfg->directives;
    while(dirs != NULL) {
        ConfigDirective *dir = dirs->data;

        /* create NSAPI directive */
        directive *d = malloc(sizeof(directive));
        d->param = pblock_create_pool(cfg_pool, 8);
        UcxList *param = cfg_param_list(dir->value, mp);
        while(param != NULL) {
            ConfigParam *p = param->data;
            pblock_nvlinsert(
                    p->name.ptr,
                    p->name.length,
                    p->value.ptr,
                    p->value.length,
                    d->param);
            
            param = param->next;
        }

        /* get function */
        char *func_name = pblock_findval("fn", d->param);
        d->func = get_function(func_name);
        if(d->func == NULL) {
            free(d);
            dirs = dirs->next;
            continue;
        }

        /* execute init directive */
        d->func->func(d->param, NULL, NULL);

        dirs = dirs->next;
    }
    
    free_init_config(cfg);
}

ServerConfiguration* load_server_conf(ServerConfiguration *old, char *file) {
    printf("load_server_conf\n");

    ServerConfig *serverconf = load_server_config(file);
    if(serverconf == NULL) {
        fprintf(stderr, "Cannot load server.conf\n");
    }
    ServerConfiguration *serverconfig = malloc(sizeof(ServerConfiguration));
    serverconfig->ref = 1;
    serverconfig->pool = pool_create();
    serverconfig->listeners = NULL;
    serverconfig->host_vs = ucx_map_new(16);
    serverconfig->authdbs = ucx_map_new(16);
    // TODO: init serverconfig stuff
    
    
    /*
     * convert ServerConfig to ServerConfiguration
     * 
     * its important to do this in the correct order:
     * LogFile (open log file first to log possible errors)
     * Runtime
     * Threadpool
     * EventHandler
     * AuthDB
     * Listener (we set the VirtualServer later)
     * VirtualServer (dependencies: Listener)
     */
    
    /* init logfile first */
    UcxList *lfl = ucx_map_sstr_get(serverconf->objects, sstrn("LogFile", 7));
    if(lfl != NULL) {
        ServerConfigObject *logobj = lfl->data;
        if(logobj == NULL) {
            /* error */
            return NULL;
        }
        
        int ret = cfg_handle_logfile(serverconfig, logobj);
        if(ret != 0) {
            /* cannot initialize log file */
            return NULL;
        }
    } else {
        /* horrible error */
        return NULL;
    }
    
    UcxList *list = ucx_map_sstr_get(serverconf->objects, sstrn("Runtime", 7));
    UCX_FOREACH(UcxList*, list, elm) {
        ServerConfigObject *scfgobj = elm->data;
        cfg_handle_runtime(serverconfig, scfgobj);
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("Threadpool", 10));
    UCX_FOREACH(UcxList*, list, elm) {
        cfg_handle_threadpool(serverconfig, elm->data);
    }
    // check thread pool config
    if(check_thread_pool_cfg() != 0) {
        /* critical error */
        return NULL;
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("EventHandler", 12));
    UCX_FOREACH(UcxList*, list, elm) {
        cfg_handle_eventhandler(serverconfig, (ServerConfigObject*)elm->data);
    }
    // check event handler config
    if(check_event_handler_cfg() != 0) {
        /* critical error */
        return NULL;
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("AccessLog", 9));
    UCX_FOREACH(UcxList*, list, elm) {
        ServerConfigObject *scfgobj = elm->data;
        cfg_handle_accesslog(serverconfig, scfgobj);
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("AuthDB", 6));
    UCX_FOREACH(UcxList*, list, elm) {
        ServerConfigObject *scfgobj = elm->data;
        cfg_handle_authdb(serverconfig, scfgobj);
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("Listener", 8));
    UCX_FOREACH(UcxList*, list, elm) {
        ServerConfigObject *scfgobj = elm->data;
        cfg_handle_listener(serverconfig, scfgobj);
    }
    
    list = ucx_map_sstr_get(serverconf->objects, sstrn("VirtualServer", 13));
    UCX_FOREACH(UcxList*, list, elm) {
        ServerConfigObject *scfgobj = elm->data;
        cfg_handle_vs(serverconfig, scfgobj);
    }
    

    // set VirtualServer for all listeners
    UcxList *ls = serverconfig->listeners;
    while(ls) {
        HttpListener *listener = ls->data;

        sstr_t vsname = sstr(listener->default_vs.vs_name);

        // search for VirtualServer
        //int b = 0;
        UcxMapIterator iter = ucx_map_iterator(serverconfig->host_vs);
        VirtualServer *vs;
        UCX_MAP_FOREACH(vs, iter) {
            if(!sstrcmp(vsname, vs->name)) {
                listener->default_vs.vs = vs;
                break;
            }
        }

        ls = ls->next;
    }

    return serverconfig;
}

void cfg_ref(ServerConfiguration *cfg) {
    ws_atomic_inc32(&cfg->ref);
}

void cfg_unref(ServerConfiguration *cfg) {
    uint32_t ref = ws_atomic_dec32(&cfg->ref);
    if(ref == 0) {
        // TODO: free configuration
    }
}


void init_server_config_parser() {
    
}

int cfg_handle_runtime(ServerConfiguration *cfg, ServerConfigObject *obj) {
    cfg->user = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("User")));
    cfg->tmp = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("Temp")));
    
    // mime file
    sstr_t mf = cfg_directivelist_get_str(obj->directives, sstr("MimeFile"));
    
    sstr_t base = sstr("config/");
    sstr_t file;
    file.length = base.length + mf.length;
    file.ptr = alloca(file.length + 1);
    file.ptr[file.length] = 0;
    file = sstrncat(2, file, base, mf);
    
    ConfigFile *f = cfgmgr_get_file(file);
    if(f == NULL) {
        f = malloc(sizeof(ConfigFile));
        f->file = sstrdup(file);
        f->reload = mime_conf_reload;
        
        // load the file content
        f->reload(f, cfg);
        cfgmgr_attach_file(f);
    }
    
    cfg->mimetypes = f->data; // TODO: ref
    
    return 0;
}

int cfg_handle_logfile(ServerConfiguration *cfg, ServerConfigObject *obj) {
    sstr_t file = cfg_directivelist_get_str(obj->directives, sstr("File"));
    sstr_t lvl = cfg_directivelist_get_str(obj->directives, sstr("Level"));
    
    if(file.ptr == NULL || lvl.ptr == NULL) {
        /* missing log file parameters */
        return -1;
    }
    
    LogConfig logcfg;
    logcfg.file = sstrdup(file).ptr;
    logcfg.level = sstrdup(lvl).ptr;
    /* TODO: stdout, stderr config */
    
    int ret = init_log_file(&logcfg);
    
    free(logcfg.file);
    free(logcfg.level);

    return ret;
}

int cfg_handle_threadpool(ServerConfiguration *cfg, ServerConfigObject *obj) {
    ThreadPoolConfig poolcfg;
    poolcfg.min_threads = 4;
    poolcfg.min_threads = 4;
    poolcfg.max_threads = 8;
    poolcfg.queue_size = 64;
    poolcfg.stack_size = 262144;
    
    sstr_t name = cfg_directivelist_get_str(
            obj->directives,
            sstr("Name"));
    sstr_t min = cfg_directivelist_get_str(
            obj->directives,
            sstr("MinThreads"));
    sstr_t max = cfg_directivelist_get_str(
            obj->directives,
            sstr("MaxThreads"));
    sstr_t stack = cfg_directivelist_get_str(
            obj->directives,
            sstr("StackSize"));
    sstr_t queue = cfg_directivelist_get_str(
            obj->directives,
            sstr("QueueSize"));
    // TODO: Type
    
    if(name.length == 0) {
        // TODO: log error
        return 1;
    }
    
    if(min.length != 0) {
        min = sstrdup(min);
        poolcfg.min_threads = atoi(min.ptr);
        free(min.ptr);
    }
    
    if(max.length != 0) {
        max = sstrdup(max);
        poolcfg.max_threads = atoi(max.ptr);
        free(max.ptr);
    }
    
    if(stack.length != 0) {
        stack = sstrdup(stack);
        poolcfg.stack_size = atoi(stack.ptr);
        free(stack.ptr);
    }
    
    if(queue.length != 0) {
        queue = sstrdup(queue);
        poolcfg.queue_size = atoi(queue.ptr);
        free(queue.ptr);
    }
    
    create_threadpool(name, &poolcfg);
}

int cfg_handle_eventhandler(ServerConfiguration *c, ServerConfigObject *obj) {
    EventHandlerConfig evcfg;
    
    sstr_t name      = cfg_directivelist_get_str(obj->directives, sstr("Name"));
    sstr_t threads   = cfg_directivelist_get_str(
            obj->directives,
            sstr("Threads"));
    sstr_t isdefault = cfg_directivelist_get_str(
            obj->directives,
            sstr("Default"));
    
    evcfg.name = name;
    
    sstr_t s = sstrdup(threads);
    evcfg.nthreads = atoi(s.ptr);
    free(s.ptr);
    
    evcfg.isdefault = util_getboolean(isdefault.ptr, 0);
    
    int ret = create_event_handler(&evcfg);
    
    return ret;
}

int cfg_handle_accesslog(ServerConfiguration *cfg, ServerConfigObject *obj) {
    // TODO: use a name to identify the log file
    
    sstr_t file = cfg_directivelist_get_str(obj->directives, sstr("File"));
    if(file.ptr == NULL) {
        return 0;
    }
    sstr_t format;
    format.ptr = NULL; 
    
    AccessLog *log = get_access_log(file, format);
    if(log) {
        // access logs are only stored in the server config to free them
        cfg->logfiles = ucx_list_append(cfg->logfiles, log);
    }
    
    return 0;
}

int cfg_handle_authdb(ServerConfiguration *cfg, ServerConfigObject *obj) {
    sstr_t name = cfg_directivelist_get_str(obj->directives, sstr("Name"));
    sstr_t type = cfg_directivelist_get_str(obj->directives, sstr("Type"));
    
    if(!sstrcmp(type, sstr("ldap"))) {
        LDAPConfig conf;
        
        sstr_t host = cfg_directivelist_get_str(
                obj->directives,
                sstr("Host"));
        sstr_t port = cfg_directivelist_get_str(
                obj->directives,
                sstr("Port"));
        sstr_t basedn = cfg_directivelist_get_str(
                obj->directives,
                sstr("BaseDN"));
        sstr_t binddn = cfg_directivelist_get_str(
                obj->directives,
                sstr("BindDN"));
        sstr_t basepw = cfg_directivelist_get_str(
                obj->directives,
                sstr("BindPW"));
        
        host = sstrdup(host);
        port = sstrdup(port);
        basedn = sstrdup(basedn);
        binddn = sstrdup(binddn);
        basepw = sstrdup(basepw);
        
        conf.hostname = host.ptr;
        conf.port = atoi(port.ptr);
        conf.basedn = basedn.ptr;
        conf.binddn = binddn.ptr;
        conf.bindpw = basepw.ptr;
        
        name = sstrdup(name);
        
        AuthDB *authdb = create_ldap_authdb(name.ptr, &conf);
        printf("authdb: %d\n", authdb);
        ucx_map_sstr_put(cfg->authdbs, name, authdb);
        
        // TODO: create_ldap_authdb should copy the strings
        /*
        free(host.ptr);
        free(port.ptr);
        free(basedn.ptr);
        free(binddn.ptr);
        free(basepw.ptr);
        free(name.ptr);
        */
        
    }

    return 0;
}

int cfg_handle_listener(ServerConfiguration *cfg, ServerConfigObject *obj) {
    ListenerConfig lc;
    lc.cfg = cfg;
    lc.port = 8080;
    lc.nacceptors = 1;
    
    // TODO: use sstrdup_pool?
    lc.name = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("Name")));
    lc.port = atoi(cfg_directivelist_get_str(
            obj->directives,
            sstr("Port")).ptr);
    lc.vs = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("DefaultVS")));
    lc.threadpool = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("Threadpool")));
    
    // TODO: check if all important configs are set
    
    HttpListener *listener = http_listener_create(&lc);
    listener->default_vs.vs_name = lc.vs.ptr;
    cfg->listeners = ucx_list_append(cfg->listeners, listener); 
    
    return 0;
}

int cfg_handle_vs(ServerConfiguration *cfg, ServerConfigObject *obj) {
    VirtualServer *vs = vs_new();

    vs->name = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("Name")));
    vs->host = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("Host")));
    vs->document_root = sstrdup(cfg_directivelist_get_str(
            obj->directives,
            sstr("DocRoot")));
    sstr_t objfile = cfg_directivelist_get_str(
            obj->directives,
            sstr("ObjectFile"));
    sstr_t base = sstr("config/");
    sstr_t file;
    file.length = base.length + objfile.length + 1;
    file.ptr = alloca(file.length);
    file.ptr[file.length] = 0;
    file = sstrncat(2, file, base, objfile);

    ConfigFile *f = cfgmgr_get_file(file);
    if(f == NULL) {
        f = malloc(sizeof(ConfigFile));
        f->file = sstrdup(file);
        f->reload = object_conf_reload;
        f->reload(f, cfg);
        cfgmgr_attach_file(f);
    }
    vs->objectfile = sstrdup(file); // TODO: pool
    vs->objects = (HTTPObjectConfig*)f->data; // TODO: ref
    
    // set the access log for the virtual server
    // TODO: don't use always the default
    vs->log = get_default_access_log();

    ucx_map_sstr_put(cfg->host_vs, vs->host, vs);
    
    return 0;
}


int object_conf_reload(ConfigFile *file, ServerConfiguration *cfg) {
    file->data = load_obj_conf(file->file.ptr);
    struct stat s;
    if(stat(file->file.ptr, &s) != 0) {
        perror("object_conf_reload: stat");
        return -1;
    }
    file->last_modified = s.st_mtim.tv_sec;
    return 0;
}

HTTPObjectConfig* load_obj_conf(char *file) {
    printf("load_obj_conf\n");

    // new conf function test
    ObjectConfig *cfg = load_object_config(file);
    UcxMempool   *mp = cfg->parser.mp;
    if(cfg == NULL) {
        return NULL;
    }

    /* create object config */
    HTTPObjectConfig *conf = calloc(sizeof(HTTPObjectConfig), 1);
    conf->pool = pool_create();

    /* convert ObjectConfig to HTTPObjectConfig */

    /* add objects */
    conf->nobj = ucx_dlist_size(cfg->objects);
    conf->objects = calloc(1, sizeof(httpd_object*));
    
    UcxDlist *objlist = cfg->objects;
    int i = 0;
    while(objlist != NULL) {
        ConfigObject *cob = objlist->data;

        /* get name and ppath */
        char *name = NULL;
        char *ppath = NULL;
        if(cob->name.length > 0) {
            name = sstrdup(cob->name).ptr;
        }
        if(cob->ppath.length > 0) {
            ppath = sstrdup(cob->ppath).ptr;
        }

        /* create and add object */
        httpd_object *obj = object_new(name);
        obj->path = NULL;

        conf->objects[i] = obj; // TODO: beyond array bounds write

        /* add directives */
        for(int i=0;i<6;i++) {
            UcxDlist *dirs = cob->directives[i];
            while(dirs != NULL) {
                ConfigDirective *cfgdir = dirs->data;

                directive *d = malloc(sizeof(directive));
                d->cond = NULL;
                d->param = pblock_create_pool(conf->pool, 8);

                /* add params */
                UcxList *param = cfg_param_list(cfgdir->value, mp);
                while(param != NULL) {
                    ConfigParam *p = param->data;
                    pblock_nvlinsert(
                            p->name.ptr,
                            p->name.length,
                            p->value.ptr,
                            p->value.length,
                            d->param);
                    param = param->next;
                }

                /* get function */
                char *func_name = pblock_findval("fn", d->param);
                d->func = get_function(func_name);

                dirs = dirs->next;

                /* add function to dtable */
                object_add_directive(obj, d, cfgdir->type_num);
            }
        }

        /* next */
        i++;
        objlist = objlist->next;
    }

    free_object_config(cfg);

    return conf;
}

int mime_conf_reload(ConfigFile *file, ServerConfiguration *cfg) {
    MimeConfig *mimecfg = load_mime_config(file->file.ptr);
    
    UcxMap *mimemap = ucx_map_new((mimecfg->ntypes * 3) / 2);
    
    // add ext type pairs
    UCX_FOREACH(UcxList*, mimecfg->directives, md) {
        MimeDirective *d = md->data;
        // add the type for each extension to the map
        UCX_FOREACH(UcxList*, d->exts, xl) {
            sstr_t ext = sstr(xl->data);
            sstr_t value = sstrdup(d->type);
            ucx_map_sstr_put(mimemap, ext, value.ptr);
        }
    }
    
    file->data = mimemap;
    return 0;
}

mercurial