merge srvctrl into default branch

Mon, 06 Mar 2017 17:32:26 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 06 Mar 2017 17:32:26 +0100
changeset 179
ef6827505bd2
parent 174
8f2a834d1d68 (diff)
parent 178
4760f7f1b197 (current diff)
child 180
98462e878ca7

merge srvctrl into default branch

src/server/Makefile file | annotate | diff | comparison | revisions
src/server/daemon/webserver.c file | annotate | diff | comparison | revisions
src/server/util/system.c file | annotate | diff | comparison | revisions
--- a/make/mingw.mk	Mon Mar 06 17:30:52 2017 +0100
+++ b/make/mingw.mk	Mon Mar 06 17:32:26 2017 +0100
@@ -33,6 +33,6 @@
 CXX = g++
 LD = g++
 
-SHLIB_CFLAGS = -fPIC
+SHLIB_CFLAGS = 
 SHLIB_LDFLAGS = -shared
 
--- a/make/solaris.mk	Mon Mar 06 17:30:52 2017 +0100
+++ b/make/solaris.mk	Mon Mar 06 17:32:26 2017 +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/server/Makefile	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/Makefile	Mon Mar 06 17:32:26 2017 +0100
@@ -75,8 +75,8 @@
 	$(CC) -o $@ -c $(CFLAGS) $<
 
 
-$(PLUGINS): $(MAIN_TARGET)
+$(PLUGINS): $(MAIN_TARGET) FORCE
 	cd plugins/$@/; $(MAKE) all
 	
-	
+FORCE:
 
--- a/src/server/config/keyfile.c	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/config/keyfile.c	Mon Mar 06 17:32:26 2017 +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	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/daemon/httplistener.c	Mon Mar 06 17:32:26 2017 +0100
@@ -50,6 +50,7 @@
 
 #include "../util/atomic.h"
 #include "httplistener.h"
+#include "netsite.h"
 
 #include "session.h"
 #include "configmanager.h"
@@ -417,12 +418,12 @@
                     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);
                 free(conn);
                 conn = NULL;
