]> granicus.if.org Git - transmission/commitdiff
(third-party) upgrade shttpd from 1.39 to the latest, 1.41
authorCharles Kerr <charles@transmissionbt.com>
Wed, 16 Jul 2008 23:55:49 +0000 (23:55 +0000)
committerCharles Kerr <charles@transmissionbt.com>
Wed, 16 Jul 2008 23:55:49 +0000 (23:55 +0000)
15 files changed:
libtransmission/rpc-server.c
third-party/shttpd/Makefile.am
third-party/shttpd/README
third-party/shttpd/auth.c
third-party/shttpd/cgi.c
third-party/shttpd/compat_unix.h
third-party/shttpd/config.c [deleted file]
third-party/shttpd/config.h
third-party/shttpd/defs.h
third-party/shttpd/io_emb.c
third-party/shttpd/io_file.c
third-party/shttpd/io_ssl.c
third-party/shttpd/shttpd.c
third-party/shttpd/shttpd.h
third-party/shttpd/standalone.c

index 106aca6826bdb29ace08529afcbe0c9ce28cd67b..083308d135310752e2b0d49a4e91aa2c101d0659 100644 (file)
@@ -328,7 +328,9 @@ startServer( tr_rpc_server * server )
 
     if( !server->ctx )
     {
-        char ports[128];
+        int i;
+        int argc = 0;
+        char * argv[100];
         char passwd[MAX_PATH_LENGTH];
         const char * clutchDir = tr_getClutchDir( server->session );
         struct timeval tv = tr_timevalMsec( INACTIVE_INTERVAL_MSEC );
@@ -339,36 +341,41 @@ startServer( tr_rpc_server * server )
         else
             edit_passwords( passwd, MY_REALM, server->username, server->password );
 
-        server->ctx = shttpd_init( );
-        tr_snprintf( ports, sizeof( ports ), "%d", server->port );
-        shttpd_register_uri( server->ctx, "/transmission/rpc", handle_rpc, server );
-        shttpd_register_uri( server->ctx, "/transmission/upload", handle_upload, server );
-
-        if( clutchDir && *clutchDir ) {
-            char * clutchAlias = tr_strdup_printf( "%s=%s,%s=%s",
-                "/transmission/clutch", clutchDir,
-                "/transmission/web", clutchDir );
-            tr_inf( _( "Serving the web interface files from \"%s\"" ), clutchDir );
-            shttpd_set_option( server->ctx, "aliases", clutchAlias );
-            tr_free( clutchAlias );
+        argv[argc++] = tr_strdup( "appname-unused" );
+        argv[argc++] = tr_strdup( "-ports" );
+        argv[argc++] = tr_strdup_printf( "%d", server->port );
+        argv[argc++] = tr_strdup( "-dir_list" );
+        argv[argc++] = tr_strdup( "0" );
+        argv[argc++] = tr_strdup( "-auth_realm" );
+        argv[argc++] = tr_strdup( MY_REALM );
+        if( server->acl )
+        {
+            argv[argc++] = tr_strdup( "-acl" );
+            argv[argc++] = tr_strdup( server->acl );
         }
-
-        shttpd_set_option( server->ctx, "ports", ports );
-        shttpd_set_option( server->ctx, "dir_list", "0" );
-        //shttpd_set_option( server->ctx, "root", "/dev/null" );
-        shttpd_set_option( server->ctx, "auth_realm", MY_REALM );
-        if( server->acl ) {
-            dbgmsg( "setting acl [%s]", server->acl );
-            shttpd_set_option( server->ctx, "acl", server->acl );
+        if( server->isPasswordEnabled )
+        {
+            argv[argc++] = tr_strdup( "-protect" );
+            argv[argc++] = tr_strdup_printf( "/transmission=%s", passwd );
         }
-        if( server->isPasswordEnabled ) {
-            char * buf = tr_strdup_printf( "/transmission=%s", passwd );
-            shttpd_set_option( server->ctx, "protect", buf );
-            tr_free( buf );
+        if( clutchDir && *clutchDir )
+        {
+            tr_inf( _( "Serving the web interface files from \"%s\"" ), clutchDir );
+            argv[argc++] = tr_strdup( "-aliases" );
+            argv[argc++] = tr_strdup_printf( "%s=%s,%s=%s",
+                                             "/transmission/clutch", clutchDir,
+                                             "/transmission/web", clutchDir );
         }
 
+        server->ctx = shttpd_init( argc, argv );
+        shttpd_register_uri( server->ctx, "/transmission/rpc", handle_rpc, server );
+        shttpd_register_uri( server->ctx, "/transmission/upload", handle_upload, server );
+
         evtimer_set( &server->timer, rpcPulse, server );
         evtimer_add( &server->timer, &tv );
+
+        for( i=0; i<argc; ++i )
+            tr_free( argv[i] );
     }
 }
 
@@ -429,7 +436,7 @@ tr_rpcGetPort( const tr_rpc_server * server )
 ****/
 
 /*
- * DELIM_CHARS, FOR_EACH_WORD_IN_LIST, isbyte, and testACL are from, or modified from,
+ * FOR_EACH_WORD_IN_LIST, isbyte, and testACL are from, or modified from,
  * shttpd, written by Sergey Lyubka under this license:
  * "THE BEER-WARE LICENSE" (Revision 42):
  * Sergey Lyubka wrote this file.  As long as you retain this notice you
@@ -437,8 +444,6 @@ tr_rpcGetPort( const tr_rpc_server * server )
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#define  DELIM_CHARS "," /* Separators for lists */
-
 #define FOR_EACH_WORD_IN_LIST(s,len)                                    \
         for (; s != NULL && (len = strcspn(s, DELIM_CHARS)) != 0;       \
                         s += len, s+= strspn(s, DELIM_CHARS))
index f5086a8dc2ffcdd8494a82ac72cd56979696aa11..f6def883b36153f47eee7e9ff34ff76fcf5f00a2 100644 (file)
@@ -4,7 +4,7 @@ noinst_LIBRARIES = libshttpd.a
 AM_CPPFLAGS = -DEMBEDDED -DNDEBUG -DNO_CGI -DNO_SSI
 
 libshttpd_a_SOURCES = \
-    string.c shttpd.c log.c auth.c md5.c cgi.c config.c \
+    string.c shttpd.c log.c auth.c md5.c \
     io_file.c io_socket.c io_ssl.c io_emb.c io_dir.c io_cgi.c \
     compat_unix.c
 
index e2430f40ca97aca6b467aac042adcca13444aab0..a74c8fc102facfbd0c87ca6e36a386c640bf2aa8 100644 (file)
@@ -1,3 +1,3 @@
 http://shttpd.sourceforge.net/
 Sergey Lyubka wrote this software.
-This snapshot si from shttpd-1.39
+This snapshot is from shttpd-1.41
index 212232e561f0d89024867e083b28518082e04395..638ff1e52c3996d9b0fb70713140037162785277 100644 (file)
@@ -281,12 +281,12 @@ check_authorization(struct conn *c, const char *path)
 
                if (!memcmp(c->uri, s, p - s)) {
                        
-                       n = s + len - p + 1;
+                       n = s + len - p;
                        if (n > (int) sizeof(protected_path) - 1)
                                n = sizeof(protected_path) - 1;
-                       
+
                        my_strlcpy(protected_path, p + 1, n);
-                       
+
                        if ((fp = fopen(protected_path, "r")) == NULL)
                                elog(E_LOG, c, "check_auth: cannot open %s: %s",
                                    protected_path, strerror(errno));
index 925b61a9fe48195e8a4b90d415a6922a3e56d213..99b3d203af1308731f8d761c71c31014b6abe1b5 100644 (file)
@@ -18,58 +18,6 @@ struct env_block {
        int     nvars;                  /* Number of variables          */
 };
 
-/*
- * UNIX socketpair() implementation. Why? Because Windows does not have it.
- * Return 0 on success, -1 on error.
- */
-static int
-my_socketpair(struct conn *c, int sp[2])
-{
-       struct sockaddr_in      sa;
-       int                     sock, ret = -1;
-       socklen_t               len = sizeof(sa);
-
-       (void) memset(&sa, 0, sizeof(sa));
-       sa.sin_family           = AF_INET;
-       sa.sin_port             = htons(0);
-       sa.sin_addr.s_addr      = htonl(INADDR_LOOPBACK);
-
-       if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
-               elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
-       } else if (bind(sock, (struct sockaddr *) &sa, len) != 0) {
-               elog(E_LOG, c, "mysocketpair: bind(): %d", ERRNO);
-               (void) closesocket(sock);
-       } else if (listen(sock, 1) != 0) {
-               elog(E_LOG, c, "mysocketpair: listen(): %d", ERRNO);
-               (void) closesocket(sock);
-       } else if (getsockname(sock, (struct sockaddr *) &sa, &len) != 0) {
-               elog(E_LOG, c, "mysocketpair: getsockname(): %d", ERRNO);
-               (void) closesocket(sock);
-       } else if ((sp[0] = socket(AF_INET, SOCK_STREAM, 6)) == -1) {
-               elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
-               (void) closesocket(sock);
-       } else if (connect(sp[0], (struct sockaddr *) &sa, len) != 0) {
-               elog(E_LOG, c, "mysocketpair: connect(): %d", ERRNO);
-               (void) closesocket(sock);
-               (void) closesocket(sp[0]);
-       } else if ((sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) == -1) {
-               elog(E_LOG, c, "mysocketpair: accept(): %d", ERRNO);
-               (void) closesocket(sock);
-               (void) closesocket(sp[0]);
-       } else {
-               /* Success */
-               ret = 0;
-               (void) closesocket(sock);
-       }
-
-#ifndef _WIN32
-       (void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
-       (void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
-#endif /* _WIN32*/
-
-       return (ret);
-}
-
 static void
 addenv(struct env_block *block, const char *fmt, ...)
 {
@@ -145,11 +93,16 @@ prepare_environment(const struct conn *c, const char *prog,
                struct env_block *blk)
 {
        const struct headers    *h = &c->ch;
-       const char              *s, *root = c->ctx->options[OPT_ROOT];
+       const char              *s, *fname, *root = c->ctx->options[OPT_ROOT];
        size_t                  len;
 
        blk->len = blk->nvars = 0;
 
+       /* SCRIPT_FILENAME */
+       fname = prog;
+       if ((s = strrchr(prog, '/')))
+               fname = s + 1;
+
        /* Prepare the environment block */
        addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
        addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
@@ -163,7 +116,7 @@ prepare_environment(const struct conn *c, const char *prog,
        addenv(blk, "REMOTE_PORT=%hu", ntohs(c->sa.u.sin.sin_port));
        addenv(blk, "REQUEST_URI=%s", c->uri);
        addenv(blk, "SCRIPT_NAME=%s", prog + strlen(root));
-       addenv(blk, "SCRIPT_FILENAME=%s", prog);        /* PHP */
+       addenv(blk, "SCRIPT_FILENAME=%s", fname);       /* PHP */
        addenv(blk, "PATH_TRANSLATED=%s", prog);
 
        if (h->ct.v_vec.len > 0)
@@ -244,7 +197,7 @@ run_cgi(struct conn *c, const char *prog)
                        break;
                }
        
-       if (my_socketpair(c, pair) != 0) {
+       if (shttpd_socketpair(pair) != 0) {
                ret = -1;
        } else if (spawn_process(c, prog, blk.buf, blk.vars, pair[1], dir)) {
                ret = -1;
index 67c935faafe1756d97fedcdf47a217793f5e8ea2..53c7f032776210969546cc921c83eabbc79161ea 100644 (file)
 #include <unistd.h>
 #include <dirent.h>
 #include <dlfcn.h>
+
+#if !defined(NO_THREADS)
+#include "pthread.h"
+#define        _beginthread(a, b, c) do { pthread_t tid; \
+       pthread_create(&tid, NULL, (void *(*)(void *))a, c); } while (0)
+#endif /* !NO_THREADS */
+
 #define        SSL_LIB                         "libssl.so"
 #define        DIRSEP                          '/'
 #define        IS_DIRSEP_CHAR(c)               ((c) == '/')
 #define        O_BINARY                        0
 #define        closesocket(a)                  close(a)
 #define        ERRNO                           errno
-#define        NO_GUI
-
-#define        InitializeCriticalSection(x)    /* FIXME UNIX version is not MT safe */
-#define        EnterCriticalSection(x)
-#define        LeaveCriticalSection(x)
diff --git a/third-party/shttpd/config.c b/third-party/shttpd/config.c
deleted file mode 100644 (file)
index 730a492..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file.  As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#include "defs.h"
-
-static int isbyte(int n) { return (n >= 0 && n <= 255); }
-
-static void
-set_acl(struct shttpd_ctx *ctx, const char *s)
-{
-       struct acl      *acl = NULL;
-       char            flag;
-       int             len, a, b, c, d, n, mask;
-       struct llhead   *lp, *tmp;
-
-       /* Delete the old ACLs if any */
-       LL_FOREACH_SAFE(&ctx->acl, lp, tmp)
-               free(LL_ENTRY(lp, struct acl, link));
-
-       FOR_EACH_WORD_IN_LIST(s, len) {
-
-               mask = 32;
-
-               if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
-                       elog(E_FATAL, NULL, "[%s]: subnet must be "
-                           "[+|-]x.x.x.x[/x]", s);
-               } else if (flag != '+' && flag != '-') {
-                       elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
-               } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
-                       elog(E_FATAL, NULL, "bad ip address: [%s]", s);
-               } else  if ((acl = malloc(sizeof(*acl))) == NULL) {
-                       elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
-               } else if (sscanf(s + n, "/%d", &mask) == 0) { 
-                       /* Do nothing, no mask specified */
-               } else if (mask < 0 || mask > 32) {
-                       elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
-               }
-
-               acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
-               acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
-               acl->flag = flag;
-               LL_TAIL(&ctx->acl, &acl->link);
-       }
-}
-
-#ifndef NO_SSL
-/*
- * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
- */
-static void
-set_ssl(struct shttpd_ctx *ctx, const char *pem)
-{
-       SSL_CTX         *CTX;
-       void            *lib;
-       struct ssl_func *fp;
-
-       /* Load SSL library dynamically */
-       if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL)
-               elog(E_FATAL, NULL, "set_ssl: cannot load %s", SSL_LIB);
-
-       for (fp = ssl_sw; fp->name != NULL; fp++)
-               if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL)
-                       elog(E_FATAL, NULL,"set_ssl: cannot find %s", fp->name);
-
-       /* Initialize SSL crap */
-       SSL_library_init();
-
-       if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
-               elog(E_FATAL, NULL, "SSL_CTX_new error");
-       else if (SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
-               elog(E_FATAL, NULL, "cannot open %s", pem);
-       else if (SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
-               elog(E_FATAL, NULL, "cannot open %s", pem);
-       ctx->ssl_ctx = CTX;
-}
-#endif /* NO_SSL */
-
-static void
-open_log_file(FILE **fpp, const char *path)
-{
-       if (*fpp != NULL)
-               (void) fclose(*fpp);
-
-       if (path == NULL) {
-               *fpp = NULL;
-       } else if ((*fpp = fopen(path, "a")) == NULL) {
-               elog(E_FATAL, NULL, "cannot open log file %s: %s",
-                   path, strerror(errno));
-       }
-}
-
-static void
-set_alog(struct shttpd_ctx *ctx, const char *path)
-{
-       open_log_file(&ctx->access_log, path);
-}
-
-static void
-set_elog(struct shttpd_ctx *ctx, const char *path)
-{
-       open_log_file(&ctx->error_log, path);
-}
-
-static void show_cfg_page(struct shttpd_arg *arg);
-
-static void
-set_cfg_uri(struct shttpd_ctx *ctx, const char *uri)
-{
-       free_list(&ctx->registered_uris, &registered_uri_destructor);
-
-       if (uri != NULL) {
-               shttpd_register_uri(ctx, uri, &show_cfg_page, ctx);
-       }
-}
-
-static void
-set_ports(struct shttpd_ctx *ctx, const char *p)
-{
-       int             len, is_ssl;
-
-       free_list(&ctx->listeners, &listener_destructor);
-
-       FOR_EACH_WORD_IN_LIST(p, len) {
-               is_ssl = p[len - 1] == 's' ? 1 : 0;
-               if (shttpd_listen(ctx, atoi(p), is_ssl) == -1)
-                       elog(E_FATAL, NULL,
-                           "Cannot open socket on port %d", atoi(p));
-       }
-}
-
-static const struct opt {
-       int             index;          /* Index in shttpd_ctx          */
-       const char      *name;          /* Option name in config file   */
-       const char      *description;   /* Description                  */
-       const char      *default_value; /* Default option value         */
-       void (*setter)(struct shttpd_ctx *, const char *);
-} known_options[] = {
-       {OPT_ROOT, "root", "\tWeb root directory", ".", NULL},
-       {OPT_INDEX_FILES, "index_files", "Index files", INDEX_FILES, NULL},
-       {OPT_PORTS, "ports", "Listening ports", LISTENING_PORTS, set_ports},
-       {OPT_DIR_LIST, "dir_list", "Directory listing", "1", NULL},
-       {OPT_CFG_URI, "cfg_uri", "Config uri", NULL, set_cfg_uri},
-       {OPT_PROTECT, "protect", "URI to htpasswd mapping", NULL, NULL},
-#ifndef NO_CGI
-       {OPT_CGI_EXTENSIONS, "cgi_ext", "CGI extensions", CGI_EXT, NULL},
-       {OPT_CGI_INTERPRETER, "cgi_interp", "CGI interpreter", NULL, NULL},
-       {OPT_CGI_ENVIRONMENT, "cgi_env", "Additional CGI env vars", NULL, NULL},
-#endif /* NO_CGI */
-       {OPT_SSI_EXTENSIONS, "ssi_ext", "SSI extensions", SSI_EXT, NULL},
-#ifndef NO_AUTH
-       {OPT_AUTH_REALM, "auth_realm", "Authentication domain name",REALM,NULL},
-       {OPT_AUTH_GPASSWD, "auth_gpass", "Global passwords file", NULL, NULL},
-       {OPT_AUTH_PUT, "auth_PUT", "PUT,DELETE auth file", NULL, NULL},
-#endif /* !NO_AUTH */
-       {OPT_ACCESS_LOG, "access_log", "Access log file", NULL, set_alog},
-       {OPT_ERROR_LOG, "error_log", "Error log file", NULL, set_elog},
-       {OPT_MIME_TYPES, "mime_types", "Additional mime types list", NULL,NULL},
-#ifndef NO_SSL
-       {OPT_SSL_CERTIFICATE, "ssl_cert", "SSL certificate file", NULL,set_ssl},
-#endif /* NO_SSL */
-       {OPT_ALIASES, "aliases", "Path=URI mappings", NULL, NULL},
-       {OPT_ACL, "acl", "\tAllow/deny IP addresses/subnets", NULL, set_acl},
-#ifdef _WIN32
-#else
-       {OPT_INETD, "inetd", "Inetd mode", "0", NULL},
-       {OPT_UID, "uid", "\tRun as user", NULL, NULL},
-#endif /* _WIN32 */
-       {-1, NULL, NULL, NULL, NULL}
-};
-
-void shttpd_set_option(struct shttpd_ctx *ctx, const char *opt, const char *val)
-{
-       const struct opt        *o;
-
-       for (o = known_options; o->name != NULL; o++)
-               if (!strcmp(opt, o->name))
-                       break;
-
-       if (o->name == NULL)
-               elog(E_FATAL, NULL, "no such option: [%s]", opt);
-
-       /* Call option setter first, so it can use both new and old values */
-       if (o->setter != NULL)
-               o->setter(ctx, val);
-
-       /* Free old value if any */
-       if (ctx->options[o->index] != NULL)
-               free(ctx->options[o->index]);
-       
-       /* Set new option value */
-       ctx->options[o->index] = val ? my_strdup(val) : NULL;
-}
-
-static void
-show_cfg_page(struct shttpd_arg *arg)
-{
-       struct shttpd_ctx       *ctx = arg->user_data;
-       char                    opt_name[20], value[BUFSIZ];
-       const struct opt        *o;
-
-       opt_name[0] = value[0] = '\0';
-
-       if (!strcmp(shttpd_get_env(arg, "REQUEST_METHOD"), "POST")) {
-               if (arg->flags & SHTTPD_MORE_POST_DATA)
-                       return;
-               (void) shttpd_get_var("o", arg->in.buf, arg->in.len,
-                   opt_name, sizeof(opt_name));
-               (void) shttpd_get_var("v", arg->in.buf, arg->in.len,
-                   value, sizeof(value));
-               shttpd_set_option(ctx, opt_name, value[0] ? value : NULL);
-       }
-
-       shttpd_printf(arg, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
-           "<html><body><h1>SHTTPD v. %s</h1>", shttpd_version());
-
-       shttpd_printf(arg, "%s", "<table border=1"
-           "<tr><th>Option</th><th>Description</th>"
-           "<th colspan=2>Value</th></tr>");
-
-       if (opt_name[0] != '\0' && value[0] != '\0')
-               shttpd_printf(arg, "<p style='color: green'>Saved: %s=%s</p>",
-                   opt_name, value[0] ? value : "NULL");
-
-
-       for (o = known_options; o->name != NULL; o++) {
-               shttpd_printf(arg,
-                   "<form method=post><tr><td>%s</td><td>%s</td>"
-                   "<input type=hidden name=o value='%s'>"
-                   "<td><input type=text name=v value='%s'></td>"
-                   "<td><input type=submit value=save></td></form></tr>",
-                   o->name, o->description, o->name,
-                   ctx->options[o->index] ? ctx->options[o->index] : "");
-       }
-
-       shttpd_printf(arg, "%s", "</table></body></html>");
-       arg->flags |= SHTTPD_END_OF_OUTPUT;
-}
-/*
- * Show usage string and exit.
- */
-void
-usage(const char *prog)
-{
-       const struct opt        *o;
-
-       (void) fprintf(stderr,
-           "SHTTPD version %s (c) Sergey Lyubka\n"
-           "usage: %s [options] [config_file]\n", VERSION, prog);
-
-#if !defined(NO_AUTH)
-       fprintf(stderr, "  -A <htpasswd_file> <realm> <user> <passwd>\n");
-#endif /* NO_AUTH */
-
-       for (o = known_options; o->name != NULL; o++) {
-               (void) fprintf(stderr, "  -%s\t%s", o->name, o->description);
-               if (o->default_value != NULL)
-                       fprintf(stderr, " (default: %s)", o->default_value);
-               fputc('\n', stderr);
-       }
-
-       exit(EXIT_FAILURE);
-}
-
-struct shttpd_ctx *shttpd_init(void)
-{
-       struct shttpd_ctx       *ctx;
-       struct tm               *tm;
-       const struct opt        *o;
-
-       if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
-               elog(E_FATAL, NULL, "cannot allocate shttpd context");
-
-       /* Set default values */
-       for (o = known_options; o->name != NULL; o++) {
-               ctx->options[o->index] = o->default_value == NULL ?
-                   NULL : my_strdup(o->default_value);
-       }
-
-       current_time = ctx->start_time = time(NULL);
-       tm = localtime(&current_time);
-       tz_offset = 0;
-#if 0
-       tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
-#endif
-
-       InitializeCriticalSection(&ctx->mutex);
-
-       LL_INIT(&ctx->connections);
-       LL_INIT(&ctx->registered_uris);
-       LL_INIT(&ctx->error_handlers);
-       LL_INIT(&ctx->acl);
-#if !defined(NO_SSI)
-       LL_INIT(&ctx->ssi_funcs);
-#endif
-       LL_INIT(&ctx->listeners);
-
-#ifdef _WIN32
-       {WSADATA data;  WSAStartup(MAKEWORD(2,2), &data);}
-#endif /* _WIN32 */
-
-       return (ctx);
-}
index eaaee66e0928cff730abd0d62daa091454e0d933..8eb797dd705c7a4ddf08bbc2af757983319b6d2d 100644 (file)
@@ -11,8 +11,8 @@
 #ifndef CONFIG_HEADER_DEFINED
 #define        CONFIG_HEADER_DEFINED
 
-#undef VERSION 
-#define        VERSION         "1.39"          /* Version                      */
+#undef VERSION
+#define        VERSION         "1.41"          /* Version                      */
 #define        CONFIG_FILE     "shttpd.conf"   /* Configuration file           */
 #define        HTPASSWD        ".htpasswd"     /* Passwords file name          */
 #define        URI_MAX         16384           /* Default max request size     */
@@ -25,5 +25,6 @@
 #define        EXPIRE_TIME     3600            /* Expiration time, seconds     */
 #define        ENV_MAX         4096            /* Size of environment block    */
 #define        CGI_ENV_VARS    64              /* Maximum vars passed to CGI   */
+#define        SERVICE_NAME    "SHTTPD " VERSION       /* NT service name      */
 
 #endif /* CONFIG_HEADER_DEFINED */
index 7ac19fb29a4325d2437bce97fd51ec6cd1e310d1..983a5b098c03f53bef74a2790b5365346905d27c 100644 (file)
@@ -14,9 +14,9 @@
 #include "std_includes.h"
 #include "llist.h"
 #include "io.h"
-#include "shttpd.h"
 #include "md5.h"
 #include "config.h"
+#include "shttpd.h"
 
 #define        NELEMS(ar)      (sizeof(ar) / sizeof(ar[0]))
 
 #define        DBG(x)
 #endif /* DEBUG */
 
-#ifdef EMBEDDED
-#include "shttpd.h"
-#endif /* EMBEDDED */
-
 /*
  * Darwin prior to 7.0 and Win32 do not have socklen_t
  */
@@ -45,9 +41,10 @@ struct vec {
        int             len;
 };
 
-#if !defined(_WIN32)
+#if !defined(FALSE)
 enum {FALSE, TRUE};
-#endif /* _WIN32 */
+#endif /* !FALSE */
+
 enum {METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_DELETE, METHOD_HEAD};
 enum {HDR_DATE, HDR_INT, HDR_STRING};  /* HTTP header types            */
 enum {E_FATAL = 1, E_LOG = 2};         /* Flags for elog() function    */
@@ -175,7 +172,6 @@ struct stream {
        union channel           chan;           /* Descriptor           */
        struct io               io;             /* IO buffer            */
        const struct io_class   *io_class;      /* IO class             */
-       int                     nread_last;     /* Bytes last read      */
        int                     headers_len;
        big_int_t               content_len;
        unsigned int            flags;
@@ -186,10 +182,21 @@ struct stream {
 #define        FLAG_CLOSED             16
 #define        FLAG_DONT_CLOSE         32
 #define        FLAG_ALWAYS_READY       64              /* File, dir, user_func */
+#define        FLAG_SUSPEND            128
+};
+
+struct worker {
+       struct llhead   link;
+       int             num_conns;      /* Num of active connections    */
+       int             exit_flag;      /* Ditto - exit flag            */
+       int             ctl[2];         /* Control socket pair          */
+       struct shttpd_ctx *ctx;         /* Context reference            */
+       struct llhead   connections;    /* List of connections          */
 };
 
 struct conn {
        struct llhead   link;           /* Connections chain            */
+       struct worker   *worker;        /* Worker this conn belongs to  */
        struct shttpd_ctx *ctx;         /* Context this conn belongs to */
        struct usa      sa;             /* Remote socket address        */
        time_t          birth_time;     /* Creation time                */
@@ -223,7 +230,7 @@ enum {
        OPT_SSI_EXTENSIONS, OPT_AUTH_REALM, OPT_AUTH_GPASSWD,
        OPT_AUTH_PUT, OPT_ACCESS_LOG, OPT_ERROR_LOG, OPT_MIME_TYPES,
        OPT_SSL_CERTIFICATE, OPT_ALIASES, OPT_ACL, OPT_INETD, OPT_UID,
-       OPT_CFG_URI, OPT_PROTECT,
+       OPT_CFG_URI, OPT_PROTECT, OPT_SERVICE, OPT_HIDE, OPT_THREADS,
        NUM_OPTIONS
 };
 
@@ -231,35 +238,33 @@ enum {
  * SHTTPD context
  */
 struct shttpd_ctx {
-       time_t          start_time;     /* Start time                   */
-       int             nactive;        /* # of connections now         */
-       unsigned long   nrequests;      /* Requests made                */
-       uint64_t        in, out;        /* IN/OUT traffic counters      */
        SSL_CTX         *ssl_ctx;       /* SSL context                  */
-       struct llhead   connections;    /* List of connections          */
 
        struct llhead   registered_uris;/* User urls                    */
        struct llhead   error_handlers; /* Embedded error handlers      */
        struct llhead   acl;            /* Access control list          */
-#if !defined(NO_SSI)
        struct llhead   ssi_funcs;      /* SSI callback functions       */
-#endif
        struct llhead   listeners;      /* Listening sockets            */
+       struct llhead   workers;        /* Worker workers               */
 
-       FILE    *access_log;            /* Access log stream            */
-       FILE    *error_log;             /* Error log stream             */
+       FILE            *access_log;    /* Access log stream            */
+       FILE            *error_log;     /* Error log stream             */
 
        char    *options[NUM_OPTIONS];  /* Configurable options         */
-
-#if defined(_WIN32)
-       CRITICAL_SECTION mutex;         /* For MT case                  */
-       HANDLE          ev[2];          /* For thread synchronization */
-#elif defined(__rtems__)
+#if defined(__rtems__)
        rtems_id         mutex;
 #endif /* _WIN32 */
 };
 
-#define IS_TRUE(ctx, opt) ((ctx)->options[opt] && (ctx)->options[opt][0] =='1')
+struct listener {
+       struct llhead           link;
+       struct shttpd_ctx       *ctx;   /* Context that socket belongs  */
+       int                     sock;   /* Listening socket             */
+       int                     is_ssl; /* Should be SSL-ed             */
+};
+
+/* Types of messages that could be sent over the control socket */
+enum {CTL_PASS_SOCKET, CTL_WAKEUP};
 
 /*
  * In SHTTPD, list of values are represented as comma or space separated
@@ -300,11 +305,11 @@ extern int        url_decode(const char *, int, char *dst, int);
 extern void    send_server_error(struct conn *, int code, const char *reason);
 extern int     get_headers_len(const char *buf, size_t buflen);
 extern void    parse_headers(const char *s, int len, struct headers *parsed);
-extern void    open_listening_ports(struct shttpd_ctx *ctx);
+extern int     is_true(const char *str);
+extern int     shttpd_socketpair(int pair[2]);
 extern void get_mime_type(struct shttpd_ctx *, const char *, int, struct vec *);
-extern void    free_list(struct llhead *head, void (*)(struct llhead *));
-extern void    registered_uri_destructor(struct llhead *);
-extern void    listener_destructor(struct llhead *);
+
+#define        IS_TRUE(ctx, opt)       is_true((ctx)->options[opt])
 
 /*
  * config.c
@@ -342,6 +347,10 @@ extern char *      my_getcwd(char *, int);
 extern int     spawn_process(struct conn *c, const char *prog,
                char *envblk, char *envp[], int sock, const char *dir);
 
+extern void    set_nt_service(struct shttpd_ctx *, const char *);
+extern void    set_systray(struct shttpd_ctx *, const char *);
+extern void    try_to_run_as_nt_service(void);
+
 /*
  * io_*.c
  */
@@ -360,10 +369,8 @@ extern void        ssl_handshake(struct stream *stream);
 extern void    setup_embedded_stream(struct conn *, union variant, void *);
 extern struct registered_uri *is_registered_uri(struct shttpd_ctx *,
                const char *uri);
-#if !defined(NO_SSI)
 extern void    do_ssi(struct conn *);
 extern void    ssi_func_destructor(struct llhead *lp);
-#endif
 
 /*
  * auth.c
index 762afe5a311ba16f8735951ef0c5b1b944c01269..00a275d4bfd498d2b89170d70d5cb4569f9cd438 100644 (file)
@@ -52,6 +52,9 @@ call_user(struct conn *c, struct shttpd_arg *arg, shttpd_callback_t func)
                c->loc.flags &= ~FLAG_DONT_CLOSE;
        else
                c->loc.flags |= FLAG_DONT_CLOSE;
+
+       if (arg->flags & SHTTPD_SUSPEND)
+               c->loc.flags |= FLAG_SUSPEND;
 }
 
 static int
@@ -90,14 +93,10 @@ close_embedded(struct stream *stream)
 size_t
 shttpd_printf(struct shttpd_arg *arg, const char *fmt, ...)
 {
-       struct conn     *c = arg->priv;
-       struct io       *io = &c->loc.io;
        char            *buf = arg->out.buf + arg->out.num_bytes;
        int             buflen = arg->out.len - arg->out.num_bytes, len = 0;
        va_list         ap;
 
-       assert(buf <= io->buf + io->size);
-
        if (buflen > 0) {
                va_start(ap, fmt);
                len = vsnprintf(buf, buflen, fmt, ap);
@@ -267,6 +266,22 @@ shttpd_handle_error(struct shttpd_ctx *ctx, int code,
        }
 }
 
+void
+shttpd_wakeup(const void *priv)
+{
+       const struct conn       *conn = priv;
+       char                    buf[sizeof(int) + sizeof(void *)];
+       int                     cmd = CTL_WAKEUP;
+
+#if 0
+       conn->flags &= ~SHTTPD_SUSPEND;
+#endif
+       (void) memcpy(buf, &cmd, sizeof(cmd));
+       (void) memcpy(buf + sizeof(cmd), conn, sizeof(conn));
+
+       (void) send(conn->worker->ctl[1], buf, sizeof(buf), 0);
+}
+
 const struct io_class  io_embedded =  {
        "embedded",
        do_embedded,
index 1dce074fb49bc04f62d579f56c294c959dbee9d0..0d382606c2257861c11201390a47d36ee2493cb1 100644 (file)
@@ -20,7 +20,7 @@ write_file(struct stream *stream, const void *buf, size_t len)
        assert(fd != -1);
        n = write(fd, buf, len);
 
-       DBG(("put_file(%p, %d): %d bytes", (void *) stream, len, n));
+       DBG(("put_file(%p, %d): %d bytes", (void *) stream, (int) len, n));
 
        if (n <= 0 || (rem->io.total >= (big_int_t) rem->headers_len)) {
                (void) fstat(fd, &st);
index 753257fd8d8e5af4c3a80481ab9cedc2c8396ba2..e3167e22342a8a76d5c1707af7cde904a5cee63b 100644 (file)
@@ -33,21 +33,21 @@ ssl_handshake(struct stream *stream)
 {
        int     n;
 
-       if ((n = SSL_accept(stream->chan.ssl.ssl)) == 0) {
+       if ((n = SSL_accept(stream->chan.ssl.ssl)) == 1) {
+               DBG(("handshake: SSL accepted"));
+               stream->flags |= FLAG_SSL_ACCEPTED;
+       } else {
                n = SSL_get_error(stream->chan.ssl.ssl, n);
                if (n != SSL_ERROR_WANT_READ && n != SSL_ERROR_WANT_WRITE)
                        stream->flags |= FLAG_CLOSED;
-               elog(E_LOG, stream->conn, "SSL_accept error %d", n);
-       } else {
-               DBG(("handshake: SSL accepted"));
-               stream->flags |= FLAG_SSL_ACCEPTED;
+               DBG(("SSL_accept error %d", n));
        }
 }
 
 static int
 read_ssl(struct stream *stream, void *buf, size_t len)
 {
-       int     nread = 0;
+       int     nread = -1;
 
        assert(stream->chan.ssl.ssl != NULL);
 
index 1b02f80c765d06953cd3399a6d6d6c3a88c9b875..cc55fa86f56825c39414604f12d2e0948d9a078a 100644 (file)
 
 /*
  * Small and portable HTTP server, http://shttpd.sourceforge.net
- * $Id: shttpd.c,v 1.28 2008/02/17 21:45:09 drozd Exp $
+ * $Id: shttpd.c,v 1.44 2008/05/31 18:20:02 drozd Exp $
  */
 
 #include "defs.h"
 
-time_t         current_time;   /* Current UTC time             */
-int            tz_offset;      /* Time zone offset from UTC    */
+time_t current_time;   /* Current UTC time             */
+int    tz_offset;      /* Time zone offset from UTC    */
+int    exit_flag;      /* Program exit flag            */
 
 const struct vec known_http_methods[] = {
        {"GET",         3},
@@ -27,13 +28,6 @@ const struct vec known_http_methods[] = {
        {NULL,          0}
 };
 
-struct listener {
-       struct llhead   link;
-       struct shttpd_ctx *ctx;         /* Context that socket belongs  */
-       int             sock;           /* Listening socket             */
-       int             is_ssl;         /* Should be SSL-ed             */
-};
-
 /*
  * This structure tells how HTTP headers must be parsed.
  * Used by parse_headers() function.
@@ -58,6 +52,55 @@ static const struct http_header http_headers[] = {
 struct shttpd_ctx *init_ctx(const char *config_file, int argc, char *argv[]);
 static void process_connection(struct conn *, int, int);
 
+int
+is_true(const char *str)
+{
+       static const char       *trues[] = {"1", "yes", "true", "jawohl", NULL};
+       const char              **p;
+
+       for (p = trues; *p != NULL; p++)
+               if (str && !strcmp(str, *p))
+                       return (TRUE);
+
+       return (FALSE);
+}
+
+static void
+free_list(struct llhead *head, void (*dtor)(struct llhead *))
+{
+       struct llhead   *lp, *tmp;
+
+       LL_FOREACH_SAFE(head, lp, tmp) {
+               LL_DEL(lp);
+               dtor(lp);
+       }
+}
+
+static void
+listener_destructor(struct llhead *lp)
+{
+       struct listener *listener = LL_ENTRY(lp, struct listener, link);
+
+       (void) closesocket(listener->sock);
+       free(listener);
+}
+
+static void
+registered_uri_destructor(struct llhead *lp)
+{
+       struct registered_uri *ruri = LL_ENTRY(lp, struct registered_uri, link);
+
+       free((void *) ruri->uri);
+       free(ruri);
+}
+
+static void
+acl_destructor(struct llhead *lp)
+{
+       struct acl      *acl = LL_ENTRY(lp, struct acl, link);
+       free(acl);
+}
+
 int
 url_decode(const char *src, int src_len, char *dst, int dst_len)
 {
@@ -126,14 +169,14 @@ stop_stream(struct stream *stream)
        DBG(("%d %s stopped. %lu of content data, %d now in a buffer",
            stream->conn->rem.chan.sock, 
            stream->io_class ? stream->io_class->name : "(null)",
-           (unsigned long) stream->io.total, io_data_len(&stream->io)));
+           (unsigned long) stream->io.total, (int) io_data_len(&stream->io)));
 }
 
 /*
  * Setup listening socket on given port, return socket
  */
 static int
-open_listening_port(int port)
+shttpd_open_listening_port(int port)
 {
        int             sock, on = 1;
        struct usa      sa;
@@ -484,7 +527,6 @@ get_path_info(struct conn *c, char *path, struct stat *stp)
        return (-1);
 }
 
-
 static void
 decide_what_to_do(struct conn *c)
 {
@@ -638,8 +680,6 @@ parse_http_request(struct conn *c)
                send_server_error(c, 400, "Request is too big");
        }
 
-       io_inc_tail(&c->rem.io, req_len);
-
        if (req_len == 0) {
                return;
        } else if (req_len < 16) {      /* Minimal: "GET / HTTP/1.0\n\n" */
@@ -653,6 +693,8 @@ parse_http_request(struct conn *c)
        if (c->loc.flags & FLAG_CLOSED)
                return;
 
+       io_inc_tail(&c->rem.io, req_len);
+
        DBG(("Conn %d: parsing request: [%.*s]", c->rem.chan.sock, req_len, s));
        c->rem.flags |= FLAG_HEADERS_PARSED;
 
@@ -705,14 +747,17 @@ parse_http_request(struct conn *c)
        }
 }
 
-void
-shttpd_add_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
+static void
+add_socket(struct worker *worker, int sock, int is_ssl)
 {
-       struct conn     *c;
-       struct usa      sa;
-       int             l = IS_TRUE(ctx, OPT_INETD) ? E_FATAL : E_LOG;
+       struct shttpd_ctx       *ctx = worker->ctx;
+       struct conn             *c;
+       struct usa              sa;
+       int                     l = IS_TRUE(ctx, OPT_INETD) ? E_FATAL : E_LOG;
 #if !defined(NO_SSL)
        SSL             *ssl = NULL;
+#else
+       is_ssl = is_ssl;        /* supress warnings */
 #endif /* NO_SSL */
 
        sa.len = sizeof(sa.u.sin);
@@ -737,9 +782,9 @@ shttpd_add_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
                (void) closesocket(sock);
                elog(l, NULL, "add_socket: calloc: %s", strerror(ERRNO));
        } else {
-               ctx->nrequests++;
-               c->rem.conn = c->loc.conn = c;
+               c->rem.conn     = c->loc.conn = c;
                c->ctx          = ctx;
+               c->worker       = worker;
                c->sa           = sa;
                c->birth_time   = current_time;
                c->expire_time  = current_time + EXPIRE_TIME;
@@ -768,10 +813,8 @@ shttpd_add_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
                }
 #endif /* NO_SSL */
 
-               EnterCriticalSection(&ctx->mutex);
-               LL_TAIL(&ctx->connections, &c->link);
-               ctx->nactive++;
-               LeaveCriticalSection(&ctx->mutex);
+               LL_TAIL(&worker->connections, &c->link);
+               worker->num_conns++;
                
                DBG(("%s:%hu connected (socket %d)",
                    inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
@@ -779,59 +822,66 @@ shttpd_add_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
        }
 }
 
-int
-shttpd_active(struct shttpd_ctx *ctx)
+static struct worker *
+first_worker(struct shttpd_ctx *ctx)
 {
-       return (ctx->nactive);
+       return (LL_ENTRY(ctx->workers.next, struct worker, link));
 }
 
-/*
- * Setup a listening socket on given port. Return opened socket or -1
- */
-int
-shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl)
+static void
+pass_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
 {
-       struct listener *l;
-       int             sock;
+       struct llhead   *lp;
+       struct worker   *worker, *lazy;
+       int             buf[3];
 
-       if ((sock = open_listening_port(port)) == -1) {
-               elog(E_FATAL, NULL, "cannot open port %d", port);
-       } else if ((l = calloc(1, sizeof(*l))) == NULL) {
-               (void) closesocket(sock);
-               elog(E_FATAL, NULL, "cannot allocate listener");
-       } else if (is_ssl && ctx->ssl_ctx == NULL) {
-               (void) closesocket(sock);
-               elog(E_FATAL, NULL, "cannot add SSL socket, "
-                   "please specify certificate file");
-       } else {
-               l->is_ssl = is_ssl;
-               l->sock = sock;
-               l->ctx  = ctx;
-               LL_TAIL(&ctx->listeners, &l->link);
-               DBG(("shttpd_listen: added socket %d", sock));
+       lazy = first_worker(ctx);
+
+       /* Find least busy worker */
+       LL_FOREACH(&ctx->workers, lp) {
+               worker = LL_ENTRY(lp, struct worker, link);
+               if (worker->num_conns < lazy->num_conns)
+                       lazy = worker;
        }
 
-       return (sock);
+       buf[0] = CTL_PASS_SOCKET;
+       buf[1] = sock;
+       buf[2] = is_ssl;
+
+       (void) send(lazy->ctl[1], (void *) buf, sizeof(buf), 0);
 }
 
-int
-shttpd_accept(int lsn_sock, int milliseconds)
+static void
+set_ports(struct shttpd_ctx *ctx, const char *p)
 {
-       struct timeval  tv;
-       struct usa      sa;
-       fd_set          read_set;
-       int             sock = -1;
-       
-       tv.tv_sec       = milliseconds / 1000;
-       tv.tv_usec      = milliseconds % 1000;
-       sa.len          = sizeof(sa.u.sin);
-       FD_ZERO(&read_set);
-       FD_SET(lsn_sock, &read_set);
-       
-       if (select(lsn_sock + 1, &read_set, NULL, NULL, &tv) == 1)
-               sock = accept(lsn_sock, &sa.u.sa, &sa.len);
+       int             sock, len, is_ssl, port;
+       struct listener *l;
 
-       return (sock);
+
+       free_list(&ctx->listeners, &listener_destructor);
+
+       FOR_EACH_WORD_IN_LIST(p, len) {
+
+               is_ssl  = p[len - 1] == 's' ? 1 : 0;
+               port    = atoi(p);
+
+               if ((sock = shttpd_open_listening_port(port)) == -1) {
+                       elog(E_FATAL, NULL, "cannot open port %d", port);
+               } else if ((l = calloc(1, sizeof(*l))) == NULL) {
+                       (void) closesocket(sock);
+                       elog(E_FATAL, NULL, "cannot allocate listener");
+               } else if (is_ssl && ctx->ssl_ctx == NULL) {
+                       (void) closesocket(sock);
+                       elog(E_FATAL, NULL, "cannot add SSL socket, "
+                           "please specify certificate file");
+               } else {
+                       l->is_ssl = is_ssl;
+                       l->sock = sock;
+                       l->ctx  = ctx;
+                       LL_TAIL(&ctx->listeners, &l->link);
+                       DBG(("shttpd_listen: added socket %d", sock));
+               }
+       }
 }
 
 static void
@@ -848,8 +898,7 @@ read_stream(struct stream *stream)
                len = stream->content_len - stream->io.total;
 
        /* Read from underlying channel */
-       n = stream->nread_last = stream->io_class->read(stream,
-           io_space(&stream->io), len);
+       n = stream->io_class->read(stream, io_space(&stream->io), len);
 
        if (n > 0)
                io_inc_head(&stream->io, n);
@@ -903,7 +952,7 @@ write_stream(struct stream *from, struct stream *to)
 
 
 static void
-disconnect(struct llhead *lp)
+connection_desctructor(struct llhead *lp)
 {
        struct conn             *c = LL_ENTRY(lp, struct conn, link);
        static const struct vec vec = {"close", 5};
@@ -941,7 +990,7 @@ disconnect(struct llhead *lp)
                c->loc.io_class = NULL;
                c->loc.flags = 0;
                c->loc.content_len = 0;
-               c->rem.flags = FLAG_W | FLAG_R;
+               c->rem.flags = FLAG_W | FLAG_R | FLAG_SSL_ACCEPTED;
                c->query = c->request = c->uri = c->path_info = NULL;
                c->mime_type.len = 0;
                (void) memset(&c->ch, 0, sizeof(c->ch));
@@ -953,16 +1002,23 @@ disconnect(struct llhead *lp)
                if (c->rem.io_class != NULL)
                        c->rem.io_class->close(&c->rem);
 
-               EnterCriticalSection(&c->ctx->mutex);
                LL_DEL(&c->link);
-               c->ctx->nactive--;
-               assert(c->ctx->nactive >= 0);
-               LeaveCriticalSection(&c->ctx->mutex);
+               c->worker->num_conns--;
+               assert(c->worker->num_conns >= 0);
 
                free(c);
        }
 }
 
+static void
+worker_destructor(struct llhead *lp)
+{
+       struct worker   *worker = LL_ENTRY(lp, struct worker, link);
+
+       free_list(&worker->connections, connection_desctructor);
+       free(worker);
+}
+
 static int
 is_allowed(const struct shttpd_ctx *ctx, const struct usa *usa)
 {
@@ -1000,10 +1056,10 @@ process_connection(struct conn *c, int remote_ready, int local_ready)
        if (!(c->rem.flags & FLAG_HEADERS_PARSED))
                parse_http_request(c);
 
-       DBG(("loc: %u [%.*s]", io_data_len(&c->loc.io),
-           io_data_len(&c->loc.io), io_data(&c->loc.io)));
-       DBG(("rem: %u [%.*s]", io_data_len(&c->rem.io),
-           io_data_len(&c->rem.io), io_data(&c->rem.io)));
+       DBG(("loc: %d [%.*s]", (int) io_data_len(&c->loc.io),
+           (int) io_data_len(&c->loc.io), io_data(&c->loc.io)));
+       DBG(("rem: %d [%.*s]", (int) io_data_len(&c->rem.io),
+           (int) io_data_len(&c->rem.io), io_data(&c->rem.io)));
 
        /* Read from the local end if it is ready */
        if (local_ready && io_space_len(&c->loc.io))
@@ -1016,52 +1072,85 @@ process_connection(struct conn *c, int remote_ready, int local_ready)
        if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
                write_stream(&c->loc, &c->rem); 
 
-       if (c->rem.nread_last > 0)
-               c->ctx->in += c->rem.nread_last;
-       if (c->loc.nread_last > 0)
-               c->ctx->out += c->loc.nread_last;
-
        /* Check whether we should close this connection */
        if ((current_time > c->expire_time) ||
            (c->rem.flags & FLAG_CLOSED) ||
            ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
-               disconnect(&c->link);
+               connection_desctructor(&c->link);
 }
 
-/*
- * One iteration of server loop. This is the core of the data exchange.
- */
-void
-shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
+static int
+num_workers(const struct shttpd_ctx *ctx)
 {
-       struct llhead   *lp, *tmp;
-       struct listener *l;
-       struct conn     *c;
-       struct timeval  tv;                     /* Timeout for select() */
-       fd_set          read_set, write_set;
-       int             sock, max_fd = -1, msec = milliseconds;
-       struct usa      sa;
+       char    *p = ctx->options[OPT_THREADS];
+       return (p ? atoi(p) : 1);
+}
 
-       current_time = time(0);
-       FD_ZERO(&read_set);
-       FD_ZERO(&write_set);
+static void
+handle_connected_socket(struct shttpd_ctx *ctx,
+               struct usa *sap, int sock, int is_ssl)
+{
+#if !defined(_WIN32)
+       if (sock >= (int) FD_SETSIZE) {
+               elog(E_LOG, NULL, "ctx %p: discarding "
+                   "socket %d, too busy", ctx, sock);
+               (void) closesocket(sock);
+       } else
+#endif /* !_WIN32 */
+               if (!is_allowed(ctx, sap)) {
+               elog(E_LOG, NULL, "%s is not allowed to connect",
+                   inet_ntoa(sap->u.sin.sin_addr));
+               (void) closesocket(sock);
+       } else if (num_workers(ctx) > 1) {
+               pass_socket(ctx, sock, is_ssl);
+       } else {
+               add_socket(first_worker(ctx), sock, is_ssl);
+       }
+}
 
-       /* Add listening sockets to the read set */
-       LL_FOREACH(&ctx->listeners, lp) {
-               l = LL_ENTRY(lp, struct listener, link);
-               FD_SET(l->sock, &read_set);
-               if (l->sock > max_fd)
-                       max_fd = l->sock;
-               DBG(("FD_SET(%d) (listening)", l->sock));
+static int
+do_select(int max_fd, fd_set *read_set, fd_set *write_set, int milliseconds)
+{
+       struct timeval  tv;
+       int             n;
+
+       tv.tv_sec = milliseconds / 1000;
+       tv.tv_usec = (milliseconds % 1000) * 1000;
+
+       /* Check IO readiness */
+       if ((n = select(max_fd + 1, read_set, write_set, NULL, &tv)) < 0) {
+#ifdef _WIN32
+               /*
+                * On windows, if read_set and write_set are empty,
+                * select() returns "Invalid parameter" error
+                * (at least on my Windows XP Pro). So in this case,
+                * we sleep here.
+                */
+               Sleep(milliseconds);
+#endif /* _WIN32 */
+               DBG(("select: %d", ERRNO));
        }
 
+       return (n);
+}
+
+static void
+multiplex_worker_sockets(const struct worker *worker, int *max_fd,
+               fd_set *read_set, fd_set *write_set, int *milliseconds)
+{
+       struct llhead   *lp;
+       struct conn     *c;
+
+       /* Add control socket */
+       add_to_set(worker->ctl[0], read_set, max_fd);
+
        /* Multiplex streams */
-       LL_FOREACH(&ctx->connections, lp) {
+       LL_FOREACH(&worker->connections, lp) {
                c = LL_ENTRY(lp, struct conn, link);
                
                /* If there is a space in remote IO, check remote socket */
                if (io_space_len(&c->rem.io))
-                       add_to_set(c->rem.chan.fd, &read_set, &max_fd);
+                       add_to_set(c->rem.chan.fd, read_set, max_fd);
 
 #if !defined(NO_CGI)
                /*
@@ -1070,7 +1159,7 @@ shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
                 */
                if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
                    c->loc.io_class == &io_cgi)
-                       add_to_set(c->loc.chan.fd, &read_set, &max_fd);
+                       add_to_set(c->loc.chan.fd, read_set, max_fd);
 
                /*
                 * If there is some data read from remote socket, and
@@ -1078,119 +1167,111 @@ shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
                 */
                if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
                    c->loc.io_class == &io_cgi)
-                       add_to_set(c->loc.chan.fd, &write_set, &max_fd);
+                       add_to_set(c->loc.chan.fd, write_set, max_fd);
 #endif /* NO_CGI */
 
                /*
                 * If there is some data read from local endpoint, check the
                 * remote socket for write availability
                 */
-               if (io_data_len(&c->loc.io))
-                       add_to_set(c->rem.chan.fd, &write_set, &max_fd);
+               if (io_data_len(&c->loc.io) && !(c->loc.flags & FLAG_SUSPEND))
+                       add_to_set(c->rem.chan.fd, write_set, max_fd);
 
+               /*
+                * Set select wait interval to zero if FLAG_ALWAYS_READY set
+                */
                if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
                    (c->loc.flags & FLAG_ALWAYS_READY))
-                       msec = 0;
+                       *milliseconds = 0;
                
                if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
                    (c->loc.flags & FLAG_ALWAYS_READY))
-                       msec = 0;
+                       *milliseconds = 0;
        }
+}
 
-       tv.tv_sec = msec / 1000;
-       tv.tv_usec = (msec % 1000) * 1000;
-
-       /* Check IO readiness */
-       if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) < 0) {
-#ifdef _WIN32
-               /*
-                * On windows, if read_set and write_set are empty,
-                * select() returns "Invalid parameter" error
-                * (at least on my Windows XP Pro). So in this case,
-                * we sleep here.
-                */
-               Sleep(milliseconds);
-#endif /* _WIN32 */
-               DBG(("select: %d", ERRNO));
-               return;
-       }
+static void
+process_worker_sockets(struct worker *worker, fd_set *read_set)
+{
+       struct llhead   *lp, *tmp;
+       int             cmd, skt[2], sock = worker->ctl[0];
+       struct conn     *c;
 
-       /* Check for incoming connections on listener sockets */
-       LL_FOREACH(&ctx->listeners, lp) {
-               l = LL_ENTRY(lp, struct listener, link);
-               if (!FD_ISSET(l->sock, &read_set))
-                       continue;
-               do {
-                       sa.len = sizeof(sa.u.sin);
-                       if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1) {
-#if defined(_WIN32)
-                               shttpd_add_socket(ctx, sock, l->is_ssl);
-#else
-                               if (sock >= (int) FD_SETSIZE) {
-                                       elog(E_LOG, NULL,
-                                          "shttpd_poll: ctx %p: disarding "
-                                          "socket %d, too busy", ctx, sock);
-                                       (void) closesocket(sock);
-                               } else if (!is_allowed(ctx, &sa)) {
-                                       elog(E_LOG, NULL, "shttpd_poll: %s "
-                                           "is not allowed to connect",
-                                          inet_ntoa(sa.u.sin.sin_addr));
-                                       (void) closesocket(sock);
-                               } else {
-                                       shttpd_add_socket(ctx, sock, l->is_ssl);
-                               }
-#endif /* _WIN32 */
+       /* Check if new socket is passed to us over the control socket */
+       if (FD_ISSET(worker->ctl[0], read_set))
+               while (recv(sock, (void *) &cmd, sizeof(cmd), 0) == sizeof(cmd))
+                       switch (cmd) {
+                       case CTL_PASS_SOCKET:
+                               (void)recv(sock, (void *) &skt, sizeof(skt), 0);
+                               add_socket(worker, skt[0], skt[1]);
+                               break;
+                       case CTL_WAKEUP:
+                               (void)recv(sock, (void *) &c, sizeof(c), 0);
+                               c->loc.flags &= FLAG_SUSPEND;
+                               break;
+                       default:
+                               elog(E_FATAL, NULL, "ctx %p: ctl cmd %d",
+                                   worker->ctx, cmd);
+                               break;
                        }
-               } while (sock != -1);
-       }
 
        /* Process all connections */
-       LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
+       LL_FOREACH_SAFE(&worker->connections, lp, tmp) {
                c = LL_ENTRY(lp, struct conn, link);
-               process_connection(c, FD_ISSET(c->rem.chan.fd, &read_set),
+               process_connection(c, FD_ISSET(c->rem.chan.fd, read_set),
                    ((c->loc.flags & FLAG_ALWAYS_READY)
 #if !defined(NO_CGI)
                    || (c->loc.io_class == &io_cgi &&
-                    FD_ISSET(c->loc.chan.fd, &read_set))
+                    FD_ISSET(c->loc.chan.fd, read_set))
 #endif /* NO_CGI */
                    ));
        }
 }
 
+/*
+ * One iteration of server loop. This is the core of the data exchange.
+ */
 void
-free_list(struct llhead *head, void (*dtor)(struct llhead *))
+shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
 {
-       struct llhead   *lp, *tmp;
+       struct llhead   *lp;
+       struct listener *l;
+       fd_set          read_set, write_set;
+       int             sock, max_fd = -1;
+       struct usa      sa;
 
-       LL_FOREACH_SAFE(head, lp, tmp) {
-               LL_DEL(lp);
-               dtor(lp);
-       }
-}
+       current_time = time(0);
+       FD_ZERO(&read_set);
+       FD_ZERO(&write_set);
 
-void
-listener_destructor(struct llhead *lp)
-{
-       struct listener *listener = LL_ENTRY(lp, struct listener, link);
+       /* Add listening sockets to the read set */
+       LL_FOREACH(&ctx->listeners, lp) {
+               l = LL_ENTRY(lp, struct listener, link);
+               add_to_set(l->sock, &read_set, &max_fd);
+               DBG(("FD_SET(%d) (listening)", l->sock));
+       }
 
-       (void) closesocket(listener->sock);
-       free(listener);
-}
+       if (num_workers(ctx) == 1)
+               multiplex_worker_sockets(first_worker(ctx), &max_fd,
+                               &read_set, &write_set, &milliseconds);
 
-void
-registered_uri_destructor(struct llhead *lp)
-{
-       struct registered_uri *ruri = LL_ENTRY(lp, struct registered_uri, link);
+       if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
+               return;;
 
-       free((void *) ruri->uri);
-       free(ruri);
-}
+       /* Check for incoming connections on listener sockets */
+       LL_FOREACH(&ctx->listeners, lp) {
+               l = LL_ENTRY(lp, struct listener, link);
+               if (!FD_ISSET(l->sock, &read_set))
+                       continue;
+               do {
+                       sa.len = sizeof(sa.u.sin);
+                       if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1)
+                               handle_connected_socket(ctx,&sa,sock,l->is_ssl);
+               } while (sock != -1);
+       }
 
-static void
-acl_destructor(struct llhead *lp)
-{
-       struct acl      *acl = LL_ENTRY(lp, struct acl, link);
-       free(acl);
+       if (num_workers(ctx) == 1)
+               process_worker_sockets(first_worker(ctx), &read_set);
 }
 
 /*
@@ -1201,13 +1282,13 @@ shttpd_fini(struct shttpd_ctx *ctx)
 {
        size_t  i;
 
-       free_list(&ctx->connections, disconnect);
+       free_list(&ctx->workers, worker_destructor);
        free_list(&ctx->registered_uris, registered_uri_destructor);
        free_list(&ctx->acl, acl_destructor);
        free_list(&ctx->listeners, listener_destructor);
 #if !defined(NO_SSI)
        free_list(&ctx->ssi_funcs, ssi_func_destructor);
-#endif
+#endif /* !NO_SSI */
 
        for (i = 0; i < NELEMS(ctx->options); i++)
                if (ctx->options[i] != NULL)
@@ -1220,3 +1301,540 @@ shttpd_fini(struct shttpd_ctx *ctx)
 
        free(ctx);
 }
+
+/*
+ * UNIX socketpair() implementation. Why? Because Windows does not have it.
+ * Return 0 on success, -1 on error.
+ */
+int
+shttpd_socketpair(int sp[2])
+{
+       struct sockaddr_in      sa;
+       int                     sock, ret = -1;
+       socklen_t               len = sizeof(sa);
+
+       sp[0] = sp[1] = -1;
+
+       (void) memset(&sa, 0, sizeof(sa));
+       sa.sin_family           = AF_INET;
+       sa.sin_port             = htons(0);
+       sa.sin_addr.s_addr      = htonl(INADDR_LOOPBACK);
+
+       if ((sock = socket(AF_INET, SOCK_STREAM, 0)) != -1 &&
+           !bind(sock, (struct sockaddr *) &sa, len) &&
+           !listen(sock, 1) &&
+           !getsockname(sock, (struct sockaddr *) &sa, &len) &&
+           (sp[0] = socket(AF_INET, SOCK_STREAM, 6)) != -1 &&
+           !connect(sp[0], (struct sockaddr *) &sa, len) &&
+           (sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) != -1) {
+
+               /* Success */
+               ret = 0;
+       } else {
+
+               /* Failure, close descriptors */
+               if (sp[0] != -1)
+                       (void) closesocket(sp[0]);
+               if (sp[1] != -1)
+                       (void) closesocket(sp[1]);
+       }
+
+       (void) closesocket(sock);
+       (void) set_non_blocking_mode(sp[0]);
+       (void) set_non_blocking_mode(sp[1]);
+
+#ifndef _WIN32
+       (void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
+       (void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
+#endif /* _WIN32*/
+
+       return (ret);
+}
+
+static int isbyte(int n) { return (n >= 0 && n <= 255); }
+
+static void
+set_inetd(struct shttpd_ctx *ctx, const char *flag)
+{
+       ctx = NULL; /* Unused */
+
+       if (is_true(flag)) {
+               shttpd_set_option(ctx, "ports", NULL);
+               (void) freopen("/dev/null", "a", stderr);
+               add_socket(first_worker(ctx), 0, 0);
+       }
+}
+
+static void
+set_uid(struct shttpd_ctx *ctx, const char *uid)
+{
+       struct passwd   *pw;
+
+       ctx = NULL; /* Unused */
+
+#if !defined(_WIN32)
+       if ((pw = getpwnam(uid)) == NULL)
+               elog(E_FATAL, 0, "%s: unknown user [%s]", __func__, uid);
+       else if (setgid(pw->pw_gid) == -1)
+               elog(E_FATAL, NULL, "%s: setgid(%s): %s",
+                   __func__, uid, strerror(errno));
+       else if (setuid(pw->pw_uid) == -1)
+               elog(E_FATAL, NULL, "%s: setuid(%s): %s",
+                   __func__, uid, strerror(errno));
+#endif /* !_WIN32 */
+}
+
+static void
+set_acl(struct shttpd_ctx *ctx, const char *s)
+{
+       struct acl      *acl = NULL;
+       char            flag;
+       int             len, a, b, c, d, n, mask;
+       struct llhead   *lp, *tmp;
+
+       /* Delete the old ACLs if any */
+       LL_FOREACH_SAFE(&ctx->acl, lp, tmp)
+               free(LL_ENTRY(lp, struct acl, link));
+
+       FOR_EACH_WORD_IN_LIST(s, len) {
+
+               mask = 32;
+
+               if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
+                       elog(E_FATAL, NULL, "[%s]: subnet must be "
+                           "[+|-]x.x.x.x[/x]", s);
+               } else if (flag != '+' && flag != '-') {
+                       elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
+               } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
+                       elog(E_FATAL, NULL, "bad ip address: [%s]", s);
+               } else  if ((acl = malloc(sizeof(*acl))) == NULL) {
+                       elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
+               } else if (sscanf(s + n, "/%d", &mask) == 0) { 
+                       /* Do nothing, no mask specified */
+               } else if (mask < 0 || mask > 32) {
+                       elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
+               }
+
+               acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
+               acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
+               acl->flag = flag;
+               LL_TAIL(&ctx->acl, &acl->link);
+       }
+}
+
+#ifndef NO_SSL
+/*
+ * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
+ */
+static void
+set_ssl(struct shttpd_ctx *ctx, const char *pem)
+{
+       SSL_CTX         *CTX;
+       void            *lib;
+       struct ssl_func *fp;
+
+       /* Load SSL library dynamically */
+       if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL)
+               elog(E_FATAL, NULL, "set_ssl: cannot load %s", SSL_LIB);
+
+       for (fp = ssl_sw; fp->name != NULL; fp++)
+               if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL)
+                       elog(E_FATAL, NULL,"set_ssl: cannot find %s", fp->name);
+
+       /* Initialize SSL crap */
+       SSL_library_init();
+
+       if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
+               elog(E_FATAL, NULL, "SSL_CTX_new error");
+       else if (SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
+               elog(E_FATAL, NULL, "cannot open %s", pem);
+       else if (SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
+               elog(E_FATAL, NULL, "cannot open %s", pem);
+       ctx->ssl_ctx = CTX;
+}
+#endif /* NO_SSL */
+
+static void
+open_log_file(FILE **fpp, const char *path)
+{
+       if (*fpp != NULL)
+               (void) fclose(*fpp);
+
+       if (path == NULL) {
+               *fpp = NULL;
+       } else if ((*fpp = fopen(path, "a")) == NULL) {
+               elog(E_FATAL, NULL, "cannot open log file %s: %s",
+                   path, strerror(errno));
+       }
+}
+
+static void set_alog(struct shttpd_ctx *ctx, const char *path) {
+       open_log_file(&ctx->access_log, path);
+}
+
+static void set_elog(struct shttpd_ctx *ctx, const char *path) {
+       open_log_file(&ctx->error_log, path);
+}
+
+static void show_cfg_page(struct shttpd_arg *arg);
+
+static void
+set_cfg_uri(struct shttpd_ctx *ctx, const char *uri)
+{
+       free_list(&ctx->registered_uris, &registered_uri_destructor);
+
+       if (uri != NULL)
+               shttpd_register_uri(ctx, uri, &show_cfg_page, ctx);
+}
+
+static struct worker *
+add_worker(struct shttpd_ctx *ctx)
+{
+       struct worker   *worker;
+
+       if ((worker = calloc(1, sizeof(*worker))) == NULL)
+               elog(E_FATAL, NULL, "Cannot allocate worker");
+       LL_INIT(&worker->connections);
+       worker->ctx = ctx;
+       (void) shttpd_socketpair(worker->ctl);
+       LL_TAIL(&ctx->workers, &worker->link);
+
+       return (worker);
+}
+
+#if !defined(NO_THREADS)
+static void
+poll_worker(struct worker *worker, int milliseconds)
+{
+       fd_set          read_set, write_set;
+       int             max_fd = -1;
+
+       FD_ZERO(&read_set);
+       FD_ZERO(&write_set);
+
+       multiplex_worker_sockets(worker, &max_fd,
+                       &read_set, &write_set, &milliseconds);
+
+       if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
+               return;;
+
+       process_worker_sockets(worker, &read_set);
+}
+
+static void
+worker_function(void *param)
+{
+       struct worker *worker = param;
+
+       while (worker->exit_flag == 0)
+               poll_worker(worker, 1000 * 10);
+
+       free_list(&worker->connections, connection_desctructor);
+       free(worker);
+}
+
+static void
+set_workers(struct shttpd_ctx *ctx, const char *value)
+{
+       int             new_num, old_num;
+       struct llhead   *lp, *tmp;
+       struct worker   *worker;
+
+               new_num = atoi(value);
+       old_num = 0;
+       LL_FOREACH(&ctx->workers, lp)
+               old_num++;
+
+       if (new_num == 1) {
+               if (old_num > 1)
+                       /* Stop old threads */
+                       LL_FOREACH_SAFE(&ctx->workers, lp, tmp) {
+                               worker = LL_ENTRY(lp, struct worker, link);
+                               LL_DEL(&worker->link);
+                               worker = LL_ENTRY(lp, struct worker, link);
+                               worker->exit_flag = 1;
+                       }
+               (void) add_worker(ctx);
+       } else {
+               /* FIXME: we cannot here reduce the number of threads */
+               while (new_num > 1 && new_num > old_num) {
+                       worker = add_worker(ctx);
+                       _beginthread(worker_function, 0, worker);
+                       old_num++;
+               }
+       }
+}
+#endif /* NO_THREADS */
+
+static const struct opt {
+       int             index;          /* Index in shttpd_ctx          */
+       const char      *name;          /* Option name in config file   */
+       const char      *description;   /* Description                  */
+       const char      *default_value; /* Default option value         */
+       void (*setter)(struct shttpd_ctx *, const char *);
+} known_options[] = {
+       {OPT_ROOT, "root", "\tWeb root directory", ".", NULL},
+       {OPT_INDEX_FILES, "index_files", "Index files", INDEX_FILES, NULL},
+#ifndef NO_SSL
+       {OPT_SSL_CERTIFICATE, "ssl_cert", "SSL certificate file", NULL,set_ssl},
+#endif /* NO_SSL */
+       {OPT_PORTS, "ports", "Listening ports", LISTENING_PORTS, set_ports},
+       {OPT_DIR_LIST, "dir_list", "Directory listing", "yes", NULL},
+       {OPT_CFG_URI, "cfg_uri", "Config uri", NULL, set_cfg_uri},
+       {OPT_PROTECT, "protect", "URI to htpasswd mapping", NULL, NULL},
+#ifndef NO_CGI
+       {OPT_CGI_EXTENSIONS, "cgi_ext", "CGI extensions", CGI_EXT, NULL},
+       {OPT_CGI_INTERPRETER, "cgi_interp", "CGI interpreter", NULL, NULL},
+       {OPT_CGI_ENVIRONMENT, "cgi_env", "Additional CGI env vars", NULL, NULL},
+#endif /* NO_CGI */
+       {OPT_SSI_EXTENSIONS, "ssi_ext", "SSI extensions", SSI_EXT, NULL},
+#ifndef NO_AUTH
+       {OPT_AUTH_REALM, "auth_realm", "Authentication domain name",REALM,NULL},
+       {OPT_AUTH_GPASSWD, "auth_gpass", "Global passwords file", NULL, NULL},
+       {OPT_AUTH_PUT, "auth_PUT", "PUT,DELETE auth file", NULL, NULL},
+#endif /* !NO_AUTH */
+#ifdef _WIN32
+       {OPT_SERVICE, "service", "Manage WinNNT service (install"
+           "|uninstall)", NULL, set_nt_service},
+       {OPT_HIDE, "systray", "Hide console, show icon on systray",
+               "no", set_systray},
+#else
+       {OPT_INETD, "inetd", "Inetd mode", "no", set_inetd},
+       {OPT_UID, "uid", "\tRun as user", NULL, set_uid},
+#endif /* _WIN32 */
+       {OPT_ACCESS_LOG, "access_log", "Access log file", NULL, set_alog},
+       {OPT_ERROR_LOG, "error_log", "Error log file", NULL, set_elog},
+       {OPT_MIME_TYPES, "mime_types", "Additional mime types list", NULL,NULL},
+       {OPT_ALIASES, "aliases", "Path=URI mappings", NULL, NULL},
+       {OPT_ACL, "acl", "\tAllow/deny IP addresses/subnets", NULL, set_acl},
+#if !defined(NO_THREADS)
+       {OPT_THREADS, "threads", "Number of worker threads", "1", set_workers},
+#endif /* !NO_THREADS */
+       {-1, NULL, NULL, NULL, NULL}
+};
+
+static const struct opt *
+find_opt(const char *opt_name)
+{
+       int     i;
+
+       for (i = 0; known_options[i].name != NULL; i++)
+               if (!strcmp(opt_name, known_options[i].name))
+                       return (known_options + i);
+
+       elog(E_FATAL, NULL, "no such option: [%s]", opt_name);
+
+       /* UNREACHABLE */
+       return (NULL);
+}
+
+void
+shttpd_set_option(struct shttpd_ctx *ctx, const char *opt, const char *val)
+{
+       const struct opt        *o = find_opt(opt);
+
+       /* Call option setter first, so it can use both new and old values */
+       if (o->setter != NULL)
+               o->setter(ctx, val);
+
+       /* Free old value if any */
+       if (ctx->options[o->index] != NULL)
+               free(ctx->options[o->index]);
+       
+       /* Set new option value */
+       ctx->options[o->index] = val ? my_strdup(val) : NULL;
+}
+
+static void
+show_cfg_page(struct shttpd_arg *arg)
+{
+       struct shttpd_ctx       *ctx = arg->user_data;
+       char                    opt_name[20], value[BUFSIZ];
+       const struct opt        *o;
+
+       opt_name[0] = value[0] = '\0';
+
+       if (!strcmp(shttpd_get_env(arg, "REQUEST_METHOD"), "POST")) {
+               if (arg->flags & SHTTPD_MORE_POST_DATA)
+                       return;
+               (void) shttpd_get_var("o", arg->in.buf, arg->in.len,
+                   opt_name, sizeof(opt_name));
+               (void) shttpd_get_var("v", arg->in.buf, arg->in.len,
+                   value, sizeof(value));
+               shttpd_set_option(ctx, opt_name, value[0] ? value : NULL);
+       }
+
+       shttpd_printf(arg, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
+           "<html><body><h1>SHTTPD v. %s</h1>", shttpd_version());
+
+       shttpd_printf(arg, "%s", "<table border=1"
+           "<tr><th>Option</th><th>Description</th>"
+           "<th colspan=2>Value</th></tr>");
+
+       if (opt_name[0] != '\0' && value[0] != '\0')
+               shttpd_printf(arg, "<p style='color: green'>Saved: %s=%s</p>",
+                   opt_name, value[0] ? value : "NULL");
+
+
+       for (o = known_options; o->name != NULL; o++) {
+               shttpd_printf(arg,
+                   "<form method=post><tr><td>%s</td><td>%s</td>"
+                   "<input type=hidden name=o value='%s'>"
+                   "<td><input type=text name=v value='%s'></td>"
+                   "<td><input type=submit value=save></td></form></tr>",
+                   o->name, o->description, o->name,
+                   ctx->options[o->index] ? ctx->options[o->index] : "");
+       }
+
+       shttpd_printf(arg, "%s", "</table></body></html>");
+       arg->flags |= SHTTPD_END_OF_OUTPUT;
+}
+
+/*
+ * Show usage string and exit.
+ */
+void
+usage(const char *prog)
+{
+       const struct opt        *o;
+
+       (void) fprintf(stderr,
+           "SHTTPD version %s (c) Sergey Lyubka\n"
+           "usage: %s [options] [config_file]\n", VERSION, prog);
+
+#if !defined(NO_AUTH)
+       fprintf(stderr, "  -A <htpasswd_file> <realm> <user> <passwd>\n");
+#endif /* NO_AUTH */
+
+       for (o = known_options; o->name != NULL; o++) {
+               (void) fprintf(stderr, "  -%s\t%s", o->name, o->description);
+               if (o->default_value != NULL)
+                       fprintf(stderr, " (default: %s)", o->default_value);
+               fputc('\n', stderr);
+       }
+
+       exit(EXIT_FAILURE);
+}
+
+static void
+set_opt(struct shttpd_ctx *ctx, const char *opt, const char *value)
+{
+       const struct opt        *o;
+
+       o = find_opt(opt);
+       if (ctx->options[o->index] != NULL)
+               free(ctx->options[o->index]);
+       ctx->options[o->index] = my_strdup(value);
+}
+
+static void
+process_command_line_arguments(struct shttpd_ctx *ctx, char *argv[])
+{
+       const char              *config_file = CONFIG_FILE;
+       char                    line[BUFSIZ], opt[BUFSIZ],
+                               val[BUFSIZ], path[FILENAME_MAX], *p;
+       FILE                    *fp;
+       size_t                  i, line_no = 0;
+
+       /* First find out, which config file to open */
+       for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
+               if (argv[i + 1] == NULL)
+                       usage(argv[0]);
+
+       if (argv[i] != NULL && argv[i + 1] != NULL) {
+               /* More than one non-option arguments are given w*/
+               usage(argv[0]);
+       } else if (argv[i] != NULL) {
+               /* Just one non-option argument is given, this is config file */
+               config_file = argv[i];
+       } else {
+               /* No config file specified. Look for one where shttpd lives */
+               if ((p = strrchr(argv[0], DIRSEP)) != 0) {
+                       my_snprintf(path, sizeof(path), "%.*s%s",
+                           p - argv[0] + 1, argv[0], config_file);
+                       config_file = path;
+               }
+       }
+
+       fp = fopen(config_file, "r");
+
+       /* If config file was set in command line and open failed, exit */
+       if (fp == NULL && argv[i] != NULL)
+               elog(E_FATAL, NULL, "cannot open config file %s: %s",
+                   config_file, strerror(errno));
+
+       if (fp != NULL) {
+
+               elog(E_LOG, NULL, "Loading config file %s", config_file);
+
+               /* Loop over the lines in config file */
+               while (fgets(line, sizeof(line), fp) != NULL) {
+
+                       line_no++;
+
+                       /* Ignore empty lines and comments */
+                       if (line[0] == '#' || line[0] == '\n')
+                               continue;
+
+                       if (sscanf(line, "%s %[^\n#]", opt, val) != 2)
+                               elog(E_FATAL, NULL, "line %d in %s is invalid",
+                                   line_no, config_file);
+
+                       set_opt(ctx, opt, val);
+               }
+
+               (void) fclose(fp);
+       }
+
+       /* Now pass through the command line options */
+       for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
+               set_opt(ctx, &argv[i][1], argv[i + 1]);
+}
+
+struct shttpd_ctx *
+shttpd_init(int argc, char *argv[])
+{
+       struct shttpd_ctx       *ctx;
+       struct tm               *tm;
+       const struct opt        *o;
+
+       if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
+               elog(E_FATAL, NULL, "cannot allocate shttpd context");
+
+       LL_INIT(&ctx->registered_uris);
+       LL_INIT(&ctx->error_handlers);
+       LL_INIT(&ctx->acl);
+       LL_INIT(&ctx->ssi_funcs);
+       LL_INIT(&ctx->listeners);
+       LL_INIT(&ctx->workers);
+
+       /* Initialize options. First pass: set default option values */
+       for (o = known_options; o->name != NULL; o++)
+               ctx->options[o->index] = o->default_value ?
+                       my_strdup(o->default_value) : NULL;
+
+       /* Second and third passes: config file and argv */
+       if (argc > 0 && argv != NULL)
+               process_command_line_arguments(ctx, argv);
+
+       /* Call setter functions */
+       for (o = known_options; o->name != NULL; o++)
+               if (o->setter && ctx->options[o->index] != NULL)
+                       o->setter(ctx, ctx->options[o->index]);
+
+       current_time = time(NULL);
+       tm = localtime(&current_time);
+       tz_offset = 0;
+
+       if (num_workers(ctx) == 1)
+               (void) add_worker(ctx);
+#if 0
+       tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
+#endif
+
+#ifdef _WIN32
+       {WSADATA data;  WSAStartup(MAKEWORD(2,2), &data);}
+#endif /* _WIN32 */
+
+       return (ctx);
+}
index 0d8d510dfde3d803ed5611a8fe39eb449e71cee9..6e473210c1376eebc7ac34aad04a9ec7672c12cb 100644 (file)
@@ -1,25 +1,13 @@
 /*
- * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * Copyright (c) 2004-2008 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
  *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
  *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * $Id: shttpd.h,v 1.9 2008/02/13 20:44:32 drozd Exp $
+ * $Id: shttpd.h,v 1.16 2008/05/31 09:02:02 drozd Exp $
  */
 
 #ifndef SHTTPD_HEADER_INCLUDED
@@ -41,54 +29,62 @@ struct ubuf {
 struct shttpd_arg {
        void            *priv;          /* Private! Do not touch!       */
        void            *state;         /* User state                   */
-       void            *user_data;     /* User-defined data            */
+       void            *user_data;     /* Data from register_uri()     */
        struct ubuf     in;             /* Input is here, POST data     */
        struct ubuf     out;            /* Output goes here             */
+
        unsigned int    flags;
-#define        SHTTPD_END_OF_OUTPUT    1
-#define        SHTTPD_CONNECTION_ERROR 2
-#define        SHTTPD_MORE_POST_DATA   4
-#define        SHTTPD_POST_BUFFER_FULL 8
-#define        SHTTPD_SSI_EVAL_TRUE    16
+#define        SHTTPD_END_OF_OUTPUT    1       /* No more data do send         */
+#define        SHTTPD_CONNECTION_ERROR 2       /* Server closed the connection */
+#define        SHTTPD_MORE_POST_DATA   4       /* arg->in has incomplete data  */
+#define        SHTTPD_POST_BUFFER_FULL 8       /* arg->in has max data         */
+#define        SHTTPD_SSI_EVAL_TRUE    16      /* SSI eval callback must set it*/
+#define        SHTTPD_SUSPEND          32      /* User wants to suspend output */
 };
 
 /*
  * User callback function. Called when certain registered URLs have been
  * requested. These are the requirements to the callback function:
  *
- * 1. it must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
+ * 1. It must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
  *     and record how many bytes are copied, into 'out.num_bytes'
- * 2. it must not block the execution
- * 3. it must set SHTTPD_END_OF_OUTPUT flag when finished
- * 4. for POST requests, it must process the incoming data (in.buf) of length
+ * 2. It must not call any blocking functions
+ * 3. It must set SHTTPD_END_OF_OUTPUT flag when there is no more data to send
+ * 4. For POST requests, it must process the incoming data (in.buf) of length
  *     'in.len', and set 'in.num_bytes', which is how many bytes of POST
- *     data is read and can be discarded by SHTTPD.
+ *     data was processed and can be discarded by SHTTPD.
  * 5. If callback allocates arg->state, to keep state, it must deallocate it
  *    at the end of coonection SHTTPD_CONNECTION_ERROR or SHTTPD_END_OF_OUTPUT
+ * 6. If callback function wants to suspend until some event, it must store
+ *     arg->priv pointer elsewhere, set SHTTPD_SUSPEND flag and return. When
+ *     the event happens, user code should call shttpd_wakeup(priv).
+ *     It is safe to call shttpd_wakeup() from any thread. User code must
+ *     not call shttpd_wakeup once the connection is closed.
  */
 typedef void (*shttpd_callback_t)(struct shttpd_arg *);
 
 /*
- * shttpd_init         Initialize shttpd context.
- * shttpd_set_option   Set new value for option.
- * shttpd_fini         Dealocate the context
- * shttpd_register_uri Setup the callback function for specified URL.
- * shtppd_listen       Setup a listening socket in the SHTTPD context
+ * shttpd_init         Initialize shttpd context
+ * shttpd_fini         Dealocate the context, close all connections
+ * shttpd_set_option   Set new value for option
+ * shttpd_register_uri Setup the callback function for specified URL
  * shttpd_poll         Do connections processing
  * shttpd_version      return string with SHTTPD version
  * shttpd_get_var      Fetch POST/GET variable value by name. Return value len
  * shttpd_get_header   return value of the specified HTTP header
- * shttpd_get_env      return string values for the following
- *                     pseudo-variables: "REQUEST_METHOD", "REQUEST_URI",
- *                     "REMOTE_USER" and "REMOTE_ADDR".
+ * shttpd_get_env      return values for the following pseudo-variables:
+                       "REQUEST_METHOD", "REQUEST_URI",
+ *                     "REMOTE_USER" and "REMOTE_ADDR"
+ * shttpd_printf       helper function to output data
+ * shttpd_handle_error register custom HTTP error handler
+ * shttpd_wakeup       clear SHTTPD_SUSPEND state for the connection
  */
 
 struct shttpd_ctx;
 
-struct shttpd_ctx *shttpd_init(void);
+struct shttpd_ctx *shttpd_init(int argc, char *argv[]);
 void shttpd_set_option(struct shttpd_ctx *, const char *opt, const char *val);
 void shttpd_fini(struct shttpd_ctx *);
-int shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl);
 void shttpd_register_uri(struct shttpd_ctx *ctx, const char *uri,
                shttpd_callback_t callback, void *const user_data);
 void shttpd_poll(struct shttpd_ctx *, int milliseconds);
@@ -104,19 +100,7 @@ void shttpd_handle_error(struct shttpd_ctx *ctx, int status,
                shttpd_callback_t func, void *const data);
 void shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
                shttpd_callback_t func, void *const user_data);
-
-/*
- * The following three functions are for applications that need to
- * load-balance the connections on their own. Many threads may be spawned
- * with one SHTTPD context per thread. Boss thread may only wait for
- * new connections by means of shttpd_accept(). Then it may scan thread
- * pool for the idle thread by means of shttpd_active(), and add new
- * connection to the context by means of shttpd_add().
- */
-void shttpd_add_socket(struct shttpd_ctx *, int sock, int is_ssl);
-int shttpd_accept(int lsn_sock, int milliseconds);
-int shttpd_active(struct shttpd_ctx *);
-
+void shttpd_wakeup(const void *priv);
 
 #ifdef __cplusplus
 }
