merges aio into default branch

Sat, 13 Jan 2018 19:01:00 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 13 Jan 2018 19:01:00 +0100
changeset 193
aa8393527b1e
parent 183
f33974f0dce0 (diff)
parent 192
6a145e13d933 (current diff)
child 194
6345f50208d5

merges aio into default branch

src/server/daemon/event.h file | annotate | diff | comparison | revisions
src/server/daemon/event_linux.c file | annotate | diff | comparison | revisions
src/server/daemon/event_solaris.c file | annotate | diff | comparison | revisions
src/server/daemon/httplistener.c file | annotate | diff | comparison | revisions
src/server/daemon/httprequest.c file | annotate | diff | comparison | revisions
src/server/daemon/sessionhandler.c file | annotate | diff | comparison | revisions
src/server/daemon/vfs.c file | annotate | diff | comparison | revisions
src/server/public/nsapi.h file | annotate | diff | comparison | revisions
src/server/util/io.c file | annotate | diff | comparison | revisions
src/server/util/io.h file | annotate | diff | comparison | revisions
--- a/make/install.mk	Sat Jan 13 18:48:19 2018 +0100
+++ b/make/install.mk	Sat Jan 13 19:01:00 2018 +0100
@@ -46,7 +46,9 @@
 	sed s:%%WS_HOST%%:$(HOST):g ../templates/config/server.template > $(INSTALL_DIR)/config/server.conf
 	@echo "copy binaries"
 	cp ../build/bin/webservd$(APP_EXT) $(INSTALL_DIR)/bin/
+	cp ../build/bin/wstool$(APP_EXT) $(INSTALL_DIR)/bin/
 	cp ../build/lib/libucx$(LIB_EXT) $(INSTALL_DIR)/lib/
+	cp ../build/lib/libwscfg$(LIB_EXT) $(INSTALL_DIR)/lib/
 	@echo "copy includes"
 	cp ../src/server/public/nsapi.h $(INSTALL_DIR)/include/nsapi.h
 	cp ../src/server/public/auth.h $(INSTALL_DIR)/include/auth.h
--- a/make/mingw.mk	Sat Jan 13 18:48:19 2018 +0100
+++ b/make/mingw.mk	Sat Jan 13 19:01:00 2018 +0100
@@ -33,6 +33,6 @@
 CXX = g++
 LD = g++
 
-SHLIB_CFLAGS = -fPIC
+SHLIB_CFLAGS = 
 SHLIB_LDFLAGS = -shared
 
--- a/make/solaris.mk	Sat Jan 13 18:48:19 2018 +0100
+++ b/make/solaris.mk	Sat Jan 13 19:01:00 2018 +0100
@@ -29,7 +29,7 @@
 
 # compiler and linker flags
 CFLAGS += -DSOLARIS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS
-LDFLAGS += -lsocket -lnsl -lsendfile -lposix4 -lpthread -ldl -lm -lldap
+LDFLAGS += -lsocket -lnsl -lsendfile -lposix4 -lpthread -ldl -lm -lmd -lldap
 
 #PLUGINS = java
 
--- a/src/Makefile	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/Makefile	Sat Jan 13 19:01:00 2018 +0100
@@ -28,11 +28,17 @@
 
 BUILD_ROOT = ../
 
-all: _ucx _server
+all: ucx server tools
 	
 
-_ucx:
+ucx: .FORCE
 	cd ucx; $(MAKE) 
 
-_server:
+server: ucx .FORCE
 	cd server; $(MAKE) 
+
+tools: server .FORCE
+	cd tools; $(MAKE)
+
+.FORCE:
+	
\ No newline at end of file
--- a/src/server/Makefile	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/Makefile	Sat Jan 13 19:01:00 2018 +0100
@@ -33,7 +33,9 @@
 
 MAIN_TARGET = $(BUILD_ROOT)/build/bin/webservd
 
-all: preparation $(MAIN_TARGET) $(PLUGINS)
+LIB_WSCFG = $(BUILD_ROOT)/build/lib/libwscfg$(LIB_EXT)
+
+all: preparation $(MAIN_TARGET) $(LIB_WSCFG) $(PLUGINS)
 
 include util/objs.mk
 include safs/objs.mk
@@ -65,13 +67,16 @@
 $(MAIN_TARGET): $(MAINOBJS)
 	$(CXX) -o $(MAIN_TARGET) $(MAINOBJS) -L$(BUILD_ROOT)/build/lib $(LDFLAGS)
 
+$(LIB_WSCFG): $(CONFOBJS)
+	$(CC) $(SHLIB_LDFLAGS) -o $@ $(CONFOBJS)
+
 
 ../../build/server/ucx/%.o: %.c
 	$(CC) -o $@ -c $(CFLAGS) $<
 
 
-$(PLUGINS): $(MAIN_TARGET)
+$(PLUGINS): $(MAIN_TARGET) FORCE
 	cd plugins/$@/; $(MAKE) all
 	
-	
+FORCE:
 
--- a/src/server/config/Makefile	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/config/Makefile	Sat Jan 13 19:01:00 2018 +0100
@@ -27,8 +27,5 @@
 #
 
 $(CONF_OBJPRE)%.o: config/%.c
-	$(CC) -o $@ -c $(CFLAGS) $<
+	$(CC) -o $@ -c $(CFLAGS) $(SHLIB_CFLAGS) $<
 
