UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2013 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "../public/nsapi.h" 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 34 #include <fcntl.h> 35 #include <sys/types.h> 36 #include <sys/file.h> 37 #include <sys/stat.h> 38 #include <sys/mman.h> 39 40 #include <cx/string.h> 41 #include <cx/utils.h> 42 #include <cx/hash_map.h> 43 #include <cx/linked_list.h> 44 #include <cx/compare.h> 45 46 #include "httplistener.h" 47 #include "config.h" 48 #include "func.h" 49 #include "log.h" 50 #include "event.h" 51 #include "threadpools.h" 52 #include "ldap_auth.h" 53 #include "configmanager.h" 54 #include "resourcepool.h" 55 56 #include "vserver.h" 57 #include "../util/pblock.h" 58 #include "../util/util.h" 59 #include "../util/atomic.h" 60 #include "cx/buffer.h" 61 62 static pool_handle_t *init_pool; 63 64 pool_handle_t* cfg_get_init_pool(void) { 65 return init_pool; 66 } 67 68 char* cfg_config_file_path(const char *file) { 69 cxstring base = CX_STR("config/"); 70 cxmutstr path = cx_strcat(2, base, cx_str(file)); 71 return path.ptr; 72 } 73 74 InitConfig* load_init_conf(const char *file) { 75 log_ereport(LOG_VERBOSE, "load_init_conf"); 76 77 InitConfig *cfg = initconfig_load(file); 78 if(cfg == NULL) { 79 log_ereport(LOG_FAILURE, "Cannot load init.conf"); 80 return NULL;; 81 } 82 83 return cfg; 84 } 85 86 int apply_init_conf(InitConfig *cfg) { 87 init_pool = pool_create(); 88 89 ConfigNode *dir = cfg->root->children_begin; 90 while(dir) { 91 if(dir->type != CONFIG_NODE_DIRECTIVE) { 92 // dir is just space/comment 93 dir = dir->next; 94 continue; 95 } 96 97 // create NSAPI directive 98 99 // The parser checks the directive name and makes sure it is "Init" 100 // if more than one init directive type is introduced, the parser 101 // must be extended and also dir->name must be checked here 102 103 directive *d = pool_malloc(init_pool, sizeof(directive)); 104 d->param = pblock_create_pool(init_pool, 8); 105 106 ConfigParam *param = dir->args; 107 while(param != NULL) { 108 pblock_nvlinsert( 109 param->name.ptr, 110 param->name.length, 111 param->value.ptr, 112 param->value.length, 113 d->param); 114 115 param = param->next; 116 } 117 118 // get function 119 // the parser makes sure that an "fn" parameter always exists 120 char *func_name = pblock_findval("fn", d->param); 121 d->func = get_function(func_name); 122 if(d->func == NULL) { 123 log_ereport( 124 LOG_MISCONFIG, 125 "Cannot find Init function %s", 126 func_name); 127 return 1; 128 } 129 130 // execute init directive 131 int ret = d->func->func(d->param, NULL, NULL); 132 if(ret != REQ_PROCEED && ret != REQ_NOACTION) { 133 log_ereport( 134 LOG_FAILURE, 135 "Error running Init function %s", 136 func_name); 137 return 1; 138 } 139 140 dir = dir->next; 141 } 142 143 return 0; 144 } 145 146 void free_init_conf(InitConfig *cfg) { 147 initconfig_free(cfg); 148 } 149 150 ServerConfiguration* load_server_conf(CfgManager *mgr, char *file) { 151 log_ereport(LOG_VERBOSE, "load_server_conf"); 152 153 ServerConfig *serverconf = serverconfig_load(file); 154 if(!serverconf) { 155 log_ereport(LOG_FAILURE, "Cannot load server.conf"); 156 return NULL; 157 } 158 mgr->serverconf = serverconf; 159 160 pool_handle_t *pool = pool_create(); 161 162 163 ServerConfiguration *serverconfig = pool_calloc(pool, 1, sizeof(ServerConfiguration)); 164 serverconfig->ref = 1; 165 serverconfig->pool = pool; 166 167 CxAllocator *allocator = pool_allocator(serverconfig->pool); 168 serverconfig->a = allocator; 169 170 serverconfig->listeners = cxLinkedListCreate(serverconfig->a, NULL, CX_STORE_POINTERS); 171 serverconfig->logfiles = cxLinkedListCreate(serverconfig->a, NULL, CX_STORE_POINTERS); 172 serverconfig->host_vs = cxHashMapCreate(serverconfig->a, CX_STORE_POINTERS, 16); 173 serverconfig->authdbs = cxHashMapCreate(serverconfig->a, CX_STORE_POINTERS, 16); 174 serverconfig->resources = cxHashMapCreate(serverconfig->a, CX_STORE_POINTERS, 16); 175 serverconfig->dav = cxHashMapCreate(serverconfig->a, CX_STORE_POINTERS, 16); 176 177 // STAGE 1 load_server_conf: 178 // At stage 1 we load the file and get the Runtime infos for changing 179 // the uid, which must be done before most steps. 180 // Before the uid can be changed, we also need to bind listeners, 181 // therefore we need to get the listener config and all dependencies. 182 // 183 // Runtime 184 // Listener (dependencies: Threadpool, EventHandler) 185 186 // load Runtime config 187 CxList *list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Runtime")); 188 CxIterator iter = cxListIterator(list); 189 cx_foreach(ConfigNode *, runtimeobj, iter) { 190 if(cfg_handle_runtime(serverconfig, runtimeobj)) { 191 // error 192 log_ereport(LOG_FAILURE, "server.conf runtime"); 193 return NULL; 194 } 195 } 196 cxListDestroy(list); 197 198 // load threadpool config 199 log_ereport(LOG_DEBUG, "apply config: Threadpool"); 200 list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Threadpool")); 201 iter = cxListIterator(list); 202 cx_foreach(ConfigNode *, elm, iter) { 203 if(cfg_handle_threadpool(serverconfig, elm)) { 204 log_ereport(LOG_FAILURE, "server.conf threadpool"); 205 return NULL; 206 } 207 } 208 cxListDestroy(list); 209 // check thread pool config 210 if(check_thread_pool_cfg() != 0) { 211 /* critical error */ 212 return NULL; 213 } 214 215 // load eventhandler config 216 log_ereport(LOG_DEBUG, "apply config: EventHandler"); 217 list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("EventHandler")); 218 iter = cxListIterator(list); 219 cx_foreach(ConfigNode *, elm, iter) { 220 if(cfg_handle_eventhandler(serverconfig, elm)) { 221 // error 222 log_ereport(LOG_FAILURE, "cannot create event handler"); 223 return NULL; 224 } 225 } 226 // check event handler config 227 if(check_event_handler_cfg() != 0) { 228 /* critical error */ 229 return NULL; 230 } 231 cxListDestroy(list); 232 233 // load Listener config 234 log_ereport(LOG_DEBUG, "apply config: Listener"); 235 list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Listener")); 236 iter = cxListIterator(list); 237 cx_foreach(ConfigNode *, scfgobj, iter) { 238 if(cfg_handle_listener(serverconfig, scfgobj)) { 239 return NULL; 240 } 241 } 242 cxListDestroy(list); 243 244 // we return here, to let the webserver use the runtime info to 245 // change the uid if needed 246 return serverconfig; 247 } 248 249 ServerConfiguration* apply_server_conf(CfgManager *mgr) { 250 ServerConfig *serverconf = mgr->serverconf; 251 ServerConfiguration *serverconfig = mgr->cfg; 252 253 /* 254 * convert ServerConfig to ServerConfiguration 255 * 256 * its important to do this in the correct order: 257 * LogFile (open log file first to log possible errors) 258 * Threadpool 259 * EventHandler 260 * AuthDB 261 * Listener (we set the VirtualServer later) 262 * VirtualServer (dependencies: Listener) 263 */ 264 265 // init logfile first 266 CxList *list; 267 268 log_ereport(LOG_DEBUG, "apply config: LogFile"); 269 list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("LogFile")); 270 CxIterator iter = cxListIterator(list); 271 cx_foreach(ConfigNode *, logobj, iter) { 272 if(!logobj) { 273 // error 274 cxListDestroy(list); 275 return NULL; 276 } 277 278 int ret = cfg_handle_logfile(serverconfig, logobj); 279 if(ret != 0) { 280 // cannot initialize log file 281 cxListDestroy(list); 282 return NULL; 283 } 284 } 285 cxListDestroy(list); 286 287 log_ereport(LOG_DEBUG, "apply config: AccessLog"); 288 list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("AccessLog")); 289 iter = cxListIterator(list); 290 cx_foreach(ConfigNode *, scfgobj, iter) { 291 if(cfg_handle_accesslog(serverconfig, scfgobj)) { 292 return NULL; 293 } 294 } 295 cxListDestroy(list); 296 297 log_ereport(LOG_DEBUG, "apply config: AuthDB"); 298 list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("AuthDB")); 299 iter = cxListIterator(list); 300 cx_foreach(ConfigNode *, scfgobj, iter) { 301 if(cfg_handle_authdb(serverconfig, scfgobj)) { 302 return NULL; 303 } 304 } 305 cxListDestroy(list); 306 307 log_ereport(LOG_DEBUG, "apply config: VirtualServer"); 308 list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("VirtualServer")); 309 iter = cxListIterator(list); 310 cx_foreach(ConfigNode *, scfgobj, iter) { 311 if(cfg_handle_vs(serverconfig, scfgobj)) { 312 return NULL; 313 } 314 } 315 cxListDestroy(list); 316 317 log_ereport(LOG_DEBUG, "apply config: ResourcePool"); 318 list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("ResourcePool")); 319 iter = cxListIterator(list); 320 cx_foreach(ConfigNode *, scfgobj, iter) { 321 if(cfg_handle_resourcepool(serverconfig, scfgobj)) { 322 return NULL; 323 } 324 } 325 cxListDestroy(list); 326 327 log_ereport(LOG_DEBUG, "apply config: Dav"); 328 list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Dav")); 329 iter = cxListIterator(list); 330 cx_foreach(ConfigNode *, scfgobj, iter) { 331 if(cfg_handle_dav(serverconfig, scfgobj)) { 332 return NULL; 333 } 334 } 335 cxListDestroy(list); 336 337 // set VirtualServer for all listeners 338 CxList *ls = serverconfig->listeners; 339 iter = cxListIterator(ls); 340 cx_foreach(HttpListener *, listener, iter) { 341 cxstring vsname = cx_str(listener->default_vs.vs_name); 342 343 // search for VirtualServer 344 //int b = 0; 345 CxIterator map_iter = cxMapIteratorValues(serverconfig->host_vs); 346 cx_foreach(VirtualServer *, vs, map_iter) { 347 if(!cx_strcmp(vsname, (cxstring){vs->name.ptr, vs->name.length})) { 348 listener->default_vs.vs = vs; 349 break; 350 } 351 } 352 } 353 354 serverconfig_free(serverconf); 355 356 return serverconfig; 357 } 358 359 int migrate_server_conf(ServerConfiguration *old_cfg, ServerConfiguration *new_cfg) { 360 old_cfg->next = new_cfg; 361 cfg_ref(new_cfg); // new cfg should not be freed until old cfg is freed 362 363 // compare old/new listeners and set next listener, if they are using 364 // the same socket 365 CxIterator old_listeners = cxListIterator(old_cfg->listeners); 366 cx_foreach(HttpListener*, oldls, old_listeners) { 367 if(oldls->next) { 368 // maybe we can remove this check 369 log_ereport(LOG_WARN, "migrate_server_conf: oldls->next not NULL"); 370 continue; 371 } 372 373 CxIterator new_listeners = cxListIterator(new_cfg->listeners); 374 cx_foreach(HttpListener*, newls, new_listeners) { 375 if(http_listener_socket_eq(oldls, newls)) { 376 http_listener_set_next(oldls, newls); 377 break; 378 } 379 } 380 http_listener_shutdown_acceptors(oldls); 381 382 // TODO: wait until old listener is shut down 383 } 384 385 return 0; 386 } 387 388 void cfg_ref(ServerConfiguration *cfg) { 389 ws_atomic_inc32(&cfg->ref); 390 } 391 392 void cfg_unref(ServerConfiguration *cfg) { 393 uint32_t ref = ws_atomic_dec32(&cfg->ref); 394 if(ref == 0) { 395 if(cfg->next) { 396 cfg_unref(cfg->next); 397 } 398 log_ereport(LOG_VERBOSE, "destroy configuration %p", cfg); 399 400 CxIterator i = cxListIterator(cfg->listeners); 401 cx_foreach(HttpListener*, listener, i) { 402 http_listener_destroy(listener); 403 } 404 405 pool_destroy(cfg->pool); 406 } 407 } 408 409 410 void init_server_config_parser() { 411 412 } 413 414 int cfg_handle_runtime(ServerConfiguration *cfg, ConfigNode *obj) { 415 cxstring user = serverconfig_object_directive_value(obj, cx_str("User")); 416 if(user.ptr) { 417 cfg->user = cx_strdup_a(cfg->a, user); 418 } 419 cxstring tmp = serverconfig_object_directive_value(obj, cx_str("Temp")); 420 if(tmp.ptr) { 421 cfg->tmp = cx_strdup_a(cfg->a, tmp); 422 } else { 423 // TODO: do this check after all config loading is done 424 log_ereport(LOG_MISCONFIG, "no temporary directory specified"); 425 return -1; 426 } 427 428 // mime file 429 cxstring mf = serverconfig_object_directive_value(obj, cx_str("MimeFile")); 430 cxstring base = cx_str("config/"); 431 cxmutstr file = cx_strcat(2, base, mf); 432 433 if(mime_conf_load(cfg, file)) { 434 return -1; 435 } 436 437 free(file.ptr); 438 return 0; 439 } 440 441 int cfg_handle_logfile(ServerConfiguration *cfg, ConfigNode *obj) { 442 cxstring file = serverconfig_object_directive_value(obj, cx_str("File")); 443 cxstring lvl = serverconfig_object_directive_value(obj, cx_str("Level")); 444 445 int err = 0; 446 if(file.ptr == NULL) { 447 err = 1; 448 log_ereport(LOG_MISCONFIG, "LogFile: parameter missing: File"); 449 } 450 if(lvl.ptr == NULL) { 451 err = 1; 452 log_ereport(LOG_MISCONFIG, "LogFile: parameter missing: Level"); 453 } 454 if(err) { 455 return -1; 456 } 457 458 LogConfig logcfg; 459 logcfg.file = file.ptr; 460 logcfg.level = lvl.ptr; 461 logcfg.log_stdout = 0; 462 logcfg.log_stderr = 0; 463 /* TODO: stdout, stderr config */ 464 465 int ret = init_log_file(&logcfg); 466 467 return ret; 468 } 469 470 int cfg_handle_threadpool(ServerConfiguration *cfg, ConfigNode *obj) { 471 ThreadPoolConfig poolcfg; 472 poolcfg.min_threads = 4; 473 poolcfg.min_threads = 4; 474 poolcfg.max_threads = 8; 475 poolcfg.queue_size = 64; 476 poolcfg.stack_size = 262144; 477 478 cxstring name = serverconfig_object_directive_value(obj, cx_str("Name")); 479 cxstring min = serverconfig_object_directive_value(obj, cx_str("MinThreads")); 480 cxstring max = serverconfig_object_directive_value(obj, cx_str("MaxThreads")); 481 cxstring stack = serverconfig_object_directive_value(obj, cx_str("StackSize")); 482 cxstring queue = serverconfig_object_directive_value(obj, cx_str("QueueSize")); 483 // TODO: Type 484 485 if(name.length == 0) { 486 // TODO: log error 487 return 1; 488 } 489 490 if(min.length != 0) { 491 int64_t value; 492 if(util_strtoint(min.ptr, &value)) { 493 poolcfg.min_threads = value; 494 } else { 495 log_ereport(LOG_MISCONFIG, "Threadpool: MinThreads not an integer"); 496 return 1; 497 } 498 } 499 500 if(max.length != 0) { 501 int64_t value; 502 if(util_strtoint(max.ptr, &value)) { 503 poolcfg.max_threads = value; 504 } else { 505 log_ereport(LOG_MISCONFIG, "Threadpool: MaxThreads not an integer"); 506 return 1; 507 } 508 } 509 510 if(stack.length != 0) { 511 int64_t value; 512 if(util_strtoint(stack.ptr, &value)) { 513 poolcfg.stack_size = value; 514 } else { 515 log_ereport(LOG_MISCONFIG, "Threadpool: StackSize not an integer"); 516 } 517 } 518 519 if(queue.length != 0) { 520 int64_t value; 521 if(util_strtoint(queue.ptr, &value)) { 522 poolcfg.queue_size = value; 523 } else { 524 log_ereport(LOG_MISCONFIG, "Threadpool: QueueSize not an integer"); 525 } 526 } 527 528 create_threadpool(name, &poolcfg); 529 530 return 0; 531 } 532 533 #define EV_MAX_THREADS 2048 534 int cfg_handle_eventhandler(ServerConfiguration *c, ConfigNode *obj) { 535 EventHandlerConfig evcfg; 536 537 cxstring name = serverconfig_object_directive_value(obj, cx_str("Name")); 538 cxstring threads = serverconfig_object_directive_value(obj, cx_str("Threads")); 539 cxstring isdefault = serverconfig_object_directive_value(obj, cx_str("Default")); 540 541 evcfg.name = name; 542 543 int64_t value; 544 if(!util_strtoint(threads.ptr, &value)) { 545 log_ereport(LOG_MISCONFIG, "EventHandler: Threads: ''%s'' is not an integer", threads.ptr); 546 return 1; 547 } 548 if(value < 1 || value > EV_MAX_THREADS) { 549 log_ereport(LOG_MISCONFIG, "EventHandler: Invalid number of threads (1 .. %d)", EV_MAX_THREADS); 550 return 1; 551 } 552 553 evcfg.nthreads = value; 554 555 evcfg.isdefault = util_getboolean(isdefault.ptr, 0); 556 557 return create_event_handler(&evcfg); 558 } 559 560 int cfg_handle_resourcepool(ServerConfiguration *cfg, ConfigNode *obj) { 561 cxstring name = serverconfig_object_directive_value(obj, cx_str("Name")); 562 cxstring type = serverconfig_object_directive_value(obj, cx_str("Type")); 563 564 int ret = 0; 565 if(resourcepool_new(cfg, type, name, obj)) { 566 ret = 1; 567 } 568 569 return ret; 570 } 571 572 int cfg_handle_accesslog(ServerConfiguration *cfg, ConfigNode *obj) { 573 // TODO: use a name to identify the log file 574 575 cxstring file = serverconfig_object_directive_value(obj, cx_str("File")); 576 if(file.ptr == NULL) { 577 return 0; 578 } 579 cxmutstr format; 580 format.ptr = NULL; 581 format.length = 0; 582 583 //AccessLog *log = get_access_log(file, format); 584 LogFile *log_file = get_access_log_file(file); 585 if(!log_file) { 586 // TODO: error/warning 587 return 0; 588 } 589 AccessLog *log = pool_malloc(cfg->pool, sizeof(AccessLog)); 590 log->file = cx_strdup_a(cfg->a, file); 591 log->format = format; 592 log->log = log_file; 593 cxListAdd(cfg->logfiles, log); 594 595 if(!cfg->default_log) { 596 cfg->default_log = log; 597 } 598 599 return 0; 600 } 601 602 int cfg_handle_authdb(ServerConfiguration *cfg, ConfigNode *obj) { 603 cxstring name = serverconfig_object_directive_value(obj, cx_str("Name")); 604 cxstring type = serverconfig_object_directive_value(obj, cx_str("Type")); 605 606 AuthDB *authdb = NULL; 607 608 if(!cx_strcmp(type, cx_str("ldap"))) { 609 authdb = create_ldap_authdb(cfg, name.ptr, obj); 610 } else if(!cx_strcmp(type, cx_str("keyfile"))) { 611 // we only need the file parameter 612 cxstring file = serverconfig_object_directive_value(obj, cx_str("File")); 613 if(file.length == 0) { 614 log_ereport( 615 LOG_MISCONFIG, 616 "missing File parameter for keyfile authdb"); 617 return 1; 618 } 619 620 // load keyfile 621 authdb = keyfile_load(cfg, file); 622 } 623 624 if(authdb) { 625 if(cxMapPut(cfg->authdbs, cx_hash_key_bytes((const unsigned char*)name.ptr, name.length), authdb)) { 626 return -1; 627 } 628 } 629 630 return 0; 631 } 632 633 int cfg_handle_listener(ServerConfiguration *cfg, ConfigNode *obj) { 634 ListenerConfig lc; 635 ZERO(&lc, sizeof(ListenerConfig)); 636 lc.cfg = cfg; 637 lc.port = 8080; 638 lc.nacceptors = 1; 639 640 cxstring name = serverconfig_object_directive_value(obj, cx_str("Name")); 641 cxstring port = serverconfig_object_directive_value(obj, cx_str("Port")); 642 cxstring vs = serverconfig_object_directive_value(obj, cx_str("DefaultVS")); 643 cxstring thrp = serverconfig_object_directive_value(obj, cx_str("Threadpool")); 644 cxstring blck = serverconfig_object_directive_value(obj, cx_str("BlockingIO")); 645 646 // TODO: use cx_strdup_pool? 647 int64_t port_value; 648 if(!util_strtoint(port.ptr, &port_value)) { 649 log_ereport(LOG_MISCONFIG, "Listener: Invalid argument for parameter ''Port'': ''%s''", port.ptr); 650 return 1; 651 } 652 if(port_value < 1 || port_value > 65535) { 653 log_ereport(LOG_MISCONFIG, "Listener: Port number out of range (1 .. 65535)"); 654 return 1; 655 } 656 657 lc.name = cx_strdup(name); 658 lc.port = port_value; 659 lc.vs = cx_strdup(vs); 660 lc.threadpool = cx_strdup(thrp); 661 662 lc.blockingio = util_getboolean_s(blck, WS_FALSE); 663 664 cxstring ssl = serverconfig_object_directive_value(obj, cx_str("SSL")); 665 if(util_getboolean_s(ssl, WS_FALSE)) { 666 cxstring cert = serverconfig_object_directive_value(obj, cx_str("Cert")); 667 cxstring privkey = serverconfig_object_directive_value(obj, cx_str("Key")); 668 cxstring chain = serverconfig_object_directive_value(obj, cx_str("CertChain")); 669 cxstring disableprot = serverconfig_object_directive_value(obj, cx_str("SSLDisableProtocol")); 670 671 WSBool config_ok = WS_TRUE; 672 // TODO: log error 673 if(!cert.ptr && !chain.ptr) { 674 log_ereport( 675 LOG_MISCONFIG, 676 "SSL Listener %s: Missing Cert or ChainCert directive", 677 lc.name.ptr); 678 config_ok = WS_FALSE; 679 } 680 if(!privkey.ptr) { 681 log_ereport( 682 LOG_MISCONFIG, 683 "SSL Listener %s: Missing Key directive", 684 lc.name.ptr); 685 config_ok = WS_FALSE; 686 } 687 688 if(config_ok) { 689 lc.certfile = cert; 690 lc.privkeyfile = privkey; 691 lc.chainfile = chain; 692 lc.disable_proto = disableprot; 693 lc.ssl = WS_TRUE; 694 } 695 } else { 696 lc.ssl = WS_FALSE; 697 } 698 699 // TODO: check if all important configs are set 700 701 int ret = 0; 702 HttpListener *listener = http_listener_create(&lc); 703 if(listener) { 704 listener->default_vs.vs_name = cx_strdup_a(cfg->a, (cxstring){lc.vs.ptr, lc.vs.length}).ptr; 705 cxListAdd(cfg->listeners, listener); 706 } else { 707 ret = 1; 708 } 709 710 free(lc.name.ptr); 711 free(lc.vs.ptr); 712 free(lc.threadpool.ptr); 713 714 return 0; 715 } 716 717 int cfg_handle_vs(ServerConfiguration *cfg, ConfigNode *obj) { 718 VirtualServer *vs = vs_new(cfg->pool); 719 720 vs->name = cx_strdup_a(cfg->a, serverconfig_object_directive_value(obj, cx_str("Name"))); 721 vs->host = cx_strdup_a(cfg->a, serverconfig_object_directive_value(obj, cx_str("Host"))); 722 vs->document_root = cx_strdup_a(cfg->a, serverconfig_object_directive_value(obj, cx_str("DocRoot"))); 723 724 cxstring objfile = serverconfig_object_directive_value(obj, cx_str("ObjectFile")); 725 cxstring aclfile = serverconfig_object_directive_value(obj, cx_str("ACLFile")); 726 727 // load the object config file 728 cxstring base = cx_str("config/"); 729 // cx_strcat with allocator because we want to keep the string 730 cxmutstr file = cx_strcat_a(cfg->a, 2, base, objfile); 731 732 HTTPObjectConfig *httpobj = objconf_load(cfg, file); 733 if(!httpobj) { 734 return -1; 735 } 736 vs->objectfile = file; 737 vs->objects = httpobj; 738 739 740 // load acl config file 741 cxmutstr acl_filepath = cx_strcat(2, base, aclfile); 742 743 ACLData *acldata = acl_conf_load(cfg, acl_filepath.ptr); 744 free(acl_filepath.ptr); 745 if(!acldata) { 746 return -1; 747 } 748 vs->acls = acldata; 749 750 751 // set the access log for the virtual server 752 // TODO: don't always use the default 753 vs->log = cfg->default_log; 754 755 cxMapPut(cfg->host_vs, cx_hash_key_bytes((unsigned const char*)vs->host.ptr, vs->host.length), vs); 756 757 return 0; 758 } 759 760 int cfg_handle_dav(ServerConfiguration *cfg, ConfigNode *obj) { 761 CxAllocator *a = pool_allocator(cfg->pool); 762 CxList *backends = cxLinkedListCreate(a, NULL, CX_STORE_POINTERS); // list of ConfigParam* 763 int init_error; 764 765 // parse args 766 char *uri = NULL; 767 char *ppath = NULL; 768 char *name = NULL; 769 for(ConfigParam *arg=obj->args;arg;arg=arg->next) { 770 cxstring arg_name = (cxstring){ arg->name.ptr, arg->name.length }; 771 if(arg->name.ptr == NULL) { 772 // default: uri 773 uri = arg->value.ptr; 774 } else if(!cx_strcasecmp(arg_name, cx_str("uri"))) { 775 uri = arg->value.ptr; 776 } else if(!cx_strcasecmp(arg_name, cx_str("ppath"))) { 777 ppath = arg->value.ptr; 778 } else if(!cx_strcasecmp(arg_name, cx_str("name"))) { 779 name = arg->value.ptr; 780 } 781 } 782 if(!uri && !ppath && !name) { 783 return 1; 784 } 785 786 // get a list of all DavBackends 787 for(ConfigNode *node=obj->children_begin;node;node=node->next) { 788 cxstring node_name = cx_strn(node->name.ptr, node->name.length); 789 if(!cx_strcasecmp(node_name, cx_str("DavBackend"))) { 790 if(node->type == CONFIG_NODE_DIRECTIVE) { 791 if(CFG_NUM_PARAMS(node->args) == 1) { 792 cxListAdd(backends, node->args); 793 } else { 794 log_ereport(LOG_MISCONFIG, "DavBackend must have only one value"); 795 cxListDestroy(backends); 796 return 1; 797 } 798 } else { 799 log_ereport(LOG_MISCONFIG, "DavBackend must be a directive"); 800 cxListDestroy(backends); 801 return 1; 802 } 803 } 804 } 805 806 int ret = 0; 807 WebdavRepository *repository = pool_malloc(cfg->pool, sizeof(WebdavRepository)); 808 repository->vfs = NULL; 809 repository->vfsInitData = NULL; 810 repository->davBackends = cxLinkedListCreate(a, NULL, CX_STORE_POINTERS); // value type: WebdavBackendInitData* 811 812 // initialize backends 813 CxIterator i = cxListIterator(backends); 814 cx_foreach(ConfigParam *, backendArg, i) { 815 // the DavBackend value should contain the dav class name 816 817 WebdavType *dav = webdav_get_type((cxstring){backendArg->value.ptr, backendArg->value.length}); 818 if(!dav) { 819 log_ereport(LOG_MISCONFIG, "Unknown webdav backend type ''%s''", backendArg->value.ptr); 820 ret = 1; 821 break; 822 } 823 824 // call backend init 825 // init_data can be NULL, errors will be indicated by init_error 826 void *init_data = webdav_init_backend(cfg, cfg->pool, dav, obj, &init_error); 827 if(init_error) { 828 log_ereport(LOG_FAILURE, "Failed to initialize webdav backend %s", backendArg->value.ptr); 829 ret = 1; 830 break; 831 } 832 833 WebdavBackendInitData *davInit = pool_malloc(cfg->pool, sizeof(WebdavBackendInitData)); 834 if(!davInit) { 835 log_ereport(LOG_FAILURE, "Failed to initialize webdav backend %s: OOM", backendArg->value.ptr); 836 ret = 1; 837 break; 838 } 839 davInit->davType = dav; 840 davInit->davInitData = init_data; 841 842 cxListAdd(repository->davBackends, davInit); 843 } 844 cxListDestroy(backends); 845 846 // initialize vfs 847 cxstring vfs_class = serverconfig_object_directive_value(obj, cx_str("VFS")); 848 if(vfs_class.length > 0) { 849 VfsType *vfs = vfs_get_type((cxstring){vfs_class.ptr, vfs_class.length}); 850 if(vfs) { 851 repository->vfs = vfs; 852 repository->vfsInitData = vfs_init_backend(cfg, cfg->pool, vfs, obj, &init_error); 853 if(!ret) { 854 ret = init_error; 855 } 856 } else { 857 log_ereport(LOG_FAILURE, "Unknown vfs type ''%s''", vfs_class.ptr); 858 ret = 1; 859 } 860 } 861 862 cxstring object = serverconfig_object_directive_value(obj, cx_str("Object")); 863 if(object.length > 0) { 864 repository->object = cx_strdup_a(a, object); 865 if(repository->object.length != object.length) { 866 // OOM 867 log_ereport(LOG_FAILURE, "Cannot create webdav repository: OOM"); 868 ret = 1; 869 } 870 } 871 872 if(!ret) { 873 if(name) { 874 cxMapPut(cfg->dav, cx_hash_key_str(name), repository); 875 } else { 876 log_ereport(LOG_FAILURE, "TODO: location based dav repositories not implemented"); 877 ret = 1; 878 } 879 } 880 881 return ret; 882 } 883 884 // condition depth limit, evaluated in recursive convert_objconf_directives calls 885 #define OBJ_CONF_MAX_CONDITION_DEPTH 500 886 887 static int set_client_condition(pool_handle_t *pool, ConfigNode *node, Condition *condition) { 888 return 0; 889 } 890 891 static int set_if_condition(pool_handle_t *pool, ConfigNode *node, Condition *condition) { 892 // convert to parameters to a list of tokens 893 // usually, one parameter is one token, however the config parser 894 // converts name=value pairs to one ConfigParam 895 896 // list of cxmutstr, however the expression parser will use this 897 // as list of cxstring, but that is totally fine 898 CxList *tokens = cxLinkedListCreate(pool_allocator(pool), NULL, sizeof(cxmutstr)); 899 ConfigParam *arg = node->args; 900 while(arg) { 901 if(arg->name.length > 0) { 902 // arg text is name=value, therefore we add 3 tokens 903 // name, "=", value 904 cxListAdd(tokens, &arg->name); 905 cxmutstr op = (cxmutstr){ "=", 1 }; 906 cxListAdd(tokens, &op); 907 } 908 if(cxListAdd(tokens, &arg->value)) { 909 cxListDestroy(tokens); 910 return 1; // OOM 911 } 912 arg = arg->next; 913 } 914 915 int ret = 0; 916 condition->expression = condition_create(pool, tokens); 917 if(!condition->expression) { 918 ret = 1; 919 } 920 921 // don't need the token list anymore 922 cxListDestroy(tokens); 923 924 return ret; 925 } 926 927 // convert a condition 928 static Condition* convert_objconf_condition( 929 pool_handle_t *pool, 930 ConfigNode *node, 931 Condition *prev_condition, 932 Condition *parent_condition, 933 int *condition_index) 934 { 935 const char *condnames[] = { "Client", "If", "ElseIf", "Else" }; 936 size_t typeindex; 937 if(serverconfig_validate_directive_name(node, condnames, 4, &typeindex)) { 938 // probably node->name is "Object", but nested objects are not allowed 939 // maybe there should be a special error message in this case 940 return NULL; 941 } 942 943 // "ElseIf" and "Else" require, that a previous "If" or "ElseIf" node exists 944 if((typeindex == 2 || typeindex == 3) && prev_condition == NULL) { 945 return NULL; 946 } 947 Condition *condition = pool_malloc(pool, sizeof(Condition)); 948 ZERO(condition, sizeof(Condition)); 949 950 condition->index = *condition_index; 951 condition->parent = parent_condition; 952 953 if(typeindex == 0) { 954 // "Client" 955 if(set_client_condition(pool, node, condition)) { 956 return NULL; 957 } 958 } else { 959 condition->ifnot = prev_condition; 960 961 // set expression for "If" or "ElseIf" 962 if(typeindex != 4 && set_if_condition(pool, node, condition)) { 963 return NULL; 964 } 965 } 966 967 (*condition_index)++; 968 return condition; 969 } 970 971 // add directives to the httpd_object 972 // node->type can be CONFIG_NODE_DIRECTIVE or CONFIG_NODE_OBJECT 973 // CONFIG_NODE_DIRECTIVE can translated directly to directive* 974 // CONFIG_NODE_OBJECT node must be a condition (If/ElseIf/Else/Client) 975 static int convert_objconf_directives( 976 pool_handle_t *pool, 977 const char *file, 978 httpd_object *obj, 979 ConfigNode *node, 980 Condition *parent_condition, 981 int *condition_index) 982 { 983 // previous condition, if it was the previous node 984 // this is needed to link the "ElseIf" or "Else" node with the previous 985 // "If" node 986 Condition *prev_condition = NULL; 987 988 for(;node;node=node->next) { 989 if(node->type == CONFIG_NODE_OBJECT) { 990 Condition *condition = convert_objconf_condition(pool, node, prev_condition, parent_condition, condition_index); 991 if(!condition) { 992 return 1; 993 } 994 // add children of condition node 995 if(convert_objconf_directives(pool, file, obj, node->children_begin, condition, condition_index)) { 996 return 1; 997 } 998 999 // previous condition is used to link "If" "IfElse" and "Else" 1000 // if the current node is "Else", the if-else block is complete 1001 // "Client" is unrelated to if-else 1002 if(!strcmp(node->name.ptr, "If") || !strcmp(node->name.ptr, "ElseIf")) { 1003 prev_condition = condition; 1004 } else { 1005 prev_condition = NULL; 1006 } 1007 } else if(node->type == CONFIG_NODE_DIRECTIVE) { 1008 directive *d = pool_malloc(pool, sizeof(directive)); 1009 if(!d) return -1; 1010 d->param = pblock_create_pool(pool, 8); 1011 1012 d->cond = parent_condition; 1013 1014 // add params 1015 ConfigParam *param = node->args; 1016 while(param != NULL) { 1017 pblock_nvlinsert( 1018 param->name.ptr, 1019 param->name.length, 1020 param->value.ptr, 1021 param->value.length, 1022 d->param); 1023 param = param->next; 1024 } 1025 1026 // get function 1027 char *func_name = pblock_findval("fn", d->param); 1028 if(!func_name) { 1029 log_ereport(LOG_MISCONFIG, "%s: Missing fn parameter", file); 1030 return -1; 1031 } 1032 d->func = get_function(func_name); 1033 if(!d->func) { 1034 log_ereport(LOG_MISCONFIG, "func %s not found", func_name); 1035 return -1; 1036 } 1037 1038 // add function to dtable 1039 int dir_type = cfg_get_directive_type_num(cx_strcast(node->name)); 1040 if(dir_type < 0) { 1041 log_ereport(LOG_MISCONFIG, "unknown directive type %s", node->name); 1042 } 1043 object_add_directive(obj, d, dir_type); 1044 1045 prev_condition = NULL; 1046 } 1047 } 1048 1049 return 0; 1050 } 1051 1052 static int convert_objconf(ServerConfiguration *scfg, ObjectConfig2 *cfg, HTTPObjectConfig *conf, cxmutstr file) { 1053 pool_handle_t *pool = conf->pool; 1054 1055 int condition_index = 0; 1056 1057 int i = 0; 1058 for(ConfigNode *objnode=cfg->root->children_begin;objnode;objnode=objnode->next) { 1059 if(objnode->type != CONFIG_NODE_OBJECT) { 1060 if(objnode->type == CONFIG_NODE_DIRECTIVE) { 1061 // error 1062 return 1; 1063 } 1064 continue; 1065 } 1066 1067 if(strcmp(objnode->name.ptr, "Object")) { 1068 // error: not an object 1069 return 1; 1070 } 1071 1072 // get name and ppath 1073 cxmutstr cfg_name = cfg_param_get(objnode->args, cx_str("name")); 1074 cxmutstr cfg_ppath = cfg_param_get(objnode->args, cx_str("ppath")); 1075 1076 char *name = NULL; 1077 char *ppath = NULL; 1078 1079 if(cfg_name.length > 0) { 1080 name = cx_strdup_pool(pool, cfg_name).ptr; 1081 if(!name) return -1; 1082 } 1083 if(cfg_ppath.length > 0) { 1084 ppath = cx_strdup_pool(pool, cfg_ppath).ptr; 1085 if(!ppath) return -1; 1086 } 1087 1088 // create and add object 1089 httpd_object *obj = object_new(pool, name); 1090 if(!obj) return -1; 1091 obj->path = NULL; 1092 1093 conf->objects[i] = obj; 1094 1095 // add directives 1096 if(convert_objconf_directives(pool, file.ptr, obj, objnode->children_begin, NULL, &condition_index)) { 1097 return 1; 1098 } 1099 1100 // next 1101 i++; 1102 } 1103 1104 return 0; 1105 } 1106 1107 HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, cxmutstr file) { 1108 log_ereport(LOG_VERBOSE, "load_obj_conf"); 1109 1110 int ret = 0; 1111 1112 // create object config 1113 pool_handle_t *pool = scfg->pool; 1114 HTTPObjectConfig *conf = pool_calloc(pool, sizeof(HTTPObjectConfig), 1); 1115 if(!conf) { 1116 return NULL; 1117 } 1118 conf->pool = pool; 1119 1120 // load obj config file 1121 ObjectConfig2 *cfg = objectconf_load(file.ptr); 1122 if(!cfg) { 1123 return NULL; 1124 } 1125 1126 // convert ObjectConfig to HTTPObjectConfig 1127 1128 // add objects 1129 conf->nobj = serverconfig_children_count(cfg->root, CONFIG_NODE_OBJECT); 1130 conf->objects = pool_calloc(pool, conf->nobj, sizeof(httpd_object*)); 1131 if(conf->objects) { 1132 ret = convert_objconf(scfg, cfg, conf, file); 1133 } else { 1134 ret = -1; 1135 } 1136 1137 objectconf_free(cfg); 1138 1139 return !ret ? conf : NULL; 1140 } 1141 1142 int mime_conf_load(ServerConfiguration *cfg, cxmutstr file) { 1143 MimeConfig *mimecfg = load_mime_config(file.ptr); 1144 if(!mimecfg) { 1145 return -1; 1146 } 1147 1148 int ret = 0; 1149 1150 // cleanup in case of errors is done by the allocator 1151 MimeMap *mimemap = cxMalloc(cfg->a, sizeof(MimeMap)); 1152 CxMap *map = cxHashMapCreate(cfg->a, CX_STORE_POINTERS, (mimecfg->ntypes * 3) / 2); 1153 1154 if(mimemap && map) { 1155 mimemap->map = map; 1156 1157 // add ext type pairs 1158 for(MimeDirective *d=mimecfg->directives_begin;d;d=d->next) { 1159 // add the type for each extension to the map 1160 for(int i=0;i<d->nextensions;i++) { 1161 cxstring ext = d->extensions[i]; 1162 cxmutstr value = cx_strdup(cx_strn(d->type.ptr, d->type.length)); 1163 if(cxMapPut(map, cx_hash_key_bytes((const unsigned char *)ext.ptr, ext.length), value.ptr)) { 1164 log_ereport(LOG_CATASTROPHE, "OOM"); 1165 ret = -1; 1166 break; 1167 } 1168 } 1169 if(ret) { 1170 break; 1171 } 1172 } 1173 1174 cfg->mimetypes = mimemap; 1175 } else { 1176 log_ereport(LOG_CATASTROPHE, "OOM"); 1177 ret = -1; 1178 } 1179 1180 free_mime_config(mimecfg); 1181 return ret; 1182 } 1183 1184 1185 1186 ACLData* acl_conf_load(ServerConfiguration *cfg, const char *file) { 1187 ACLFile *aclfile = load_acl_file(file); 1188 if(!aclfile) { 1189 log_ereport(LOG_FAILURE, "Cannot load acl file %s", file); 1190 return NULL; 1191 } 1192 1193 // TODO: malloc return checks 1194 1195 ACLData *acldata = acl_data_new(cfg->a); 1196 CxIterator iter = cxListIterator(aclfile->namedACLs); 1197 cx_foreach(ACLConfig *, ac, iter) { 1198 ACLList *acl = acl_config_convert(cfg, ac); 1199 log_ereport(LOG_VERBOSE, "add acl: %.*s", (int)ac->id.length, ac->id.ptr); 1200 cxMapPut(acldata->namedACLs, cx_hash_key(ac->id.ptr, ac->id.length), acl); 1201 } 1202 free_acl_file(aclfile); 1203 1204 return acldata; 1205 } 1206 1207 ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl) { 1208 CxAllocator *a = cfg->a; 1209 1210 WSAcl *acllist = cxMalloc(cfg->a, sizeof(WSAcl)); 1211 acllist->acl.check = (acl_check_f)wsacl_check; 1212 acllist->acl.authdb = NULL; 1213 acllist->acl.authprompt = NULL; 1214 acllist->acl.isextern = 0; 1215 acllist->ace = NULL; 1216 acllist->ece = NULL; 1217 1218 if(acl->type.ptr && !cx_strcmp(cx_strn(acl->type.ptr, acl->type.length), cx_str("fs"))) { 1219 acllist->acl.isextern = 1; 1220 } 1221 1222 size_t s = CFG_ACE_LIST_SIZE(acl->entries); 1223 WSAce **tmp_aces = calloc(s, sizeof(WSAce*)); 1224 WSAce **tmp_eces = calloc(s, sizeof(WSAce*)); 1225 int ai = 0; 1226 int ei = 0; 1227 1228 // convert entries 1229 for(ACEConfig *acecfg=acl->entries;acecfg;acecfg=acecfg->next) { 1230 // copy data 1231 WSAce *ace = cxMalloc(a, sizeof(WSAce)); 1232 ace->access_mask = acecfg->access_mask; 1233 ace->flags = acecfg->flags; 1234 ace->type = acecfg->type; 1235 ace->who = cx_strdup_a(a, cx_strcast(acecfg->who)).ptr; 1236 1237 // add the entry to the correct array 1238 if(ace->type >= ACL_TYPE_AUDIT) { 1239 tmp_eces[ei] = ace; 1240 ei++; 1241 } else { 1242 tmp_aces[ai] = ace; 1243 ai++; 1244 } 1245 } 1246 1247 // create new entrie arrays with perfect fitting size 1248 if(ai > 0) { 1249 acllist->ace = cxCalloc(a, ai, sizeof(WSAce*)); 1250 } 1251 if(ei > 0) { 1252 acllist->ece = cxCalloc(a, ei, sizeof(WSAce*)); 1253 } 1254 memcpy(acllist->ace, tmp_aces, ai*sizeof(WSAce*)); 1255 memcpy(acllist->ece, tmp_eces, ei*sizeof(WSAce*)); 1256 acllist->acenum = ai; 1257 acllist->ecenum = ei; 1258 1259 free(tmp_aces); 1260 free(tmp_eces); 1261 1262 // get authentication information 1263 if(acl->authparam) { 1264 cxmutstr authdb_str = cfg_param_get(acl->authparam, cx_str("authdb")); 1265 cxmutstr prompt_str = cfg_param_get(acl->authparam, cx_str("prompt")); 1266 1267 if(authdb_str.ptr) { 1268 AuthDB *authdb = cxMapGet(cfg->authdbs, cx_hash_key(authdb_str.ptr, authdb_str.length)); 1269 acllist->acl.authdb = authdb; 1270 if(authdb && prompt_str.ptr) { 1271 acllist->acl.authprompt = cx_strdup_a(a, cx_strcast(prompt_str)).ptr; 1272 } 1273 } 1274 } 1275 1276 return &acllist->acl; 1277 } 1278 1279 AuthDB* keyfile_load(ServerConfiguration *cfg, cxstring file) { 1280 Keyfile *keyfile = keyfile_new(cfg->a); 1281 if(!keyfile) { 1282 return NULL; 1283 } 1284 1285 KeyfileConfig *conf = load_keyfile_config(file.ptr); 1286 if(!conf) { 1287 return NULL; 1288 } 1289 1290 AuthDB *ret = &keyfile->authdb; 1291 1292 for(KeyfileEntry *user=conf->users_begin;user;user=user->next) { 1293 if(keyfile_add_user( 1294 keyfile, 1295 user->name, 1296 user->hashtype, 1297 user->hashdata, 1298 user->groups, 1299 user->numgroups)) 1300 { 1301 ret = NULL; 1302 break; 1303 } 1304 } 1305 1306 free_keyfile_config(conf); 1307 1308 return ret; 1309 } 1310 1311 pblock* config_obj2pblock(pool_handle_t *pool, ConfigNode *obj) { 1312 pblock *pb = pblock_create_pool(pool, 8); 1313 for(ConfigNode *d=obj->children_begin;d;d=d->next) { 1314 if(d->type == CONFIG_NODE_DIRECTIVE && d->name.length > 0 && CFG_NUM_PARAMS(d->args) == 1) { 1315 ConfigParam *arg = d->args; 1316 pblock_nvlinsert(d->name.ptr, d->name.length, arg->value.ptr, arg->value.length, pb); 1317 } 1318 } 1319 return pb; 1320 } 1321