-                close(clientfd);
+                system_close(clientfd);
             } else {
                 conn->ssl = ssl;
                 conn->read = connection_ssl_read;
--- a/src/server/daemon/httpparser.c	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/daemon/httpparser.c	Mon Mar 06 17:32:26 2017 +0100
@@ -131,11 +131,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/httprequest.c	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/daemon/httprequest.c	Mon Mar 06 17:32:26 2017 +0100
@@ -234,11 +234,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 +261,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	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/daemon/keyfile_auth.c	Mon Mar 06 17:32:26 2017 +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	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/daemon/keyfile_auth.h	Mon Mar 06 17:32:26 2017 +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/netsite.h	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/daemon/netsite.h	Mon Mar 06 17:32:26 2017 +0100
@@ -142,6 +142,8 @@
 
 /* --- End public functions --- */
 
+int system_close(int fd);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/src/server/daemon/sessionhandler.c	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/daemon/sessionhandler.c	Mon Mar 06 17:32:26 2017 +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) {
@@ -320,7 +332,7 @@
     int r = handle_request(request, NULL);
     if(r != 0) {
         // TODO: error message
-        close(request->connection->fd);
+        connection_destroy(request->connection);
     }
     
     /*
--- a/src/server/daemon/vfs.c	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/daemon/vfs.c	Mon Mar 06 17:32:26 2017 +0100
@@ -32,10 +32,10 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
-
 #include <ucx/map.h>
 
 #include "../util/pool.h"
+#include "netsite.h"
 #include "acl.h"
 #include "vfs.h"
 
@@ -146,7 +146,7 @@
     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;
         }
     }
@@ -155,7 +155,7 @@
     VFSFile *file = pool ?
             pool_malloc(pool, sizeof(VFSFile)) : malloc(sizeof(VFSFile));
     if(!file) {
-        close(fd);
+        system_close(fd);
         return NULL;
     }
     file->ctx = ctx;
@@ -487,7 +487,7 @@
 }
 
 void sys_file_close(SYS_FILE fd) {
-    close(fd->fd);
+    system_close(fd->fd);
 }
 
 int sys_dir_read(VFS_DIR dir, VFS_ENTRY *entry, int getstat) {
--- a/src/server/safs/cgi.c	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/safs/cgi.c	Mon Mar 06 17:32:26 2017 +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	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/safs/common.c	Mon Mar 06 17:32:26 2017 +0100
@@ -38,7 +38,7 @@
 static UcxMap *var_names;
 
 enum SAFVarNames {
-    COMMONSAF_INSERT_CLIENT = 0,
+    COMMONSAF_INSERT_CLIENT = 1,
     COMMONSAF_INSERT_VARS,
     COMMONSAF_INSERT_REQPB,
     COMMONSAF_INSERT_HEADERS,
@@ -74,7 +74,7 @@
 #define COMMONSAF_RET_ERROR -2
 
 void common_saf_init() {
-    var_names = ucx_map_new(12);
+    var_names = ucx_map_new(32);
     
     ucx_map_cstr_put(var_names, "insert-client", (intptr_t)COMMONSAF_INSERT_CLIENT);
     ucx_map_cstr_put(var_names, "insert-vars", (intptr_t)COMMONSAF_INSERT_VARS);
@@ -114,27 +114,79 @@
     return REQ_NOACTION;
 }
 
+static void var_set(char *value, pblock *pb, WSBool insert) {
+    sstr_t n;
+    sstr_t v;
+    v.ptr = NULL;
+    
+    n.ptr = value;
+    int i;
+    int len = strlen(value);
+    for(i=1;i<len;i++) {
+        if(value[i] == '=') {
+            n.length = i;
+            v = sstrsubs(sstrn(value, len), i + 1);
+            break;
+        }
+    }
+    if(!v.ptr || v.length == 0) {
+        log_ereport(
+                LOG_MISCONFIG,
+                "set-variable: string '%s' has not name=value format",
+                value);
+        return;
+    }
+    
+    if(!insert) {
+        // TODO
+    }
+    pblock_nvlinsert(n.ptr, n.length, v.ptr, v.length, pb);
+}
 
 static int set_var(Session *sn, Request *rq, char *var, char *value) {
     intptr_t v = (intptr_t)ucx_map_cstr_get(var_names, var);
     switch(v) {
         default: break;
-        case COMMONSAF_INSERT_CLIENT: break;
-        case COMMONSAF_INSERT_VARS: break;
-        case COMMONSAF_INSERT_REQPB: break;
-        case COMMONSAF_INSERT_HEADERS: break;
-        case COMMONSAF_INSERT_SRVHDRS: break;
-        case COMMONSAF_SET_CLIENT: break;
-        case COMMONSAF_SET_VARS: break;
-        case COMMONSAF_SET_REQPB: break;
-        case COMMONSAF_SET_HEADERS: break;
-        case COMMONSAF_SET_SRVHDRS: break;
-        case COMMONSAF_REMOVE_CLIENT: break;
-        case COMMONSAF_REMOVE_VARS: break;
-        case COMMONSAF_REMOVE_HEADERS: break;
-        case COMMONSAF_REMOVE_SRVHDRS: break;
+        case COMMONSAF_INSERT_CLIENT: var_set(value, sn->client, TRUE); break;
+        case COMMONSAF_INSERT_VARS: var_set(value, rq->vars, TRUE); break;
+        case COMMONSAF_INSERT_REQPB: var_set(value, rq->reqpb, TRUE); break;
+        case COMMONSAF_INSERT_HEADERS: var_set(value, rq->headers, TRUE); break;
+        case COMMONSAF_INSERT_SRVHDRS: var_set(value, rq->srvhdrs, TRUE); break;
+        case COMMONSAF_SET_CLIENT: var_set(value, sn->client, FALSE); break;
+        case COMMONSAF_SET_VARS: var_set(value, rq->vars, FALSE); break;
+        case COMMONSAF_SET_REQPB: var_set(value, rq->reqpb, FALSE); break;
+        case COMMONSAF_SET_HEADERS: var_set(value, rq->headers, FALSE); break;
+        case COMMONSAF_SET_SRVHDRS: var_set(value, rq->srvhdrs, FALSE); break;
+        case COMMONSAF_REMOVE_CLIENT: pblock_remove(value, sn->client); break;
+        case COMMONSAF_REMOVE_VARS: pblock_remove(value, rq->vars); break;
+        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;
@@ -145,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	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/util/io.c	Mon Mar 06 17:32:26 2017 +0100
@@ -64,6 +64,7 @@
 #include "../daemon/vfs.h"
 #include "io.h"
 #include "pool.h"
+#include "../daemon/netsite.h"
 #include "ucx/utils.h"
 
 IOStream native_io_funcs = {
@@ -167,7 +168,7 @@
 #endif
 
 void net_sys_close(SysStream *st) {
-    close(st->fd);
+    system_close(st->fd);
 }
 
 #elif defined(XP_WIN32)
@@ -334,7 +335,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	Mon Mar 06 17:30:52 2017 +0100
+++ b/src/server/util/system.c	Mon Mar 06 17:32:26 2017 +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)
 {

mercurial