-$(CONF_OBJPRE)%.o: config/%.cpp
-	$(CXX) -o $@ -c $(CFLAGS) $<
-	
--- a/src/server/config/keyfile.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/config/keyfile.c	Sat Jan 13 19:01:00 2018 +0100
@@ -104,6 +104,10 @@
     
     if(!sstrcmp(hash_type, sstr("SSHA"))) {
         entry->hashtype = KEYFILE_SSHA;
+    } else if(!sstrcmp(hash_type, sstr("SSHA256"))) {
+        entry->hashtype = KEYFILE_SSHA256;
+    } else if(!sstrcmp(hash_type, sstr("SSHA512"))) {
+        entry->hashtype = KEYFILE_SSHA512;
     } else {
         // unkown hash type
         log_ereport(
--- a/src/server/daemon/httplistener.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/httplistener.c	Sat Jan 13 19:01:00 2018 +0100
@@ -50,6 +50,7 @@
 
 #include "../util/atomic.h"
 #include "httplistener.h"
+#include "netsite.h"
 
 #include "session.h"
 #include "configmanager.h"
--- a/src/server/daemon/httpparser.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/httpparser.c	Sat Jan 13 19:01:00 2018 +0100
@@ -35,7 +35,7 @@
 
 
 HttpParser* http_parser_new(HTTPRequest *request) {
-    HttpParser *parser = malloc(sizeof(HttpParser));
+    HttpParser *parser = calloc(1, sizeof(HttpParser));
     parser->request = request;
     
     parser->state = 0;
@@ -72,6 +72,18 @@
     return -1;
 }
 
+int http_parser_validate(HttpParser *parser) {
+    HTTPRequest *req = parser->request;
+    if(
+            !req->method.ptr || req->method.length == 0
+            || !req->uri.ptr || req->uri.length == 0
+            || !req->httpv.ptr || req->httpv.length == 0)
+    {
+        return 0;
+    }
+    return 1;
+}
+
 int get_start_line(HttpParser *parser) {
     netbuf *buf = parser->request->netbuf;
     while(buf->pos < buf->cursize) {
@@ -131,11 +143,15 @@
                 return 0;
             } else {
                 parser->offset = buf->pos;
-                if(parser->value.ptr != NULL) {
-                    parser->value.length = (buf->inbuf + buf->pos - 1)
+                if(parser->name.length != 0) {
+                    if(parser->value.ptr) {
+                        parser->value.length = (buf->inbuf + buf->pos - 1)
                             - (unsigned char*)parser->value.ptr;
-                    if(buf->inbuf[buf->pos-2] == '\r') {
-                        parser->value.length--;
+                        if(buf->inbuf[buf->pos-2] == '\r') {
+                            parser->value.length--;
+                        }
+                    } else {
+                        parser->value.ptr = "";
                     }
                     // add header
                     header_add(
--- a/src/server/daemon/httpparser.h	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/httpparser.h	Sat Jan 13 19:01:00 2018 +0100
@@ -74,6 +74,8 @@
  */
 int http_parser_process(HttpParser *parser);
 
+int http_parser_validate(HttpParser *parser);
+
 int get_start_line(HttpParser *parser);
 int http_parser_parse_header(HttpParser *parser);
 
--- a/src/server/daemon/httprequest.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/httprequest.c	Sat Jan 13 19:01:00 2018 +0100
@@ -46,8 +46,7 @@
 #include "error.h"
 
 void http_request_init(HTTPRequest *req) {
-    req->connection = NULL;
-    req->uri.ptr = NULL;
+    memset(req, 0, sizeof(HTTPRequest));
 
     HeaderArray *hd = malloc(sizeof(HeaderArray));
     hd->next = NULL;
@@ -155,14 +154,13 @@
     // Pass request line as "clf-request"
     // remove \r\n 
     sstr_t clfreq = request->request_line;
-    while(clfreq.ptr[clfreq.length - 1] < 33) {
+    while(clfreq.length > 0 && clfreq.ptr[clfreq.length - 1] < 33) {
         clfreq.length--;
     }
-    request->request_line = clfreq;
     pblock_kvinsert(
             pb_key_clf_request,
-            request->request_line.ptr,
-            request->request_line.length,
+            clfreq.ptr,
+            clfreq.length,
             rq->rq.reqpb);
 
     // Pass method as "method" in reqpb, and also as method_num
@@ -234,11 +232,22 @@
     }
     
     // Get abs_path part of request URI, and canonicalize the path
+    sstr_t orig_path = absPath;
     absPath.ptr = util_canonicalize_uri(
             pool,
             absPath.ptr,
             absPath.length,
             (int*)&absPath.length);
+    if(!absPath.ptr) {
+        log_ereport(
+                LOG_WARN,
+                "invalid request path: {%.*s}",
+                (int)orig_path.length,
+                orig_path.ptr);
+        pool_destroy(pool);
+        // TODO: 400 bad request
+        return 1;
+    }
 
     // Decode the abs_path
     if(util_uri_unescape_strict(absPath.ptr)) {
@@ -250,7 +259,12 @@
                 rq->rq.reqpb);
     } else {
         // TODO: log error
-        log_ereport(LOG_WARN, "uri unescape failed");
+        log_ereport(
+                LOG_WARN,
+                "uri unescape failed: {%.*s}",
+                (int)absPath.length,
+                absPath.ptr);
+        // TODO: 400 bad request
         pblock_kvinsert(pb_key_uri, "/", 1, rq->rq.reqpb);
     }
 
--- a/src/server/daemon/keyfile_auth.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/keyfile_auth.c	Sat Jan 13 19:01:00 2018 +0100
@@ -29,7 +29,14 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+
 #include <openssl/sha.h>
+#if defined(__sun) && defined(__SunOS_5_10)
+#include <sha2.h>
+#define SHA256_Init     SHA256Init
+#define SHA256_Update   SHA256Update
+#define SHA256_Final    SHA256Final
+#endif
 
 #include "../util/atomic.h"
 #include "../util/util.h"
@@ -113,10 +120,7 @@
 
 int keyfile_user_verify_password(User *user, char *password) {
     KeyfileUser *usr = (KeyfileUser*)user;
-    switch(usr->hash_type) {
-        case KEYFILE_SSHA: return ssha_verify(usr, password);
-    }
-    return 0;
+    return ssha_verify(usr, password);
 }
 
 int keyfile_user_check_group(User *user, char *group) {
@@ -137,26 +141,54 @@
 
 int ssha_verify(KeyfileUser *user, char *password) {
     /*
-     * SSHA: SHA1(pw + salt) + 8 bytes salt
+     * SSHA: SHA(pw + salt) + salt
      * user->hash is already base64 decoded
      */
     
-    // TODO: variable length salt
+    size_t hlen;
+    switch(user->hash_type) {
+        case KEYFILE_SSHA: hlen = 20; break;
+        case KEYFILE_SSHA256: hlen = 32; break;
+        case KEYFILE_SSHA512: hlen = 64; break;
+    }
     
-    char *salt = user->hash + user->hashlen - 8; // last 8 bytes are the salt 
-    size_t pwlen = strlen(password);
+    char *salt = user->hash + hlen;
+    size_t saltlen = user->hashlen - hlen;
+    
+    size_t pwlen = strlen(password); 
     
-    size_t saltpwlen = pwlen + 8;
-    char *saltpw = malloc(saltpwlen);
-    memcpy(saltpw, password, pwlen);
-    memcpy(saltpw + pwlen, salt, 8);
+    unsigned char pwhash[64];
+    switch(user->hash_type) {
+        case KEYFILE_SSHA: {
+            SHA_CTX ctx;
+            SHA1_Init(&ctx);
+            SHA1_Update(&ctx, password, pwlen);
+            SHA1_Update(&ctx, salt, saltlen);
+            SHA1_Final(pwhash, &ctx);
+            break;
+        }
+        case KEYFILE_SSHA256: {
+            SHA256_CTX ctx;
+            SHA256_Init(&ctx);
+            SHA256_Update(&ctx, password, pwlen);
+            SHA256_Update(&ctx, salt, saltlen);
+            SHA256_Final(pwhash, &ctx);
+            break;
+        }
+        case KEYFILE_SSHA512: {
+            SHA512_CTX ctx;
+            SHA512_Init(&ctx);
+            SHA512_Update(&ctx, password, pwlen);
+            SHA512_Update(&ctx, salt, saltlen);
+            SHA512_Final(pwhash, &ctx);
+            break;
+        }
+    }
     
-    unsigned char pwhash[20];
-    SHA1((const unsigned char*)saltpw, saltpwlen, pwhash);
-    
-    if(!memcmp(user->hash, pwhash, 20)) {
+    if(!memcmp(user->hash, pwhash, hlen)) {
         return 1;
     } else {
         return 0;
     }
 }
+
--- a/src/server/daemon/keyfile_auth.h	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/keyfile_auth.h	Sat Jan 13 19:01:00 2018 +0100
@@ -42,7 +42,9 @@
 typedef struct keyfile_user   KeyfileUser;
     
 enum KeyfileHashType {
-    KEYFILE_SSHA = 0
+    KEYFILE_SSHA = 0,
+    KEYFILE_SSHA256,
+    KEYFILE_SSHA512
 };
 
 struct keyfile {
--- a/src/server/daemon/log.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/log.c	Sat Jan 13 19:01:00 2018 +0100
@@ -36,6 +36,7 @@
 #include <unistd.h>
 #include <aio.h>
 #include <time.h>
+#include <pthread.h>
 
 #include "log.h"
 #include "../util/strbuf.h"
@@ -43,12 +44,17 @@
 #include "../util/atomic.h"
 
 #include <ucx/map.h>
+#include <ucx/list.h>
 
 static int is_initialized = 0;
 
 static int log_file_fd;
 static int log_level = 0;
 
+static uint32_t log_dup_count = 0;
+static UcxList *log_dup_list = NULL;
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
 WSBool main_is_daemon(void);
 
 /*
@@ -84,7 +90,8 @@
     "failure",
     "catastrophe",
     "info",
-    "verbose"
+    "verbose",
+    "debug"
 };
 
 static int can_log[] = {
@@ -94,7 +101,8 @@
     1, // failure
     1, // catastrophe
     1, // info
-    0  // verbose
+    0, // verbose
+    0  // debug
 };
 
 int init_log_file(LogConfig *cfg) {
@@ -114,10 +122,11 @@
         can_log[LOG_INFORM] = 0;
     } else if(!strcmp(cfg->level, "WARNING")) {
         can_log[LOG_INFORM] = 0;
-    } else if(!strcmp(cfg->level, "INFO")) {
-        
     } else if(!strcmp(cfg->level, "VERBOSE")) {
         can_log[LOG_VERBOSE] = 1;
+    } else if(!strcmp(cfg->level, "DEBUG")) {
+        can_log[LOG_VERBOSE] = 1;
+        can_log[LOG_DEBUG] = 1;
     }
     
     if(cfg->log_stdout) {
@@ -178,6 +187,21 @@
     if(!main_is_daemon()) {
         writev(STDOUT_FILENO, io, 2);
     }
+    
+    if(log_dup_count > 0) {
+        char *msg = malloc(len + 1);
+        memcpy(msg, str, len);
+        msg[len] = '\n';
+        
+        pthread_mutex_lock(&mutex);
+        UCX_FOREACH(elm, log_dup_list) {
+            LogDup *dup = elm->data;
+            dup->write(dup->cookie, msg, len + 1);
+        }        
+        pthread_mutex_unlock(&mutex);
+        
+        free(msg);
+    }
 }
 
 sstr_t log_get_prefix(int level) {
@@ -211,6 +235,27 @@
     return d;
 }
 
+void log_add_logdup(LogDup *dup) {
+    pthread_mutex_lock(&mutex);  
+    log_dup_list = ucx_list_append(log_dup_list, dup);
+    ws_atomic_inc32(&log_dup_count);   
+    pthread_mutex_unlock(&mutex);
+}
+
+void log_remove_logdup(LogDup *ldup) {
+    pthread_mutex_lock(&mutex);
+    UcxList *elm = log_dup_list;
+    while(elm) {
+        if(elm->data == ldup) {
+            log_dup_list = ucx_list_remove(log_dup_list, elm);
+            ws_atomic_dec32(&log_dup_count);
+            break;
+        }
+        elm = elm->next;
+    }
+    pthread_mutex_unlock(&mutex);
+}
+
 
 /*
  * log api functions
@@ -225,10 +270,10 @@
 }
 
 int log_ereport_v(int degree, const char *format, va_list args) { 
-    if(degree > 6) {
+    if(degree < 0 || degree > 7) {
         return 0;
     }
-    if(degree > 0 && !can_log[degree]) {
+    if(!can_log[degree]) {
         return 0;
     }
     
--- a/src/server/daemon/log.h	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/log.h	Sat Jan 13 19:01:00 2018 +0100
@@ -31,6 +31,7 @@
 
 #include "../public/nsapi.h"
 #include <ucx/string.h>
+#include <ucx/utils.h>
 
 #include <inttypes.h>
 
@@ -56,6 +57,11 @@
     LogFile  *log;
 } AccessLog;
 
+typedef void (*log_writefunc)(void *cookie, char *msg, size_t length);
+typedef struct {
+    log_writefunc write;
+    void *cookie;
+} LogDup;
 // server logging
 int init_log_file(LogConfig *cfg);
 
@@ -65,6 +71,8 @@
 
 sstr_t log_get_prefix(int level);
 
+void log_add_logdup(LogDup *dup);
+void log_remove_logdup(LogDup *dup);
 
 // access logging
 LogFile* get_access_log_file(sstr_t file);
--- a/src/server/daemon/main.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/main.c	Sat Jan 13 19:01:00 2018 +0100
@@ -39,10 +39,13 @@
 #include "../util/plist.h"
 #include "../util/date.h"
 
+#include "../../ucx/string.h"
+
 #include "webserver.h"
 #include "log.h"
 #include "httprequest.h"
 #include "httplistener.h"
+#include "srvctrl.h"
 
 #include "configmanager.h"
 
@@ -81,7 +84,7 @@
  */
 void sig_term(int sig) {
     webserver_shutdown();
-    exit(EXIT_SUCCESS);
+    //exit(EXIT_SUCCESS);
 }
 
 void* log_pipe_thread(void *data) {
@@ -164,17 +167,22 @@
     int status;
     status = webserver_init();
     if(status != 0) {
-        log_ereport(LOG_FAILURE, "Cannot initialize server.");
+        log_ereport(LOG_FAILURE, "cannot initialize server.");
         return EXIT_FAILURE;
     }
 
     status = webserver_run();
     if(status != 0) {
-        log_ereport(LOG_FAILURE, "Cannot run server.");
+        log_ereport(LOG_FAILURE, "cannot run server.");
         return EXIT_FAILURE;
     }
-     
+    
+    if(srvctrl_wait()) {
+        return EXIT_FAILURE;
+    }
+    
     /* TODO: join threads (or not?) */
+/*
     while(1) {
         if(is_daemon) {
             fflush(stdout);
@@ -185,6 +193,7 @@
             break;
         }
     }
+*/
 
     return EXIT_SUCCESS;
 }
--- a/src/server/daemon/netsite.h	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/netsite.h	Sat Jan 13 19:01:00 2018 +0100
@@ -142,6 +142,8 @@
 
 /* --- End public functions --- */
 
+int system_close(int fd);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/src/server/daemon/objs.mk	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/objs.mk	Sat Jan 13 19:01:00 2018 +0100
@@ -44,6 +44,7 @@
 DAEMONOBJ += sessionhandler.o
 DAEMONOBJ += vserver.o
 DAEMONOBJ += webserver.o
+DAEMONOBJ += srvctrl.o
 DAEMONOBJ += ws-fn.o
 DAEMONOBJ += configmanager.o
 DAEMONOBJ += log.o
--- a/src/server/daemon/sessionhandler.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/sessionhandler.c	Sat Jan 13 19:01:00 2018 +0100
@@ -55,7 +55,13 @@
 }
 
 void connection_close(Connection *conn) {
-    close(conn->fd);
+    while(close(conn->fd)) {
+        if(errno != EINTR) {
+            log_ereport(LOG_VERBOSE, "connection close failed: %s", strerror(errno));
+            break;
+        }
+        log_ereport(LOG_VERBOSE, "connection close: EINTR");
+    }
 }
 
 int connection_ssl_read(Connection *conn, void *buf, int len) {
@@ -82,7 +88,13 @@
             log_ereport(LOG_VERBOSE, "SSL_shutdown failed: %d", conn->ssl_error);
         }
     }
-    close(conn->fd);
+    while(close(conn->fd)) {
+        if(errno != EINTR) {
+            log_ereport(LOG_VERBOSE, "connection close failed: %s", strerror(errno));
+            break;
+        }
+        log_ereport(LOG_VERBOSE, "connection close: EINTR");
+    }
 }
 
 void connection_destroy(Connection *conn) {
@@ -170,6 +182,11 @@
         }
         buf->cursize += r;
     }
+    if(!http_parser_validate(parser)) {
+        log_ereport(LOG_FAILURE, "http_parser_validate failed");
+        // TODO: send error 400 bad request
+        return NULL;
+    }
     
     // process request
     r = handle_request(&request, NULL, NULL); // TODO: use correct thread pool
@@ -280,7 +297,7 @@
             case SSL_ERROR_WANT_ACCEPT: errstr = "SSL_ERROR_WANT_ACCEPT"; break;
             case SSL_ERROR_WANT_X509_LOOKUP: errstr = "SSL_ERROR_WANT_X509_LOOKUP"; break;
             case SSL_ERROR_SYSCALL: errstr = "SSL_ERROR_SYSCALL"; break;
-            case SSL_ERROR_SSL: errstr = "SL_ERROR_SSL"; break;
+            case SSL_ERROR_SSL: errstr = "SSL_ERROR_SSL"; break;
             
             log_ereport(LOG_VERBOSE, "SSL accept error[%d]: %s", error, errstr);
             event->finish = evt_request_error;
@@ -358,8 +375,15 @@
         event->finish = evt_request_error;
         io->error = 3;
         return 0;
+    }  
+    
+    if(!http_parser_validate(parser)) {
+        log_ereport(LOG_FAILURE, "http_parser_validate failed");
+        // TODO: send error 400 bad request
+        event->finish = evt_request_error;
+        return 0;
     }
-     
+    
     /*
      * process request
      * 
@@ -377,7 +401,7 @@
     int r = handle_request(request, NULL, h);
     if(r != 0) {
         // TODO: error message
-        close(request->connection->fd);
+        connection_destroy(request->connection);
     }
     
     /*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/daemon/srvctrl.c	Sat Jan 13 19:01:00 2018 +0100
@@ -0,0 +1,220 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "log.h"
+#include "webserver.h"
+
+#include "../util/systhr.h"
+
+#include "../../ucx/utils.h"
+#include "../../ucx/buffer.h"
+
+#include "srvctrl.h"
+
+#define SRVCTRL_THREAD_STACKSIZE 8192
+
+static int srvctrl;
+static sstr_t srvctrl_path;
+
+static WSBool srv_shutdown;
+
+int srvctrl_init(ServerConfiguration *cfg) {
+    // create srvctrl unix domain socket
+    // this socket is used for stop, reconfigure and other operations
+    srvctrl_path = ucx_sprintf("%s/private/srvctrl.sock", cfg->tmp.ptr);
+    struct sockaddr_un addr;
+    if(srvctrl_path.length > sizeof(addr.sun_path)-1) {
+        log_ereport(
+                LOG_CATASTROPHE,
+                "path '%s' too long for unix domain socket",
+                srvctrl_path.ptr);
+        return -1;
+    }
+    
+    // make sure there is no old srvctrl socket file
+    // otherweise bind would fail
+    if(unlink(srvctrl_path.ptr)) {
+        if(errno != ENOENT) {
+            log_ereport(
+                    LOG_CATASTROPHE,
+                    "cannot unlink old srvctrl socket '%s': %s",
+                    srvctrl_path.ptr,
+                    strerror(errno));
+            return -1;
+        }
+    }
+
+    ZERO(&addr, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    memcpy(addr.sun_path, srvctrl_path.ptr, srvctrl_path.length);
+    
+    srvctrl = socket(AF_UNIX, SOCK_STREAM, 0);
+    if(srvctrl == -1) {
+        log_ereport(
+                LOG_CATASTROPHE,
+                "Cannot create server control socket: %s",
+                strerror(errno));
+        return -1;
+    }
+    if(bind(srvctrl, (struct sockaddr*)&addr, sizeof(addr))) {
+        log_ereport(
+                LOG_CATASTROPHE,
+                "srvctrl socket bind failed: %s",
+                strerror(errno));
+        return -1;
+    }
+    
+    return 0;
+}
+
+int srvctrl_wait() {
+    listen(srvctrl, 8);
+    
+    for(;;) {
+        int fd = accept(srvctrl, NULL, 0);
+        if(fd < 0) {
+            if(srv_shutdown) break;
+            log_ereport(
+                    LOG_FAILURE,
+                    "srvctrl: accept failed: %s",
+                    strerror(errno));
+            break;
+        }
+        
+        SrvCtrlClient *client = srvctrl_create_client(fd);
+        SYS_THREAD t = systhread_start(
+                SYSTHREAD_DEFAULT_PRIORITY,
+                SRVCTRL_THREAD_STACKSIZE,
+                (thrstartfunc)srvctrl_thread,
+                client);
+        systhread_detach(t);
+        
+    }
+    
+    close(srvctrl);
+    unlink(srvctrl_path.ptr);
+    free(srvctrl_path.ptr);
+    
+    return 0;
+}
+
+void srvctrl_shutdown() {
+    srv_shutdown = TRUE;
+    shutdown(srvctrl, SHUT_RDWR);
+}
+
+SrvCtrlClient* srvctrl_create_client(int fd) {
+    SrvCtrlClient *client = malloc(sizeof(SrvCtrlClient));
+    ZERO(client, sizeof(SrvCtrlClient));
+    client->fd = fd;      
+    return client;
+}
+
+void* srvctrl_thread(SrvCtrlClient *client) {
+    LogDup log;
+    log.write = (log_writefunc)srvctrl_log;
+    log.cookie = client;
+    log_add_logdup(&log);
+    
+    char buf[64];
+    UcxBuffer *line = ucx_buffer_new(NULL, 32, UCX_BUFFER_AUTOEXTEND);
+    
+    ssize_t r;
+    WSBool br = FALSE;
+    while((r = read(client->fd, buf, 64)) > 0) {
+        for(int i=0;i<r;i++) {
+            char c = buf[i];
+            if(c == '\n') {
+                sstr_t ln = sstrn(line->space, line->pos);
+                if(srvctrl_handle_cmd(client, ln)) {
+                    br = TRUE;
+                    break;
+                }
+            } else {
+                ucx_buffer_putc(line, c);
+            }
+        }
+        if(br) {
+            break;
+        }
+    }
+    log_remove_logdup(&log);
+    
+    ucx_buffer_free(line);
+    close(client->fd);
+    free(client);
+    return NULL;
+}
+
+int srvctrl_handle_cmd(SrvCtrlClient *client, sstr_t cmd) {
+    if(!sstrcmp(cmd, S("reconfig"))) {
+        log_ereport(LOG_INFORM, "reconfigure server");
+        if(webserver_reconfig()) {
+            log_ereport(LOG_FAILURE, "cannot reload config");
+        } else {
+            log_ereport(LOG_INFORM, "reconfig: success");
+        }
+    } else if(!sstrcmp(cmd, S("shutdown"))) {
+        webserver_shutdown();
+    } else if(!sstrcmp(cmd, S("stat"))) {
+        // TODO: implement
+    } else if(!sstrcmp(cmd, S("log"))) {
+        return 0;
+    } else {
+        log_ereport(
+                LOG_FAILURE,
+                "unknown srvctrl command: %.*s",
+                (int)cmd.length,
+                cmd.ptr);
+    }
+    return 1;
+}
+
+void srvctrl_log(SrvCtrlClient *client, char *msg, size_t len) {
+    char msgheader[4];
+    msgheader[0] = SRV_MSG_LOG;
+    uint16_t msglen = len;
+    memcpy(&msgheader[1], &msglen, 2);
+    write(client->fd, msgheader, 3);
+    
+    size_t pos = 0;
+    ssize_t w = 0;
+    while(pos < len) {
+        w = write(client->fd, msg + pos, len - pos);
+        if(w <= 0) {
+            break;
+        }
+        pos += w;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/daemon/srvctrl.h	Sat Jan 13 19:01:00 2018 +0100
@@ -0,0 +1,67 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 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.
+ */
+
+#ifndef SRVCTRL_H
+#define SRVCTRL_H
+
+#include "../public/nsapi.h"
+
+#include "../../ucx/string.h"
+
+#include "config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SRV_MSG_RESPONSE 0
+#define SRV_MSG_LOG      1
+    
+typedef struct {
+    int fd;
+} SrvCtrlClient;
+    
+int srvctrl_init(ServerConfiguration *cfg);
+
+int srvctrl_wait();
+
+void srvctrl_shutdown();
+
+SrvCtrlClient* srvctrl_create_client(int fd);
+
+void* srvctrl_thread(SrvCtrlClient *client);
+int srvctrl_handle_cmd(SrvCtrlClient *client, sstr_t cmd);
+void srvctrl_log(SrvCtrlClient *client, char *msg, size_t len);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRVCTRL_H */
+
--- a/src/server/daemon/vfs.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/vfs.c	Sat Jan 13 19:01:00 2018 +0100
@@ -33,10 +33,10 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <aio.h>
-
 #include <ucx/map.h>
 
 #include "../util/pool.h"
+#include "netsite.h"
 #include "acl.h"
 #include "vfs.h"
 #include "threadpools.h"
@@ -296,14 +296,14 @@
     if(((oflags & O_CREAT) == O_CREAT) && sysacl.user_uid != -1) {
         if(fchown(fd, sysacl.user_uid, sysacl.user_gid)) {
             perror("vfs_open: fchown");
-            close(fd);
+            system_close(fd);
             return NULL;
         }
     }
     
     VFSFile *file = VFS_MALLOC(pool, sizeof(VFSFile));
     if(!file) {
-        close(fd);
+        system_close(fd);
         return NULL;
     }
     file->ctx = ctx;
@@ -511,7 +511,7 @@
 }
 
 void sys_file_close(SYS_FILE fd) {
-    close(fd->fd);
+    system_close(fd->fd);
 }
 
 int sys_file_aioread(aiocb_s *aiocb) {
--- a/src/server/daemon/webserver.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/webserver.c	Sat Jan 13 19:01:00 2018 +0100
@@ -48,6 +48,8 @@
 #include "../util/io.h"
 #include "../util/util.h"
 
+#include "../../ucx/utils.h"
+
 #include "../safs/common.h"
 
 #include "func.h"
@@ -57,6 +59,7 @@
 #include "webserver.h"
 #include "log.h"
 #include "auth.h"
+#include "srvctrl.h"
 
 extern struct FuncStruct webserver_funcs[];
 
@@ -91,20 +94,6 @@
     // init caches
     auth_cache_init();
     
-    // create tmp dir and pid file
-    char *mkdir_cmd = NULL;
-    asprintf(&mkdir_cmd, "mkdir -p %s", cfg->tmp.ptr);
-    system(mkdir_cmd);
-    free(mkdir_cmd);
-
-    char *pid_file_path = NULL;
-    asprintf(&pid_file_path, "%s/pid", cfg->tmp.ptr);
-    FILE *pidfile = fopen(pid_file_path, "w"); // TODO: check error
-    pid_t pid = getpid();
-    fprintf(pidfile, "%d", pid);
-    fclose(pidfile);
-    free(pid_file_path);
-    
     // init SAFs
     common_saf_init();
     
@@ -168,6 +157,50 @@
                     "server must be started as root to change uid");
     }
     
+    // create tmp dir and pid file
+    char *mkdir_cmd = NULL;
+    asprintf(&mkdir_cmd, "mkdir -p %s", cfg->tmp.ptr);
+    system(mkdir_cmd);
+    free(mkdir_cmd);
+
+    char *pid_file_path = NULL;
+    asprintf(&pid_file_path, "%s/pid", cfg->tmp.ptr);
+    FILE *pidfile = fopen(pid_file_path, "w"); // TODO: check error
+    pid_t pid = getpid();
+    fprintf(pidfile, "%d", pid);
+    fclose(pidfile);
+    free(pid_file_path);
+    
+    // create unix domain socket for server control
+    sstr_t tmp_priv = ucx_sprintf("%s/private", cfg->tmp.ptr);
+    // TODO: remove existing private dir
+    if(mkdir(tmp_priv.ptr, S_IRWXU)) {
+        if(errno == EEXIST) {
+            if(chmod(tmp_priv.ptr, S_IRWXU)) {
+                log_ereport(
+                        LOG_CATASTROPHE,
+                        "cannot change permissions of tmp dir %s:",
+                        tmp_priv.ptr,
+                        strerror(errno));
+                return 0;
+            }
+        } else {
+            log_ereport(
+                    LOG_CATASTROPHE,
+                    "cannot create tmp dir %s:",
+                    tmp_priv.ptr,
+                    strerror(errno));
+            return -1;
+        }
+    }
+    
+    
+    // create srvctrl unix domain socket
+    // this socket is used for stop, reconfigure and other operations
+    if(srvctrl_init(cfg)) {
+        return -1;
+    }    
+    
     //endpwent(); // TODO: close or not?
     //free(pwbuf); // TODO: ?
     
@@ -190,6 +223,8 @@
 void webserver_shutdown() {
     log_ereport(LOG_INFORM, "webserver shutdown");
     
+    srvctrl_shutdown();
+    
     // execute restart callbacks
     RestartCallback *re = atrestart;
     while(re) {
@@ -198,6 +233,16 @@
     }
 }
 
+int webserver_reconfig() {
+    if(cfgmgr_load_config(NULL) != 0) {
+        return -1;
+    }
+    // start newly created listeners
+    start_all_listener();
+    
+    return 0;
+}
+
 void webserver_atrestart(void (*fn)(void *), void *data) {
     RestartCallback *cb = malloc(sizeof(RestartCallback));
     cb->func = fn;
--- a/src/server/daemon/webserver.h	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/daemon/webserver.h	Sat Jan 13 19:01:00 2018 +0100
@@ -45,6 +45,7 @@
 int webserver_init();
 int webserver_run();
 void webserver_shutdown();
+int webserver_reconfig();
 
 void webserver_atrestart(void (*fn)(void *), void *data);
 
--- a/src/server/plugins/java/Makefile	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/plugins/java/Makefile	Sat Jan 13 19:01:00 2018 +0100
@@ -60,7 +60,7 @@
 	$(CC) -o $(NSAPI_JNI_LIB) $(JNI_OBJS) $(LDFLAGS)
 
 $(JAVA_WSRT): $(JAVASRC)
-	ant -Dbuild.compiler=javac1.7 compile jar
+	ant compile jar
 
 
 ../../../../build/server/plugins/java/%.o: %.c
--- a/src/server/public/nsapi.h	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/public/nsapi.h	Sat Jan 13 19:01:00 2018 +0100
@@ -176,6 +176,9 @@
  */
 #define LOG_VERBOSE 6
 
+// new
+#define LOG_DEBUG 7
+
 /*
  * The time format to use in the error log
  */
--- a/src/server/safs/cgi.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/safs/cgi.c	Sat Jan 13 19:01:00 2018 +0100
@@ -39,7 +39,7 @@
 #include "../util/util.h"
 #include "../util/pblock.h"
 #include "../../ucx/string.h"
-
+#include "../daemon/netsite.h"
 #include "../util/io.h"
 
 #include "cgiutils.h"
@@ -104,7 +104,10 @@
             r = netbuf_getbytes(sn->inbuf, buf, 4096);
             if(r <= 0) {
                 // TODO: handle error
-                log_ereport(LOG_FAILURE, "send-cgi: Cannot read request body");
+                log_ereport(
+                        LOG_FAILURE,
+                        "send-cgi: script: %s: cannot read request body",
+                        path);
                 kill(cgip.pid, SIGTERM);
                 cgi_close(&cgip);
                 return REQ_ABORTED;
@@ -114,22 +117,24 @@
                 // TODO: handle error
                 log_ereport(
                         LOG_FAILURE,
-                        "send-cgi: Cannot send request body to cgi process");
-                kill(cgip.pid, SIGTERM);
+                        "send-cgi: script: %s: cannot send request body to cgi process",
+                        path);
+                kill(cgip.pid, SIGKILL);
                 cgi_close(&cgip);
                 return REQ_ABORTED;
             }
             n += r;
         }
     }
-    close(cgip.in[1]);
+    system_close(cgip.in[1]);
     cgip.in[1] = -1;
     
     // read from child
     CGIResponseParser *parser = cgi_parser_new(sn, rq);
     WSBool cgiheader = TRUE;
     ssize_t wr = 0;
-    int result = REQ_PROCEED;   
+    int result = REQ_PROCEED;
+    size_t response_length = 0;
     while((r = read(cgip.out[0], buf, 4096)) > 0) {
         if(cgiheader) {
             size_t pos;
@@ -148,6 +153,7 @@
                 }
                 http_start_response(sn, rq);
                 if(pos < r) {
+                    response_length += r-pos;
                     wr = net_write(sn->csd, &buf[pos], r-pos);
                     if(wr <= 0) {
                         result = REQ_ABORTED;
@@ -156,6 +162,7 @@
                 }
             }
         } else {
+            response_length += r;
             wr = net_write(sn->csd, buf, r);
             if(wr <= 0) {
                 result = REQ_ABORTED;
@@ -164,6 +171,25 @@
         }
     }
     
+    char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs);
+    if(ctlen_header) {
+        int64_t ctlenhdr;
+        if(util_strtoint(ctlen_header, &ctlenhdr)) {
+            if(ctlenhdr != response_length) {
+                log_ereport(
+                        LOG_FAILURE,
+                        "cgi-send: script: %s: content length mismatch",
+                        path);
+                rq->rq_attr.keep_alive = 0;
+                result = REQ_ABORTED;
+            }
+        }
+    }
+    
+    if(result == REQ_ABORTED) {
+        log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", path);
+        kill(cgip.pid, SIGKILL);
+    }
     cgi_close(&cgip); // TODO: check return value
       
     cgi_parser_free(parser);
@@ -212,13 +238,13 @@
         
         // we need to close this unused pipe
         // otherwise stdin cannot reach EOF
-        close(p->in[1]);
+        system_close(p->in[1]);
         
         // execute program
         exit(execve(script.ptr, argv, envp));
     } else {
         // parent  
-        close(p->out[1]);
+        system_close(p->out[1]);
         p->out[1] = -1;
     }
     