index a7a7c8433d06793adfae9743c1c749dbf7808b49..a149d179ce3c36b3e7df018af08cf213d4ee2af0 100644 (file)
@@ -10,7 +10,7 @@
 
 #include "defs.h"
 
-static int             exit_flag;
+static int     exit_flag;      /* Program termination flag     */
 
 static void
 signal_handler(int sig_num)
@@ -27,70 +27,6 @@ signal_handler(int sig_num)
        }
 }
 
-void
-process_command_line_arguments(struct shttpd_ctx *ctx, char *argv[])
-{
-       const char      *config_file = CONFIG_FILE;
-       char            line[BUFSIZ], opt[BUFSIZ],
-                       val[BUFSIZ], path[FILENAME_MAX], *p;
-       FILE            *fp;
-       size_t          i, line_no = 0;
-
-       /* First find out, which config file to open */
-       for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
-               if (argv[i + 1] == NULL)
-                       usage(argv[0]);
-
-       if (argv[i] != NULL && argv[i + 1] != NULL) {
-               /* More than one non-option arguments are given w*/
-               usage(argv[0]);
-       } else if (argv[i] != NULL) {
-               /* Just one non-option argument is given, this is config file */
-               config_file = argv[i];
-       } else {
-               /* No config file specified. Look for one where shttpd lives */
-               if ((p = strrchr(argv[0], DIRSEP)) != 0) {
-                       my_snprintf(path, sizeof(path), "%.*s%s",
-                           p - argv[0] + 1, argv[0], config_file);
-                       config_file = path;
-               }
-       }
-
-       fp = fopen(config_file, "r");
-
-       /* If config file was set in command line and open failed, exit */
-       if (fp == NULL && argv[i] != NULL)
-               elog(E_FATAL, NULL, "cannot open config file %s: %s",
-                   config_file, strerror(errno));
-
-       if (fp != NULL) {
-
-               elog(E_LOG, NULL, "Loading config file %s", config_file);
-
-               /* Loop over the lines in config file */
-               while (fgets(line, sizeof(line), fp) != NULL) {
-
-                       line_no++;
-
-                       /* Ignore empty lines and comments */
-                       if (line[0] == '#' || line[0] == '\n')
-                               continue;
-
-                       if (sscanf(line, "%s %[^\n#]", opt, val) != 2)
-                               elog(E_FATAL, NULL, "line %d in %s is invalid",
-                                   line_no, config_file);
-
-                       shttpd_set_option(ctx, opt, val);
-               }
-
-               (void) fclose(fp);
-       }
-
-       /* Now pass through the command line options */
-       for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
-               shttpd_set_option(ctx, &argv[i][1], argv[i + 1]);
-}
-
 int
 main(int argc, char *argv[])
 {
@@ -107,24 +43,11 @@ main(int argc, char *argv[])
        if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
                usage(argv[0]);
 
-       ctx = shttpd_init();
-       process_command_line_arguments(ctx, argv);
+#if defined(_WIN32)
+       try_to_run_as_nt_service();
+#endif /* _WIN32 */
 
 #ifndef _WIN32
-       /* Switch to alternate UID, it is safe now, after shttpd_listen() */
-       if (ctx->options[OPT_UID] != NULL) {
-               struct passwd   *pw;
-
-               if ((pw = getpwnam(ctx->options[OPT_UID])) == NULL)
-                       elog(E_FATAL, 0, "main: unknown user [%s]",
-                           ctx->options[OPT_UID]);
-               else if (setgid(pw->pw_gid) == -1)
-                       elog(E_FATAL, NULL, "main: setgid(%s): %s",
-                           ctx->options[OPT_UID], strerror(errno));
-               else if (setuid(pw->pw_uid) == -1)
-                       elog(E_FATAL, NULL, "main: setuid(%s): %s",
-                           ctx->options[OPT_UID], strerror(errno));
-       }
        (void) signal(SIGCHLD, signal_handler);
        (void) signal(SIGPIPE, SIG_IGN);
 #endif /* _WIN32 */
@@ -132,22 +55,15 @@ main(int argc, char *argv[])
        (void) signal(SIGTERM, signal_handler);
        (void) signal(SIGINT, signal_handler);
 
-       if (IS_TRUE(ctx, OPT_INETD)) {
-               shttpd_set_option(ctx, "ports", NULL);
-               (void) freopen("/dev/null", "a", stderr);
-               shttpd_add_socket(ctx, fileno(stdin), 0);
-       }
+       ctx = shttpd_init(argc, argv);
 
        elog(E_LOG, NULL, "shttpd %s started on port(s) %s, serving %s",
            VERSION, ctx->options[OPT_PORTS], ctx->options[OPT_ROOT]);
 
        while (exit_flag == 0)
-               shttpd_poll(ctx, 5000);
-
-       elog(E_LOG, NULL, "%d requests %.2lf Mb in %.2lf Mb out. "
-           "Exit on signal %d", ctx->nrequests, (double) (ctx->in / 1048576),
-           (double) ctx->out / 1048576, exit_flag);
+               shttpd_poll(ctx, 10 * 1000);
 
+       elog(E_LOG, NULL, "Exit on signal %d", exit_flag);
        shttpd_fini(ctx);
 
        return (EXIT_SUCCESS);