adamc@1268: #include "config.h"
adamc@1268: 
adamc@859: #include <stdio.h>
adamc@859: #include <string.h>
adamc@859: #include <stdlib.h>
adamc@859: #include <sys/types.h>
adamc@859: #include <sys/socket.h>
adamc@859: #include <netdb.h>
adamc@859: #include <netinet/in.h>
adamc@859: #include <unistd.h>
adamc@859: #include <signal.h>
adamc@859: #include <stdarg.h>
adamc@1094: #include <ctype.h>
adamc@859: 
adamc@859: #include <pthread.h>
adamc@859: 
adamc@859: #include "urweb.h"
adamc@859: #include "request.h"
adamc@859: #include "queue.h"
adamc@859: 
adamc@859: #include "fastcgi.h"
adamc@859: 
adamc@1094: extern uw_app uw_application;
adamc@1094: 
adamc@859: typedef struct {
adamc@859:   unsigned char version;
adamc@859:   unsigned char type;
adamc@859:   unsigned char requestIdB1;
adamc@859:   unsigned char requestIdB0;
adamc@859:   unsigned char contentLengthB1;
adamc@859:   unsigned char contentLengthB0;
adamc@859:   unsigned char paddingLength;
adamc@859:   unsigned char reserved;
adamc@859:   unsigned char contentData[65535];
adamc@859: } FCGI_Record;
adamc@859: 
adamc@859: typedef struct {
adamc@859:   FCGI_Record r;
adamc@859:   int sock;
adamc@859: } FCGI_Output;
adamc@859: 
adamc@859: typedef struct {
adamc@859:   char buf[sizeof(FCGI_Record) + 255];
adamc@859:   int available, used, sock;
adamc@859: } FCGI_Input;
adamc@859: 
adamc@859: static FCGI_Output *fastcgi_output() {
adamc@859:   FCGI_Output *o = malloc(sizeof(FCGI_Output));
adamc@859: 
adamc@859:   o->r.version = FCGI_VERSION_1;
adamc@859:   o->r.paddingLength = 0;
adamc@859:   o->r.reserved = 0;
adamc@859: 
adamc@859:   return o;
adamc@859: }
adamc@859: 
adamc@859: static FCGI_Input *fastcgi_input() {
adamc@859:   FCGI_Input *i = malloc(sizeof(FCGI_Input));
adamc@859: 
adamc@859:   i->available = i->used = 0;
adamc@859: 
adamc@859:   return i;
adamc@859: }
adamc@859: 
adamc@859: static void fastcgi_input_reset(FCGI_Input *i) {
adamc@859:   i->available = i->used = 0;
adamc@859: }
adamc@859: 
adamc@859: static int fastcgi_send(FCGI_Output *o,
adamc@859:                         unsigned char type,
adamc@859:                         unsigned short contentLength) {
adamc@859:   o->r.type = type;
adamc@860:   o->r.requestIdB1 = o->r.requestIdB0 = 0;
adamc@859:   o->r.contentLengthB1 = contentLength >> 8;
adamc@859:   o->r.contentLengthB0 = contentLength & 255;
adamc@861:   return uw_really_send(o->sock, &o->r, sizeof(o->r) - 65535 + contentLength);
adamc@859: }
adamc@859: 
adamc@859: #define LATEST(i) ((FCGI_Record *)(i->buf + i->used))
adamc@859: 
adamc@859: static FCGI_Record *fastcgi_recv(FCGI_Input *i) {
adamc@859:   while (1) {
adamc@859:     ssize_t n;
adamc@859: 
adamc@859:     if (i->available >= i->used + sizeof(FCGI_Record) - 65535
adamc@859:         && i->available >= i->used + sizeof(FCGI_Record) - 65535
adamc@860:         + ((LATEST(i)->contentLengthB1 << 8) | LATEST(i)->contentLengthB0)
adamc@859:         + LATEST(i)->paddingLength) {
adamc@859:       FCGI_Record *r = LATEST(i);
adamc@859: 
adamc@859:       i->used += sizeof(FCGI_Record) - 65535
adamc@860:         + ((LATEST(i)->contentLengthB1 << 8) | LATEST(i)->contentLengthB0)
adamc@859:         + LATEST(i)->paddingLength;
adamc@859:       
adamc@859:       return r;
adamc@859:     }
adamc@859: 
adamc@859:     if (i->used > 0) {
adamc@859:       memmove(i->buf, i->buf + i->used, i->available - i->used);
adamc@859:       i->available -= i->used;
adamc@859:       i->used = 0;
adamc@859:     }
adamc@859: 
adamc@859:     n = recv(i->sock, i->buf + i->available, sizeof(i->buf) - i->available, 0);
adamc@859: 
adamc@859:     if (n <= 0)
adamc@859:       return NULL;
adamc@859: 
adamc@859:     i->available += n;
adamc@859:   }
adamc@859: }
adamc@859: 
adamc@859: static void on_success(uw_context ctx) { }
adamc@859: 
adamc@859: static void on_failure(uw_context ctx) {
adamc@859:   uw_write_header(ctx, "Status: 500 Internal Server Error\r\n");
adamc@859: }
adamc@859: 
adamc@863: static int write_stdout(void *data, const char *buf, size_t len) {
adamc@860:   FCGI_Output *o = (FCGI_Output *)data;
adamc@860:   while (len > 0) {
adamc@860:     size_t len2 = len;
adamc@860:     if (len2 > 65535)
adamc@860:       len2 = 65535;
adamc@860:     memcpy(o->r.contentData, buf, len2);
adamc@860:     if (fastcgi_send(o, FCGI_STDOUT, len2)) {
adamc@860:       fprintf(stderr, "fastcgi_send() failed in write_stdout().\n");
adamc@860:       return -1;
adamc@860:     }
adamc@860:     buf += len2;
adamc@860:     len -= len2;
adamc@860:   }
adamc@860: 
adamc@860:   return 0;
adamc@860: }
adamc@860: 
adam@1411: #include <errno.h>
adam@1411: 
adamc@859: static void write_stderr(FCGI_Output *o, const char *fmt, ...) {
adamc@859:   int len;
adamc@859:   va_list ap;
adamc@859:   va_start(ap, fmt);
adamc@859: 
adamc@1134:   len = vsnprintf((char *)o->r.contentData, 65535, fmt, ap);
adamc@859:   if (len < 0)
adamc@860:     fprintf(stderr, "vsnprintf() failed in write_stderr().\n");
adamc@860:   else if (fastcgi_send(o, FCGI_STDERR, len))
adamc@860:     fprintf(stderr, "fastcgi_send() failed in write_stderr().\n");
adamc@860: }
adamc@860: 
adamc@860: static void close_stream(FCGI_Output *o, unsigned char type) {
adamc@860:   if (fastcgi_send(o, type, 0))
adamc@860:     fprintf(stderr, "fastcgi_send() failed in close_stream().\n");
adamc@859: }
adamc@859: 
adamc@859: static void log_error(void *data, const char *fmt, ...) {
adamc@859:   FCGI_Output *o = (FCGI_Output *)data;
adamc@859:   va_list ap;
adamc@859:   va_start(ap, fmt);
adamc@859: 
adamc@859:   if (o) {
adamc@1134:     int len = vsnprintf((char *)o->r.contentData, 65535, fmt, ap);
adamc@859:     if (len < 0)
adamc@859:       fprintf(stderr, "vsnprintf() failed in log_error().\n");
adamc@860:     else if (fastcgi_send(o, FCGI_STDERR, len))
adamc@859:       fprintf(stderr, "fastcgi_send() failed in log_error().\n");
adamc@859:   } else
adamc@859:     vfprintf(stderr, fmt, ap);
adamc@859: }
adamc@859: 
adamc@859: static void log_debug(void *data, const char *fmt, ...) {
adam@1411:   FCGI_Output *o = (FCGI_Output *)data;
adam@1411:   va_list ap;
adam@1411:   va_start(ap, fmt);
adam@1411: 
adam@1411:   if (o) {
adam@1411:     strcpy((char *)o->r.contentData, "DEBUG: ");
adam@1411:     int len = vsnprintf((char *)o->r.contentData + 7, 65535 - 7, fmt, ap);
adam@1411:     if (len < 0)
adam@1411:       fprintf(stderr, "vsnprintf() failed in log_debug().\n");
adam@1411:     else if (fastcgi_send(o, FCGI_STDERR, len + 7)) {
adam@1411:       len += 7;
adam@1411:       if (len >= 65535) len = 65534;
adam@1411:       o->r.contentData[len] = 0;
adam@1411:       fputs((char *)o->r.contentData, stderr);
adam@1411:       fflush(stderr);
adam@1411:     }
adam@1411:   } else
adam@1411:     vfprintf(stderr, fmt, ap);
adamc@859: }
adamc@859: 
adamc@860: typedef struct {
adamc@860:   char *name, *value;
adamc@860:   unsigned name_len, value_len;
adamc@860: } nvp;
adamc@860: 
adamc@860: static char *search_nvps(nvp *nvps, const char *h) {
adamc@862:   for (; nvps->name[0]; ++nvps)
adamc@860:     if (!strcmp(h, nvps->name))
adamc@860:       return nvps->value;
adamc@860: 
adamc@860:   return NULL;
adamc@860: }
adamc@860: 
adamc@860: typedef struct {
adamc@860:   nvp *nvps;
adamc@860:   char *uppercased;
adamc@860:   int n_nvps, uppercased_len;
adamc@860: } headers;
adamc@860: 
adamc@860: static char *get_header(void *data, const char *h) {
adamc@860:   headers *hs = (headers *)data;
adamc@860:   size_t len = strlen(h);
adamc@1134:   char *s;
adamc@860:   const char *saved_h = h;
adamc@860: 
adamc@860:   if (len > hs->uppercased_len) {
adamc@860:     hs->uppercased_len = len;
adamc@860:     hs->uppercased = realloc(hs->uppercased, len + 6);
adamc@860:   }
adamc@860: 
adamc@860:   strcpy(hs->uppercased, "HTTP_");
adamc@860:   for (s = hs->uppercased+5; *h; ++h)
adam@1435:     *s++ = *h == '-' ? '_' : toupper((int)*h);
adamc@860:   *s = 0;
adamc@860: 
adamc@860:   if (!strcasecmp(saved_h, "Content-length")
adamc@864:       || !strcasecmp(saved_h, "Content-type")) {
adamc@1134:     if ((s = search_nvps(hs->nvps, hs->uppercased + 5)))
adamc@864:       return s;
adamc@864:   }
adamc@864:   
adamc@864:   return search_nvps(hs->nvps, hs->uppercased);
adamc@860: }
adamc@860: 
adamc@864: static int read_funny_len(unsigned char **buf, int *len) {
adamc@861:   if (*len <= 0)
adamc@861:     return -1;
adamc@861: 
adamc@861:   if ((*buf)[0] >> 7 == 0) {
adamc@861:     int r = (*buf)[0];
adamc@861:     ++*buf;
adamc@861:     --*len;
adamc@861:     return r;
adamc@861:   }
adamc@861:   else if (*len < 4)
adamc@861:     return -1;
adamc@861:   else {
adamc@1049:     int r = (((*buf)[0] & 0x7f) << 24) + ((*buf)[1] << 16) + ((*buf)[2] << 8) + (*buf)[3];
adamc@861:     *buf += 4;
adamc@861:     *len -= 4;
adamc@861:     return r;
adamc@861:   }
adamc@861: }
adamc@861: 
adamc@864: static int read_nvp(unsigned char **buf, int len, nvp *nv) {
adamc@860:   int nameLength, valueLength;
adamc@860: 
adamc@864:   if ((nameLength = read_funny_len(buf, &len)) < 0)
adamc@860:     return -1;
adamc@864:   if ((valueLength = read_funny_len(buf, &len)) < 0)
adamc@1049:     return -2;
adamc@860:   if (len < nameLength + valueLength)
adamc@1049:     return -3;
adamc@860: 
adamc@862:   if (nameLength+1 > nv->name_len) {
adamc@860:     nv->name_len = nameLength+1;
adamc@862:     nv->name = realloc(nv->name, nv->name_len);
adamc@860:   }
adamc@862:   if (valueLength+1 > nv->value_len) {
adamc@860:     nv->value_len = valueLength+1;
adamc@862:     nv->value = realloc(nv->value, nv->value_len);
adamc@860:   }
adamc@860: 
adamc@864:   memcpy(nv->name, *buf, nameLength);
adamc@860:   nv->name[nameLength] = 0;
adamc@860: 
adamc@864:   memcpy(nv->value, *buf + nameLength, valueLength);
adamc@860:   nv->value[valueLength] = 0;
adamc@860: 
adamc@864:   *buf += nameLength + valueLength;
adamc@864: 
adamc@860:   return 0;
adamc@860: }
adamc@860: 
adamc@863: static int fastcgi_close_with(FCGI_Output *out, request_result rr) {
adamc@863:   FCGI_EndRequestBody *erb = (FCGI_EndRequestBody *)out->r.contentData;
adamc@863: 
adamc@863:   close_stream(out, FCGI_STDOUT);
adamc@863:   close_stream(out, FCGI_STDERR);
adamc@863: 
adamc@863:   if (rr == SERVED)
adamc@863:     erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0;
adamc@863:   else
adamc@863:     erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0xFF;
adamc@863: 
adamc@863:   erb->protocolStatus = FCGI_REQUEST_COMPLETE;
adamc@863:   fastcgi_send(out, FCGI_END_REQUEST, sizeof(FCGI_EndRequestBody));
adamc@863:   return close(out->sock);
adamc@863: }
adamc@863: 
adamc@863: static int fastcgi_close(int sock) {
adamc@863:   FCGI_Output out;
adamc@863:   out.sock = sock;
adamc@863:   out.r.version = FCGI_VERSION_1;
adamc@863:   out.r.paddingLength = 0;
adamc@863:   out.r.reserved = 0;
adamc@863: 
adamc@863:   return fastcgi_close_with(&out, SERVED);
adamc@863: }
adamc@863: 
adamc@863: int fastcgi_send_normal(int sock, const void *buf, ssize_t len) {
adamc@863:   FCGI_Output out;
adamc@863:   out.sock = sock;
adamc@863:   out.r.version = FCGI_VERSION_1;
adamc@863:   out.r.paddingLength = 0;
adamc@863:   out.r.reserved = 0;
adamc@863: 
adamc@863:   return write_stdout(&out, buf, len);
adamc@863: }
adamc@863: 
adamc@859: static void *worker(void *data) {
adamc@859:   FCGI_Input *in = fastcgi_input();
adamc@859:   FCGI_Output *out = fastcgi_output();
adam@1446:   uw_context ctx = uw_request_new_context(*(int *)data, &uw_application, out, log_error, log_debug);
adamc@859:   uw_request_context rc = uw_new_request_context();
adamc@860:   headers hs;
adamc@860:   size_t body_size = 0;
adamc@860:   char *body = malloc(0);
adamc@860:   size_t path_size = 0;
adamc@860:   char *path_buf = malloc(0);
adamc@860: 
adamc@860:   hs.uppercased = malloc(0);
adamc@860:   hs.uppercased_len = 0;
adamc@860:   hs.nvps = malloc(sizeof(nvp));
adamc@860:   hs.n_nvps = 1;
adamc@862:   hs.nvps[0].name = malloc(1);
adamc@862:   hs.nvps[0].name_len = 1;
adamc@862:   hs.nvps[0].value = malloc(0);
adamc@862:   hs.nvps[0].value_len = 0;
adamc@859: 
adamc@859:   while (1) {
adamc@859:     FCGI_Record *r;
adamc@860:     size_t used_nvps = 0;
adamc@860:     int body_len, body_read;
adamc@860:     char *s;
adamc@860:     char *method, *path, *path_info, *query_string;
adamc@859: 
adamc@859:     in->sock = out->sock = uw_dequeue();
adamc@859: 
adamc@859:     if (!(r = fastcgi_recv(in))) {
adamc@859:       fprintf(stderr, "Error receiving initial message\n");
adamc@860:       goto done;
adamc@859:     }
adamc@861: 
adamc@859:     if (r->type != FCGI_BEGIN_REQUEST) {
adamc@859:       write_stderr(out, "First message is not BEGIN_REQUEST\n");
adamc@859:       goto done;
adamc@1139:     } else if (r->contentData[1] != FCGI_RESPONDER) {
adamc@1139:       write_stderr(out, "Request is for a role besides RESPONDER\n");
adamc@859:       goto done;
adamc@859:     }
adamc@859: 
adamc@860:     while (1) {
adamc@864:       unsigned char *buf;
adamc@864:       int len;
adamc@860: 
adamc@860:       if (!(r = fastcgi_recv(in))) {
adamc@860:         write_stderr(out, "Error receiving environment variables\n");
adamc@860:         goto done;
adamc@860:       }
adamc@860: 
adamc@860:       if (r->type != FCGI_PARAMS) {
adamc@860:         write_stderr(out, "Expected FCGI_PARAMS but got %d\n", r->type);
adamc@860:         goto done;
adamc@860:       }
adamc@860: 
adamc@860:       if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0)
adamc@860:         break;
adamc@860: 
adamc@864:       len = (r->contentLengthB1 << 8) | r->contentLengthB0;
adamc@864: 
adamc@864:       for (buf = r->contentData; buf < r->contentData + len; ) {
adamc@864:         if (used_nvps == hs.n_nvps-1) {
adamc@864:           ++hs.n_nvps;
adamc@864:           hs.nvps = realloc(hs.nvps, hs.n_nvps * sizeof(nvp));
adamc@864:           hs.nvps[hs.n_nvps-1].name = malloc(1);
adamc@864:           hs.nvps[hs.n_nvps-1].value = malloc(0);
adamc@864:           hs.nvps[hs.n_nvps-1].name_len = 1;
adamc@864:           hs.nvps[hs.n_nvps-1].value_len = 0;
adamc@864:         }
adamc@864:         
adamc@864:         if (read_nvp(&buf, len - (buf - r->contentData), &hs.nvps[used_nvps]) < 0) {
adamc@864:           write_stderr(out, "Error reading FCGI_PARAMS name-value pair\n");
adamc@864:           goto done;
adamc@864:         }
adamc@864: 
adamc@1049:         //write_stderr(out, "PARAM: %s -> %s\n", hs.nvps[used_nvps].name, hs.nvps[used_nvps].value);
adamc@864: 
adamc@864:         ++used_nvps;
adamc@860:       }
adamc@859:     }
adamc@860: 
adamc@862:     hs.nvps[used_nvps].name[0] = 0;
adamc@860: 
adamc@1134:     if ((s = get_header(&hs, "Content-Length"))) {
adamc@860:       body_len = atoi(s);
adamc@860:       if (body_len < 0) {
adamc@860:         write_stderr(out, "Invalid Content-Length\n");
adamc@860:         goto done;
adamc@860:       }
adamc@860:     } else
adamc@860:       body_len = 0;
adamc@860: 
adamc@861:     if (body_len+1 > body_size) {
adamc@861:       body_size = body_len+1;
adamc@860:       body = realloc(body, body_size);
adamc@860:     }
adamc@860: 
adamc@860:     for (body_read = 0; body_read < body_len; ) {
adamc@860:       int this_len;
adamc@860: 
adamc@860:       if (!(r = fastcgi_recv(in))) {
adamc@860:         write_stderr(out, "Error receiving STDIN\n");
adamc@860:         goto done;
adamc@860:       }
adamc@860: 
adamc@860:       if (r->type != FCGI_STDIN) {
adamc@860:         write_stderr(out, "Expected FCGI_STDIN but got %d\n", r->type);
adamc@860:         goto done;
adamc@860:       }
adamc@860: 
adamc@860:       if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0) {
adamc@860:         write_stderr(out, "End of STDIN\n");
adamc@860:         break;
adamc@860:       }
adamc@860: 
adamc@860:       this_len = (r->contentLengthB1 << 8) | r->contentLengthB0;
adamc@860: 
adamc@860:       if (body_read + this_len > body_len) {
adamc@860:         write_stderr(out, "Too much STDIN\n");
adamc@860:         goto done;
adamc@860:       }
adamc@860: 
adamc@860:       memcpy(&body[body_read], r->contentData, this_len);
adamc@860:       body_read += this_len;
adamc@860:     }
adamc@860: 
adamc@861:     body[body_read] = 0;
adamc@861: 
adamc@860:     if (!(method = search_nvps(hs.nvps, "REQUEST_METHOD"))) {
adamc@860:       write_stderr(out, "REQUEST_METHOD not set\n");
adamc@860:       goto done;
adamc@860:     }
adamc@860: 
adamc@860:     if (!(path = search_nvps(hs.nvps, "SCRIPT_NAME"))) {
adamc@860:       write_stderr(out, "SCRIPT_NAME not set\n");
adamc@860:       goto done;
adamc@860:     }
adamc@861: 
adamc@1134:     if ((path_info = search_nvps(hs.nvps, "PATH_INFO"))) {
adamc@860:       int len1 = strlen(path), len2 = strlen(path_info);
adamc@860:       int len = len1 + len2 + 1;
adamc@860: 
adamc@860:       if (len > path_size) {
adamc@860:         path_size = len;
adamc@860:         path_buf = realloc(path_buf, path_size);
adamc@860:       }
adamc@860: 
adamc@860:       sprintf(path_buf, "%s%s", path, path_info);
adamc@860:       path = path_buf;
adamc@860:     }
adamc@860: 
adamc@860:     if (!(query_string = search_nvps(hs.nvps, "QUERY_STRING")))
adamc@860:       query_string = "";
adamc@860: 
adamc@860:     uw_set_headers(ctx, get_header, &hs);
adamc@861: 
adamc@860:     {
adamc@860:       request_result rr;
adamc@860: 
adamc@860:       rr = uw_request(rc, ctx, method, path, query_string, body, body_read,
adamc@860:                       on_success, on_failure,
adamc@860:                       out, log_error, log_debug,
adamc@863:                       in->sock, fastcgi_send_normal, fastcgi_close);
adamc@860: 
adamc@860:       if (rr == KEEP_OPEN)
adamc@860:         goto done2;
adamc@860: 
adamc@860:       uw_output(ctx, write_stdout, out);
adamc@863:       fastcgi_close_with(out, rr);
adamc@863:       goto done2;
adamc@860:     }
adamc@859: 
adamc@859:   done:
adamc@859:     close(in->sock);
adamc@860:   done2:
adamc@859:     fastcgi_input_reset(in);
adamc@859:     uw_reset(ctx);
adamc@859:   }
adamc@1138: 
adamc@1138:   return NULL;
adamc@859: }
adamc@859: 
adamc@859: static void help(char *cmd) {
adamc@859:   printf("Usage: %s [-t <thread-count>]\n", cmd);
adamc@859: }
adamc@859: 
adamc@859: static void sigint(int signum) {
adamc@859:   printf("Exiting....\n");
adamc@859:   exit(0);
adamc@859: }
adamc@859: 
adamc@1094: static loggers ls = {&uw_application, NULL, log_error, log_debug};
adamc@859: 
adamc@859: int main(int argc, char *argv[]) {
adamc@859:   // The skeleton for this function comes from Beej's sockets tutorial.
adamc@859:   struct sockaddr_in their_addr; // connector's address information
adamc@1134:   socklen_t sin_size;
adamc@859:   int nthreads = 1, i, *names, opt;
adamc@859:   char *fwsa = getenv("FCGI_WEB_SERVER_ADDRS"), *nthreads_s = getenv("URWEB_NUM_THREADS");
adamc@859:  
adamc@859:   if (nthreads_s) {
adamc@859:     nthreads = atoi(nthreads_s);
adamc@859:     if (nthreads <= 0) {
adamc@859:       fprintf(stderr, "Bad URWEB_NUM_THREADS value\n");
adamc@859:       return 1;
adamc@859:     }
adamc@859:   }
adamc@859: 
adamc@859:   signal(SIGINT, sigint);
adamc@859:   signal(SIGPIPE, SIG_IGN);
adamc@859:   signal(SIGUSR1, sigint);
adamc@859:   signal(SIGTERM, sigint);
adamc@859: 
adamc@859:   while ((opt = getopt(argc, argv, "ht:")) != -1) {
adamc@859:     switch (opt) {
adamc@859:     case '?':
adamc@859:       fprintf(stderr, "Unknown command-line option");
adamc@859:       help(argv[0]);
adamc@859:       return 1;
adamc@859: 
adamc@859:     case 'h':
adamc@859:       help(argv[0]);
adamc@859:       return 0;
adamc@859: 
adamc@859:     case 't':
adamc@859:       nthreads = atoi(optarg);
adamc@859:       if (nthreads <= 0) {
adamc@859:         fprintf(stderr, "Invalid thread count\n");
adamc@859:         help(argv[0]);
adamc@859:         return 1;
adamc@859:       }
adamc@859:       break;
adamc@859: 
adamc@859:     default:
adamc@859:       fprintf(stderr, "Unexpected getopt() behavior\n");
adamc@859:       return 1;
adamc@859:     }
adamc@859:   }
adamc@859: 
adamc@860:   uw_set_on_success("");
adamc@1094:   uw_request_init(&uw_application, NULL, log_error, log_debug);
adamc@859: 
adamc@859:   names = calloc(nthreads, sizeof(int));
adamc@859: 
adamc@859:   sin_size = sizeof their_addr;
adamc@859: 
adamc@859:   {
adamc@859:     pthread_t thread;
adamc@859: 
adam@1522:     if (pthread_create_big(&thread, NULL, client_pruner, &ls)) {
adamc@859:       fprintf(stderr, "Error creating pruner thread\n");
adamc@859:       return 1;
adamc@859:     }
adamc@859:   }
adamc@859: 
adamc@859:   for (i = 0; i < nthreads; ++i) {
adamc@859:     pthread_t thread;    
adamc@859:     names[i] = i;
adam@1522:     if (pthread_create_big(&thread, NULL, worker, &names[i])) {
adamc@859:       fprintf(stderr, "Error creating worker thread #%d\n", i);
adamc@859:       return 1;
adamc@859:     }
adamc@859:   }
adamc@859: 
adamc@859:   while (1) {
adamc@859:     int new_fd = accept(FCGI_LISTENSOCK_FILENO, (struct sockaddr *)&their_addr, &sin_size);
adamc@859: 
adamc@859:     if (new_fd < 0) {
adamc@859:       fprintf(stderr, "Socket accept failed\n");
adamc@859:       return 1;
adamc@859:     }
adamc@859: 
adamc@859:     if (fwsa) {
adamc@859:       char host[100], matched = 0;
adamc@859:       char *ips, *sep;
adamc@859: 
adamc@859:       if (getnameinfo((struct sockaddr *)&their_addr, sin_size, host, sizeof host, NULL, 0, NI_NUMERICHOST)) {
adamc@859:         fprintf(stderr, "Remote IP determination failed\n");
adamc@859:         return 1;
adamc@859:       }
adamc@859: 
adamc@1134:       for (ips = fwsa; (sep = strchr(ips, ',')); ips = sep+1) {
adamc@859:         if (!strncmp(ips, host, sep - ips)) {
adamc@859:           matched = 1;
adamc@859:           break;
adamc@859:         }
adamc@859:       }
adamc@859: 
adamc@859:       if (!matched && strcmp(ips, host)) {
adamc@859:         fprintf(stderr, "Remote address is not in FCGI_WEB_SERVER_ADDRS");
adamc@859:         return 1;
adamc@859:       }
adamc@859:     }
adamc@859: 
adamc@859:     uw_enqueue(new_fd);
adamc@859:   }
adamc@859: }
adamc@1121: 
adamc@1121: void *uw_init_client_data() {
adamc@1121:   return NULL;
adamc@1121: }
adamc@1121: 
adamc@1121: void uw_free_client_data(void *data) {
adamc@1121: }
adamc@1121: 
adamc@1121: void uw_copy_client_data(void *dst, void *src) {
adamc@1121: }
adamc@1121: 
adamc@1121: void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) {
adamc@1121:   if (uw_get_app(ctx)->db_begin(ctx))
adamc@1121:     uw_error(ctx, FATAL, "Error running SQL BEGIN");
adamc@1121:   uw_get_app(ctx)->expunger(ctx, cli);
adamc@1121:   if (uw_get_app(ctx)->db_commit(ctx))
adamc@1121:     uw_error(ctx, FATAL, "Error running SQL COMMIT");
adamc@1121: }
adamc@1121: 
adamc@1121: void uw_post_expunge(uw_context ctx, void *data) {
adamc@1121: }
adam@1320: 
adam@1320: int uw_supports_direct_status = 0;