@@ -230,16 +256,16 @@
     waitpid(p->pid, &status, 0);
     
     if(p->in[0] != -1) {
-        close(p->in[0]);
+        system_close(p->in[0]);
     }
     if(p->in[1] != -1) {
-        close(p->in[1]);
+        system_close(p->in[1]);
     }
     if(p->out[0] != -1) {
-        close(p->out[0]);
+        system_close(p->out[0]);
     }
     if(p->out[1] != -1) {
-        close(p->out[1]);
+        system_close(p->out[1]);
     }
     
     return 0;
@@ -408,4 +434,3 @@
         }
     }
 }
-
--- a/src/server/safs/common.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/safs/common.c	Sat Jan 13 19:01:00 2018 +0100
@@ -162,7 +162,31 @@
         case COMMONSAF_REMOVE_HEADERS: pblock_remove(value, rq->headers);break;
         case COMMONSAF_REMOVE_SRVHDRS: pblock_remove(value, rq->srvhdrs); break;
         case COMMONSAF_ABORT: return COMMONSAF_REQ_ABORTED;
+        case COMMONSAF_NOACTION: return COMMONSAF_RET_NOACTION;
         case COMMONSAF_ERROR: {
+            int len = strlen(value);
+            WSBool isnum = TRUE;
+            int i;
+            for(i=0;i<len;i++) {
+                if(!isdigit(value[i])) {
+                    isnum = FALSE;
+                    break;
+                }
+            }
+            
+            int64_t status;
+            int ret = util_strtoint(value, &status);
+            if(status < 100 || ret > 999 || !ret) {
+                log_ereport(
+                        LOG_MISCONFIG,
+                        "set-variable: error value must contain a 3-digit http status code");
+                protocol_status(sn, rq, 500, NULL);
+                return COMMONSAF_RET_ERROR;
+            }
+            
+            char *msg = isnum ? NULL : sstrtrim(sstr(value + i)).ptr;
+            protocol_status(sn, rq, (int)status, msg);
+            
             return COMMONSAF_REQ_ABORTED;
         }
         case COMMONSAF_ESCAPE: break;
