From: Charles Kerr Date: Wed, 16 Jul 2008 23:55:49 +0000 (+0000) Subject: (third-party) upgrade shttpd from 1.39 to the latest, 1.41 X-Git-Tag: 1.31~90 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2281c39c1faa0491cbc194d502a449e0561d5d66;p=transmission (third-party) upgrade shttpd from 1.39 to the latest, 1.41 --- diff --git a/libtransmission/rpc-server.c b/libtransmission/rpc-server.c index 106aca682..083308d13 100644 --- a/libtransmission/rpc-server.c +++ b/libtransmission/rpc-server.c @@ -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; iuri, 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)); diff --git a/third-party/shttpd/cgi.c b/third-party/shttpd/cgi.c index 925b61a9f..99b3d203a 100644 --- a/third-party/shttpd/cgi.c +++ b/third-party/shttpd/cgi.c @@ -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; diff --git a/third-party/shttpd/compat_unix.h b/third-party/shttpd/compat_unix.h index 67c935faa..53c7f0327 100644 --- a/third-party/shttpd/compat_unix.h +++ b/third-party/shttpd/compat_unix.h @@ -20,14 +20,16 @@ #include #include #include + +#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 index 730a492a8..000000000 --- a/third-party/shttpd/config.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (c) 2004-2005 Sergey Lyubka - * 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, ®istered_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" - "

SHTTPD v. %s

", shttpd_version()); - - shttpd_printf(arg, "%s", "" - ""); - - if (opt_name[0] != '\0' && value[0] != '\0') - shttpd_printf(arg, "

Saved: %s=%s

", - opt_name, value[0] ? value : "NULL"); - - - for (o = known_options; o->name != NULL; o++) { - shttpd_printf(arg, - "" - "" - "" - "", - o->name, o->description, o->name, - ctx->options[o->index] ? ctx->options[o->index] : ""); - } - - shttpd_printf(arg, "%s", "
OptionDescriptionValue
%s%s
"); - 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 \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(¤t_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); -} diff --git a/third-party/shttpd/config.h b/third-party/shttpd/config.h index eaaee66e0..8eb797dd7 100644 --- a/third-party/shttpd/config.h +++ b/third-party/shttpd/config.h @@ -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 */ diff --git a/third-party/shttpd/defs.h b/third-party/shttpd/defs.h index 7ac19fb29..983a5b098 100644 --- a/third-party/shttpd/defs.h +++ b/third-party/shttpd/defs.h @@ -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])) @@ -26,10 +26,6 @@ #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 diff --git a/third-party/shttpd/io_emb.c b/third-party/shttpd/io_emb.c index 762afe5a3..00a275d4b 100644 --- a/third-party/shttpd/io_emb.c +++ b/third-party/shttpd/io_emb.c @@ -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, diff --git a/third-party/shttpd/io_file.c b/third-party/shttpd/io_file.c index 1dce074fb..0d382606c 100644 --- a/third-party/shttpd/io_file.c +++ b/third-party/shttpd/io_file.c @@ -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); diff --git a/third-party/shttpd/io_ssl.c b/third-party/shttpd/io_ssl.c index 753257fd8..e3167e223 100644 --- a/third-party/shttpd/io_ssl.c +++ b/third-party/shttpd/io_ssl.c @@ -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); diff --git a/third-party/shttpd/shttpd.c b/third-party/shttpd/shttpd.c index 1b02f80c7..cc55fa86f 100644 --- a/third-party/shttpd/shttpd.c +++ b/third-party/shttpd/shttpd.c @@ -10,13 +10,14 @@ /* * 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, ®istered_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" + "

SHTTPD v. %s

", shttpd_version()); + + shttpd_printf(arg, "%s", "" + ""); + + if (opt_name[0] != '\0' && value[0] != '\0') + shttpd_printf(arg, "

Saved: %s=%s

", + opt_name, value[0] ? value : "NULL"); + + + for (o = known_options; o->name != NULL; o++) { + shttpd_printf(arg, + "" + "" + "" + "", + o->name, o->description, o->name, + ctx->options[o->index] ? ctx->options[o->index] : ""); + } + + shttpd_printf(arg, "%s", "
OptionDescriptionValue
%s%s
"); + 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 \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(¤t_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); +} diff --git a/third-party/shttpd/shttpd.h b/third-party/shttpd/shttpd.h index 0d8d510df..6e473210c 100644 --- a/third-party/shttpd/shttpd.h +++ b/third-party/shttpd/shttpd.h @@ -1,25 +1,13 @@ /* - * Copyright (c) 2004-2005 Sergey Lyubka + * Copyright (c) 2004-2008 Sergey Lyubka + * 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 } diff --git a/third-party/shttpd/standalone.c b/third-party/shttpd/standalone.c index a7a7c8433..a149d179c 100644 --- a/third-party/shttpd/standalone.c +++ b/third-party/shttpd/standalone.c @@ -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);