@@ -173,7 +197,10 @@
             rq->rq_attr.keep_alive = util_getboolean(var, 0);
             break;
         }
-        case COMMONSAF_NAME: break;
+        case COMMONSAF_NAME: {
+            pblock_kvinsert(pb_key_name, value, strlen(value), rq->vars);
+            break;
+        }
         case COMMONSAF_SENTHDRS: break;
         case COMMONSAF_STOP: return COMMONSAF_RET_STOP;
         case COMMONSAF_URL: break;
--- a/src/server/util/io.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/util/io.c	Sat Jan 13 19:01:00 2018 +0100
@@ -64,6 +64,7 @@
 #include "../daemon/vfs.h"
 #include "io.h"
 #include "pool.h"
+#include "../daemon/netsite.h"
 #include "../daemon/event.h"
 #include "ucx/utils.h"
 
@@ -177,7 +178,7 @@
 #endif
 
 void net_sys_close(SysStream *st) {
-    close(st->fd);
+    system_close(st->fd);
 }
 
 void net_sys_setmode(SysStream *st, int mode) {
@@ -380,7 +381,7 @@
     if(ret != 1) {
         st->error = SSL_get_error(st->ssl, ret);
     }
-    close(SSL_get_fd(st->ssl));
+    system_close(SSL_get_fd(st->ssl));
 }
 
 void net_ssl_finish(SSLStream *st) {
--- a/src/server/util/system.c	Sat Jan 13 18:48:19 2018 +0100
+++ b/src/server/util/system.c	Sat Jan 13 19:01:00 2018 +0100
@@ -328,6 +328,17 @@
     return dir;
 }
 
+int system_close(int fd) {
+    while(close(fd)) {
+        if(errno != EINTR) {
+            return -1;
+        } else {
+            log_ereport(LOG_VERBOSE, "close interrupted by signal"); // TODO: use debug log level
+        }
+    }
+    return 0;
+}
+
 NSAPI_PUBLIC int 
 getThreadMallocKey(void)
 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/Makefile	Sat Jan 13 19:01:00 2018 +0100
@@ -0,0 +1,51 @@
+#
+# 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.
+#
+
+BUILD_ROOT = ../..
+
+include $(BUILD_ROOT)/config.mk
+
+CFLAGS += -I..
+LDFLAGS += -L../../build/lib -lucx -lwscfg
+
+# list of source files
+WSTOOL_SRC = wstool.c
+WSTOOL_SRC += srvctrlsocket.c
+
+WSTOOL_OBJ = $(WSTOOL_SRC:%.c=$(BUILD_ROOT)/build/tools/%$(OBJ_EXT))
+
+all: $(BUILD_ROOT)/build/tools $(BUILD_ROOT)/build/bin/wstool
+
+$(BUILD_ROOT)/build/bin/wstool: $(BUILD_ROOT)/build/tools $(WSTOOL_OBJ)
+	$(CC) -o $@ $(WSTOOL_OBJ) $(LDFLAGS)
+
+$(BUILD_ROOT)/build/tools/%$(OBJ_EXT): %.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+$(BUILD_ROOT)/build/tools:
+	mkdir -p $(BUILD_ROOT)/build/tools
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/srvctrlsocket.c	Sat Jan 13 19:01:00 2018 +0100
@@ -0,0 +1,112 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 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 "srvctrlsocket.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+SrvConnection* srvctrl_connet(char *socketfile) {
+    if(!socketfile) {
+        fprintf(stderr, "srvctrl_connect: no socketfile\n");
+        return NULL;
+    }
+    size_t len = strlen(socketfile);
+    if(len == 0) {
+        fprintf(stderr, "srvctrl_connect: invalid socket path\n");
+        return NULL;
+    }
+    if(len > 100) {
+        fprintf(stderr, "srvctrl_connect: socket path too long\n");
+        return NULL;
+    }
+    
+    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if(fd == -1) {
+        perror("srvctrl_connect: cannot create socket");
+        return NULL;
+    }
+    
+    struct sockaddr_un addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    memcpy(addr.sun_path, socketfile, len);
+    
+    if(connect(fd, (struct sockaddr*)&addr, sizeof(addr))) {
+        perror("srvctrl_connect");
+        close(fd);
+        return NULL;
+    }
+    
+    FILE *stream = fdopen(fd, "r+");
+    if(!stream) {
+        close(fd);
+        return NULL;
+    }
+    
+    SrvConnection *conn = calloc(1, sizeof(SrvConnection));
+    conn->socket = fd;
+    conn->stream = stream;
+    
+    return conn;
+}
+
+void srvctrl_close(SrvConnection *conn) {
+    fclose(conn->stream);
+    free(conn);
+}
+
+int srvctrl_readmsg(SrvConnection *conn, SrvMsg *msg) {
+    int type = 0;
+    uint16_t length;
+    
+    type = fgetc(conn->stream);
+    if(type == EOF) {
+        return 1;
+    }
+    
+    if(fread(&length, 1, 2, conn->stream) != 2) {
+        return -1;
+    }
+    
+    msg->type = type;
+    msg->length = length;
+    msg->message = malloc(length);
+    
+    size_t r = fread(msg->message, 1, length, conn->stream);
+    if(r != length) {
+        free(msg->message);
+        return -1;
+    }
+    
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/srvctrlsocket.h	Sat Jan 13 19:01:00 2018 +0100
@@ -0,0 +1,75 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 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.
+ */
+
+#ifndef SRVCTRLSOCKET_H
+#define SRVCTRLSOCKET_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct SrvConnection {
+    int  socket;
+    FILE *stream;
+} SrvConnection;
+
+typedef struct SrvMsg {
+    /*
+     * message type
+     * 0: cmd response
+     * 1: log message
+     */
+    int type;
+    
+    /*
+     * message data
+     */
+    char *message;
+    
+    /*
+     * message length
+     */
+    size_t length;
+} SrvMsg;
+
+SrvConnection* srvctrl_connet(char *socketfile);
+
+void srvctrl_close(SrvConnection *conn);
+
+int srvctrl_readmsg(SrvConnection *conn, SrvMsg *msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRVCTRLSOCKET_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/wstool.c	Sat Jan 13 19:01:00 2018 +0100
@@ -0,0 +1,123 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 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 "wstool.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../server/config/serverconf.h"
+
+#include "srvctrlsocket.h"
+
+static void print_info(char *cmd) {
+    fprintf(stderr, "usage:\n");
+    fprintf(stderr, "%s -t <srvconfigfile>\n", cmd);
+    fprintf(stderr, "%s -s <socketpath> <command>\n", cmd);
+    fprintf(stderr, "Commands: reconfig, shutdown, stat, log\n");
+}
+
+int main(int argc, char **argv) {
+    if(argc > 2) {
+        if(!strcmp(argv[1], "-t")) {
+            return tool_get_tmpdir(argv[2]);
+        } else if(!strcmp(argv[1], "-s")) {
+            if(argc != 4) {
+                print_info(argv[0]);
+                return -2;
+            }
+            return tool_srvctrl(argv[2], argv[3]);
+        }
+    }
+    
+    print_info(argv[0]);
+    return -2;
+}
+
+int tool_get_tmpdir(char *configfile) {
+    ServerConfig *serverconf = load_server_config(configfile);
+    UcxList *list = ucx_map_sstr_get(serverconf->objects, sstrn("Runtime", 7));
+    if(!list) {
+        fprintf(stderr, "Error: No Runtime element in %s\n", configfile);
+        return -1;
+    }
+    if(ucx_list_size(list) != 1) {
+        fprintf(stderr, "Error: Multiple Runtime elements in %s\n", configfile);
+        return -1;
+    }
+    ServerConfigObject *runtime = list->data;
+    sstr_t tmp = cfg_directivelist_get_str(runtime->directives, sstr("Temp"));
+    if(!tmp.ptr) {
+        fprintf(stderr, "Error: No Temp directive in Runtime Object\n");
+        return -1;
+    }
+    
+    printf("%.*s\n", (int)tmp.length, tmp.ptr);
+    
+    return 0;
+}
+
+int tool_srvctrl(char *socketfile, char *cmd) {
+    SrvConnection *srv = srvctrl_connet(socketfile);
+    if(!srv) {
+        return -1;
+    }
+    
+    fprintf(srv->stream, "%s\n", cmd);
+    fflush(srv->stream);
+    
+    SrvMsg msg;
+    while(!srvctrl_readmsg(srv, &msg)) {
+        if(msg.type == 0) {
+            fprintf(stdout, "%.*s", (int)msg.length, msg.message);
+            fflush(stdout);
+        } else if(msg.type == 1) {
+            fprintf(stderr, "%.*s", (int)msg.length, msg.message);
+            fflush(stderr);
+        }
+    }
+    
+    srvctrl_close(srv);
+    
+    return 0;
+}
+
+
+
+int log_ereport(int degree, const char *format, ...) {
+    va_list args;
+    va_start(args, format);
+    int ret = log_ereport_v(degree, format, args);
+    va_end(args);
+    return ret;
+}
+
+int log_ereport_v(int degree, const char *format, va_list args) { 
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/wstool.h	Sat Jan 13 19:01:00 2018 +0100
@@ -0,0 +1,50 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 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.
+ */
+
+#ifndef WSTOOL_H
+#define WSTOOL_H
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int tool_get_tmpdir(char *configfile);
+
+int tool_srvctrl(char *socketfile, char *cmd);
+
+int log_ereport(int degree, const char *format, ...);
+int log_ereport_v(int degree, const char *format, va_list args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WSTOOL_H */
+
--- a/templates/bin/reconfig.template	Sat Jan 13 18:48:19 2018 +0100
+++ b/templates/bin/reconfig.template	Sat Jan 13 19:01:00 2018 +0100
@@ -1,6 +1,57 @@
 #!/bin/sh
 
-PID=`cat /tmp/webserver-rw6pgl8b/pid`
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2017 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.
+#
 
-kill -USR1 $PID
+WS_INSTALL_DIR=%%WS_INSTALL_DIR%%
+
+cd $WS_INSTALL_DIR
+
+WS_TMP_DIR=`bin/wstool -t config/server.conf`
+if [ $? -ne 0 ]; then
+	exit 1
+fi
 
+WS_PID=`cat $WS_TMP_DIR/pid 2> /dev/null`
+if [ $? -ne 0 ]; then
+	echo "cannot get server pid"
+	exit 1
+fi
+if [ -z $WS_PID ]; then
+	echo "cannot get server pid"
+	exit 1
+fi
+
+kill -0 $WS_PID 2> /dev/null
+if [ $? -ne 0 ]; then
+	echo "server not running"
+	exit 1
+fi
+
+bin/wstool -s $WS_TMP_DIR/private/srvctrl.sock reconfig
+
--- a/templates/bin/startserv.template	Sat Jan 13 18:48:19 2018 +0100
+++ b/templates/bin/startserv.template	Sat Jan 13 19:01:00 2018 +0100
@@ -3,7 +3,7 @@
 #
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 #
-# Copyright 2011 Olaf Wintermann. All rights reserved.
+# Copyright 2017 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:
--- a/templates/bin/stopserv.template	Sat Jan 13 18:48:19 2018 +0100
+++ b/templates/bin/stopserv.template	Sat Jan 13 19:01:00 2018 +0100
@@ -1,6 +1,56 @@
 #!/bin/sh
 
-PID=`cat /tmp/webserver-rw6pgl8b/pid`
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2017 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.
+#
 
-kill -TERM $PID
+WS_INSTALL_DIR=%%WS_INSTALL_DIR%%
+
+cd $WS_INSTALL_DIR
+
+WS_TMP_DIR=`bin/wstool -t config/server.conf`
+if [ $? -ne 0 ]; then
+	exit 1
+fi
 
+WS_PID=`cat $WS_TMP_DIR/pid 2> /dev/null`
+if [ $? -ne 0 ]; then
+	echo "cannot get server pid"
+	exit 1
+fi
+if [ -z $WS_PID ]; then
+	echo "cannot get server pid"
+	exit 1
+fi
+
+kill -0 $WS_PID 2> /dev/null
+if [ $? -ne 0 ]; then
+	echo "server not running"
+	exit 1
+fi
+
+bin/wstool -s $WS_TMP_DIR/private/srvctrl.sock shutdown

mercurial