annotate src/c/fastcgi.c @ 1739:c414850f206f

Add support for -boot flag, which allows in-tree execution of Ur/Web The boot flag rewrites most hardcoded paths to point to the build directory, and also forces static compilation. This is convenient for developing Ur/Web, or if you cannot 'sudo make install' Ur/Web. The following changes were made: * Header files were moved to include/urweb instead of include; this lets FFI users point their C_INCLUDE_PATH at this directory at write <urweb/urweb.h>. For internal Ur/Web executables, we simply pass -I$PATH/include/urweb as normal. * Differentiate between LIB and SRCLIB; SRCLIB is Ur and JavaScript source files, while LIB is compiled products from libtool. For in-tree compilation these live in different places. * No longer reference Config for paths; instead use Settings; these settings can be changed dynamically by Compiler.enableBoot () (TODO: add a disableBoot function.) * config.h is now generated directly in include/urweb/config.h, for consistency's sake (especially since it gets installed along with the rest of the headers!) * All of the autotools build products got updated. * The linkStatic field in protocols now only contains the name of the build product, and not the absolute path. Future users have to be careful not to reference the Settings files to early, lest they get an old version (this was the source of two bugs during development of this patch.)
author Edward Z. Yang <ezyang@mit.edu>
date Wed, 02 May 2012 17:17:57 -0400
parents ea131de361d9
children a6eab6820b37
rev   line source
adamc@1268 1 #include "config.h"
adamc@1268 2
adamc@859 3 #include <stdio.h>
adamc@859 4 #include <string.h>
adamc@859 5 #include <stdlib.h>
adamc@859 6 #include <sys/types.h>
adamc@859 7 #include <sys/socket.h>
adamc@859 8 #include <netdb.h>
adamc@859 9 #include <netinet/in.h>
adamc@859 10 #include <unistd.h>
adamc@859 11 #include <signal.h>
adamc@859 12 #include <stdarg.h>
adamc@1094 13 #include <ctype.h>
adamc@859 14
adamc@859 15 #include <pthread.h>
adamc@859 16
adamc@859 17 #include "urweb.h"
adamc@859 18 #include "request.h"
adamc@859 19 #include "queue.h"
adamc@859 20
adamc@859 21 #include "fastcgi.h"
adamc@859 22
adamc@1094 23 extern uw_app uw_application;
adamc@1094 24
adamc@859 25 typedef struct {
adamc@859 26 unsigned char version;
adamc@859 27 unsigned char type;
adamc@859 28 unsigned char requestIdB1;
adamc@859 29 unsigned char requestIdB0;
adamc@859 30 unsigned char contentLengthB1;
adamc@859 31 unsigned char contentLengthB0;
adamc@859 32 unsigned char paddingLength;
adamc@859 33 unsigned char reserved;
adamc@859 34 unsigned char contentData[65535];
adamc@859 35 } FCGI_Record;
adamc@859 36
adamc@859 37 typedef struct {
adamc@859 38 FCGI_Record r;
adamc@859 39 int sock;
adamc@859 40 } FCGI_Output;
adamc@859 41
adamc@859 42 typedef struct {
adamc@859 43 char buf[sizeof(FCGI_Record) + 255];
adamc@859 44 int available, used, sock;
adamc@859 45 } FCGI_Input;
adamc@859 46
adamc@859 47 static FCGI_Output *fastcgi_output() {
adamc@859 48 FCGI_Output *o = malloc(sizeof(FCGI_Output));
adamc@859 49
adamc@859 50 o->r.version = FCGI_VERSION_1;
adamc@859 51 o->r.paddingLength = 0;
adamc@859 52 o->r.reserved = 0;
adamc@859 53
adamc@859 54 return o;
adamc@859 55 }
adamc@859 56
adamc@859 57 static FCGI_Input *fastcgi_input() {
adamc@859 58 FCGI_Input *i = malloc(sizeof(FCGI_Input));
adamc@859 59
adamc@859 60 i->available = i->used = 0;
adamc@859 61
adamc@859 62 return i;
adamc@859 63 }
adamc@859 64
adamc@859 65 static void fastcgi_input_reset(FCGI_Input *i) {
adamc@859 66 i->available = i->used = 0;
adamc@859 67 }
adamc@859 68
adamc@859 69 static int fastcgi_send(FCGI_Output *o,
adamc@859 70 unsigned char type,
adamc@859 71 unsigned short contentLength) {
adamc@859 72 o->r.type = type;
adamc@860 73 o->r.requestIdB1 = o->r.requestIdB0 = 0;
adamc@859 74 o->r.contentLengthB1 = contentLength >> 8;
adamc@859 75 o->r.contentLengthB0 = contentLength & 255;
adamc@861 76 return uw_really_send(o->sock, &o->r, sizeof(o->r) - 65535 + contentLength);
adamc@859 77 }
adamc@859 78
adamc@859 79 #define LATEST(i) ((FCGI_Record *)(i->buf + i->used))
adamc@859 80
adamc@859 81 static FCGI_Record *fastcgi_recv(FCGI_Input *i) {
adamc@859 82 while (1) {
adamc@859 83 ssize_t n;
adamc@859 84
adamc@859 85 if (i->available >= i->used + sizeof(FCGI_Record) - 65535
adamc@859 86 && i->available >= i->used + sizeof(FCGI_Record) - 65535
adamc@860 87 + ((LATEST(i)->contentLengthB1 << 8) | LATEST(i)->contentLengthB0)
adamc@859 88 + LATEST(i)->paddingLength) {
adamc@859 89 FCGI_Record *r = LATEST(i);
adamc@859 90
adamc@859 91 i->used += sizeof(FCGI_Record) - 65535
adamc@860 92 + ((LATEST(i)->contentLengthB1 << 8) | LATEST(i)->contentLengthB0)
adamc@859 93 + LATEST(i)->paddingLength;
adamc@859 94
adamc@859 95 return r;
adamc@859 96 }
adamc@859 97
adamc@859 98 if (i->used > 0) {
adamc@859 99 memmove(i->buf, i->buf + i->used, i->available - i->used);
adamc@859 100 i->available -= i->used;
adamc@859 101 i->used = 0;
adamc@859 102 }
adamc@859 103
adamc@859 104 n = recv(i->sock, i->buf + i->available, sizeof(i->buf) - i->available, 0);
adamc@859 105
adamc@859 106 if (n <= 0)
adamc@859 107 return NULL;
adamc@859 108
adamc@859 109 i->available += n;
adamc@859 110 }
adamc@859 111 }
adamc@859 112
adamc@859 113 static void on_success(uw_context ctx) { }
adamc@859 114
adamc@859 115 static void on_failure(uw_context ctx) {
adamc@859 116 uw_write_header(ctx, "Status: 500 Internal Server Error\r\n");
adamc@859 117 }
adamc@859 118
adamc@863 119 static int write_stdout(void *data, const char *buf, size_t len) {
adamc@860 120 FCGI_Output *o = (FCGI_Output *)data;
adamc@860 121 while (len > 0) {
adamc@860 122 size_t len2 = len;
adamc@860 123 if (len2 > 65535)
adamc@860 124 len2 = 65535;
adamc@860 125 memcpy(o->r.contentData, buf, len2);
adamc@860 126 if (fastcgi_send(o, FCGI_STDOUT, len2)) {
adamc@860 127 fprintf(stderr, "fastcgi_send() failed in write_stdout().\n");
adamc@860 128 return -1;
adamc@860 129 }
adamc@860 130 buf += len2;
adamc@860 131 len -= len2;
adamc@860 132 }
adamc@860 133
adamc@860 134 return 0;
adamc@860 135 }
adamc@860 136
adam@1411 137 #include <errno.h>
adam@1411 138
adamc@859 139 static void write_stderr(FCGI_Output *o, const char *fmt, ...) {
adamc@859 140 int len;
adamc@859 141 va_list ap;
adamc@859 142 va_start(ap, fmt);
adamc@859 143
adamc@1134 144 len = vsnprintf((char *)o->r.contentData, 65535, fmt, ap);
adamc@859 145 if (len < 0)
adamc@860 146 fprintf(stderr, "vsnprintf() failed in write_stderr().\n");
adamc@860 147 else if (fastcgi_send(o, FCGI_STDERR, len))
adamc@860 148 fprintf(stderr, "fastcgi_send() failed in write_stderr().\n");
adamc@860 149 }
adamc@860 150
adamc@860 151 static void close_stream(FCGI_Output *o, unsigned char type) {
adamc@860 152 if (fastcgi_send(o, type, 0))
adamc@860 153 fprintf(stderr, "fastcgi_send() failed in close_stream().\n");
adamc@859 154 }
adamc@859 155
adamc@859 156 static void log_error(void *data, const char *fmt, ...) {
adamc@859 157 FCGI_Output *o = (FCGI_Output *)data;
adamc@859 158 va_list ap;
adamc@859 159 va_start(ap, fmt);
adamc@859 160
adamc@859 161 if (o) {
adamc@1134 162 int len = vsnprintf((char *)o->r.contentData, 65535, fmt, ap);
adamc@859 163 if (len < 0)
adamc@859 164 fprintf(stderr, "vsnprintf() failed in log_error().\n");
adamc@860 165 else if (fastcgi_send(o, FCGI_STDERR, len))
adamc@859 166 fprintf(stderr, "fastcgi_send() failed in log_error().\n");
adamc@859 167 } else
adamc@859 168 vfprintf(stderr, fmt, ap);
adamc@859 169 }
adamc@859 170
adamc@859 171 static void log_debug(void *data, const char *fmt, ...) {
adam@1411 172 FCGI_Output *o = (FCGI_Output *)data;
adam@1411 173 va_list ap;
adam@1411 174 va_start(ap, fmt);
adam@1411 175
adam@1411 176 if (o) {
adam@1411 177 strcpy((char *)o->r.contentData, "DEBUG: ");
adam@1411 178 int len = vsnprintf((char *)o->r.contentData + 7, 65535 - 7, fmt, ap);
adam@1411 179 if (len < 0)
adam@1411 180 fprintf(stderr, "vsnprintf() failed in log_debug().\n");
adam@1411 181 else if (fastcgi_send(o, FCGI_STDERR, len + 7)) {
adam@1411 182 len += 7;
adam@1411 183 if (len >= 65535) len = 65534;
adam@1411 184 o->r.contentData[len] = 0;
adam@1411 185 fputs((char *)o->r.contentData, stderr);
adam@1411 186 fflush(stderr);
adam@1411 187 }
adam@1411 188 } else
adam@1411 189 vfprintf(stderr, fmt, ap);
adamc@859 190 }
adamc@859 191
adamc@860 192 typedef struct {
adamc@860 193 char *name, *value;
adamc@860 194 unsigned name_len, value_len;
adamc@860 195 } nvp;
adamc@860 196
adamc@860 197 static char *search_nvps(nvp *nvps, const char *h) {
adamc@862 198 for (; nvps->name[0]; ++nvps)
adamc@860 199 if (!strcmp(h, nvps->name))
adamc@860 200 return nvps->value;
adamc@860 201
adamc@860 202 return NULL;
adamc@860 203 }
adamc@860 204
adamc@860 205 typedef struct {
adamc@860 206 nvp *nvps;
adamc@860 207 char *uppercased;
adamc@860 208 int n_nvps, uppercased_len;
adamc@860 209 } headers;
adamc@860 210
adamc@860 211 static char *get_header(void *data, const char *h) {
adamc@860 212 headers *hs = (headers *)data;
adamc@860 213 size_t len = strlen(h);
adamc@1134 214 char *s;
adamc@860 215 const char *saved_h = h;
adamc@860 216
adamc@860 217 if (len > hs->uppercased_len) {
adamc@860 218 hs->uppercased_len = len;
adamc@860 219 hs->uppercased = realloc(hs->uppercased, len + 6);
adamc@860 220 }
adamc@860 221
adamc@860 222 strcpy(hs->uppercased, "HTTP_");
adamc@860 223 for (s = hs->uppercased+5; *h; ++h)
adam@1435 224 *s++ = *h == '-' ? '_' : toupper((int)*h);
adamc@860 225 *s = 0;
adamc@860 226
adamc@860 227 if (!strcasecmp(saved_h, "Content-length")
adamc@864 228 || !strcasecmp(saved_h, "Content-type")) {
adamc@1134 229 if ((s = search_nvps(hs->nvps, hs->uppercased + 5)))
adamc@864 230 return s;
adamc@864 231 }
adamc@864 232
adamc@864 233 return search_nvps(hs->nvps, hs->uppercased);
adamc@860 234 }
adamc@860 235
adamc@864 236 static int read_funny_len(unsigned char **buf, int *len) {
adamc@861 237 if (*len <= 0)
adamc@861 238 return -1;
adamc@861 239
adamc@861 240 if ((*buf)[0] >> 7 == 0) {
adamc@861 241 int r = (*buf)[0];
adamc@861 242 ++*buf;
adamc@861 243 --*len;
adamc@861 244 return r;
adamc@861 245 }
adamc@861 246 else if (*len < 4)
adamc@861 247 return -1;
adamc@861 248 else {
adamc@1049 249 int r = (((*buf)[0] & 0x7f) << 24) + ((*buf)[1] << 16) + ((*buf)[2] << 8) + (*buf)[3];
adamc@861 250 *buf += 4;
adamc@861 251 *len -= 4;
adamc@861 252 return r;
adamc@861 253 }
adamc@861 254 }
adamc@861 255
adamc@864 256 static int read_nvp(unsigned char **buf, int len, nvp *nv) {
adamc@860 257 int nameLength, valueLength;
adamc@860 258
adamc@864 259 if ((nameLength = read_funny_len(buf, &len)) < 0)
adamc@860 260 return -1;
adamc@864 261 if ((valueLength = read_funny_len(buf, &len)) < 0)
adamc@1049 262 return -2;
adamc@860 263 if (len < nameLength + valueLength)
adamc@1049 264 return -3;
adamc@860 265
adamc@862 266 if (nameLength+1 > nv->name_len) {
adamc@860 267 nv->name_len = nameLength+1;
adamc@862 268 nv->name = realloc(nv->name, nv->name_len);
adamc@860 269 }
adamc@862 270 if (valueLength+1 > nv->value_len) {
adamc@860 271 nv->value_len = valueLength+1;
adamc@862 272 nv->value = realloc(nv->value, nv->value_len);
adamc@860 273 }
adamc@860 274
adamc@864 275 memcpy(nv->name, *buf, nameLength);
adamc@860 276 nv->name[nameLength] = 0;
adamc@860 277
adamc@864 278 memcpy(nv->value, *buf + nameLength, valueLength);
adamc@860 279 nv->value[valueLength] = 0;
adamc@860 280
adamc@864 281 *buf += nameLength + valueLength;
adamc@864 282
adamc@860 283 return 0;
adamc@860 284 }
adamc@860 285
adamc@863 286 static int fastcgi_close_with(FCGI_Output *out, request_result rr) {
adamc@863 287 FCGI_EndRequestBody *erb = (FCGI_EndRequestBody *)out->r.contentData;
adamc@863 288
adamc@863 289 close_stream(out, FCGI_STDOUT);
adamc@863 290 close_stream(out, FCGI_STDERR);
adamc@863 291
adamc@863 292 if (rr == SERVED)
adamc@863 293 erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0;
adamc@863 294 else
adamc@863 295 erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0xFF;
adamc@863 296
adamc@863 297 erb->protocolStatus = FCGI_REQUEST_COMPLETE;
adamc@863 298 fastcgi_send(out, FCGI_END_REQUEST, sizeof(FCGI_EndRequestBody));
adamc@863 299 return close(out->sock);
adamc@863 300 }
adamc@863 301
adamc@863 302 static int fastcgi_close(int sock) {
adamc@863 303 FCGI_Output out;
adamc@863 304 out.sock = sock;
adamc@863 305 out.r.version = FCGI_VERSION_1;
adamc@863 306 out.r.paddingLength = 0;
adamc@863 307 out.r.reserved = 0;
adamc@863 308
adamc@863 309 return fastcgi_close_with(&out, SERVED);
adamc@863 310 }
adamc@863 311
adamc@863 312 int fastcgi_send_normal(int sock, const void *buf, ssize_t len) {
adamc@863 313 FCGI_Output out;
adamc@863 314 out.sock = sock;
adamc@863 315 out.r.version = FCGI_VERSION_1;
adamc@863 316 out.r.paddingLength = 0;
adamc@863 317 out.r.reserved = 0;
adamc@863 318
adamc@863 319 return write_stdout(&out, buf, len);
adamc@863 320 }
adamc@863 321
adamc@859 322 static void *worker(void *data) {
adamc@859 323 FCGI_Input *in = fastcgi_input();
adamc@859 324 FCGI_Output *out = fastcgi_output();
adam@1446 325 uw_context ctx = uw_request_new_context(*(int *)data, &uw_application, out, log_error, log_debug);
adamc@859 326 uw_request_context rc = uw_new_request_context();
adamc@860 327 headers hs;
adamc@860 328 size_t body_size = 0;
adamc@860 329 char *body = malloc(0);
adamc@860 330 size_t path_size = 0;
adamc@860 331 char *path_buf = malloc(0);
adamc@860 332
adamc@860 333 hs.uppercased = malloc(0);
adamc@860 334 hs.uppercased_len = 0;
adamc@860 335 hs.nvps = malloc(sizeof(nvp));
adamc@860 336 hs.n_nvps = 1;
adamc@862 337 hs.nvps[0].name = malloc(1);
adamc@862 338 hs.nvps[0].name_len = 1;
adamc@862 339 hs.nvps[0].value = malloc(0);
adamc@862 340 hs.nvps[0].value_len = 0;
adamc@859 341
adamc@859 342 while (1) {
adamc@859 343 FCGI_Record *r;
adamc@860 344 size_t used_nvps = 0;
adamc@860 345 int body_len, body_read;
adamc@860 346 char *s;
adamc@860 347 char *method, *path, *path_info, *query_string;
adamc@859 348
adamc@859 349 in->sock = out->sock = uw_dequeue();
adamc@859 350
adamc@859 351 if (!(r = fastcgi_recv(in))) {
adamc@859 352 fprintf(stderr, "Error receiving initial message\n");
adamc@860 353 goto done;
adamc@859 354 }
adamc@861 355
adamc@859 356 if (r->type != FCGI_BEGIN_REQUEST) {
adamc@859 357 write_stderr(out, "First message is not BEGIN_REQUEST\n");
adamc@859 358 goto done;
adamc@1139 359 } else if (r->contentData[1] != FCGI_RESPONDER) {
adamc@1139 360 write_stderr(out, "Request is for a role besides RESPONDER\n");
adamc@859 361 goto done;
adamc@859 362 }
adamc@859 363
adamc@860 364 while (1) {
adamc@864 365 unsigned char *buf;
adamc@864 366 int len;
adamc@860 367
adamc@860 368 if (!(r = fastcgi_recv(in))) {
adamc@860 369 write_stderr(out, "Error receiving environment variables\n");
adamc@860 370 goto done;
adamc@860 371 }
adamc@860 372
adamc@860 373 if (r->type != FCGI_PARAMS) {
adamc@860 374 write_stderr(out, "Expected FCGI_PARAMS but got %d\n", r->type);
adamc@860 375 goto done;
adamc@860 376 }
adamc@860 377
adamc@860 378 if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0)
adamc@860 379 break;
adamc@860 380
adamc@864 381 len = (r->contentLengthB1 << 8) | r->contentLengthB0;
adamc@864 382
adamc@864 383 for (buf = r->contentData; buf < r->contentData + len; ) {
adamc@864 384 if (used_nvps == hs.n_nvps-1) {
adamc@864 385 ++hs.n_nvps;
adamc@864 386 hs.nvps = realloc(hs.nvps, hs.n_nvps * sizeof(nvp));
adamc@864 387 hs.nvps[hs.n_nvps-1].name = malloc(1);
adamc@864 388 hs.nvps[hs.n_nvps-1].value = malloc(0);
adamc@864 389 hs.nvps[hs.n_nvps-1].name_len = 1;
adamc@864 390 hs.nvps[hs.n_nvps-1].value_len = 0;
adamc@864 391 }
adamc@864 392
adamc@864 393 if (read_nvp(&buf, len - (buf - r->contentData), &hs.nvps[used_nvps]) < 0) {
adamc@864 394 write_stderr(out, "Error reading FCGI_PARAMS name-value pair\n");
adamc@864 395 goto done;
adamc@864 396 }
adamc@864 397
adamc@1049 398 //write_stderr(out, "PARAM: %s -> %s\n", hs.nvps[used_nvps].name, hs.nvps[used_nvps].value);
adamc@864 399
adamc@864 400 ++used_nvps;
adamc@860 401 }
adamc@859 402 }
adamc@860 403
adamc@862 404 hs.nvps[used_nvps].name[0] = 0;
adamc@860 405
adamc@1134 406 if ((s = get_header(&hs, "Content-Length"))) {
adamc@860 407 body_len = atoi(s);
adamc@860 408 if (body_len < 0) {
adamc@860 409 write_stderr(out, "Invalid Content-Length\n");
adamc@860 410 goto done;
adamc@860 411 }
adamc@860 412 } else
adamc@860 413 body_len = 0;
adamc@860 414
adamc@861 415 if (body_len+1 > body_size) {
adamc@861 416 body_size = body_len+1;
adamc@860 417 body = realloc(body, body_size);
adamc@860 418 }
adamc@860 419
adamc@860 420 for (body_read = 0; body_read < body_len; ) {
adamc@860 421 int this_len;
adamc@860 422
adamc@860 423 if (!(r = fastcgi_recv(in))) {
adamc@860 424 write_stderr(out, "Error receiving STDIN\n");
adamc@860 425 goto done;
adamc@860 426 }
adamc@860 427
adamc@860 428 if (r->type != FCGI_STDIN) {
adamc@860 429 write_stderr(out, "Expected FCGI_STDIN but got %d\n", r->type);
adamc@860 430 goto done;
adamc@860 431 }
adamc@860 432
adamc@860 433 if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0) {
adamc@860 434 write_stderr(out, "End of STDIN\n");
adamc@860 435 break;
adamc@860 436 }
adamc@860 437
adamc@860 438 this_len = (r->contentLengthB1 << 8) | r->contentLengthB0;
adamc@860 439
adamc@860 440 if (body_read + this_len > body_len) {
adamc@860 441 write_stderr(out, "Too much STDIN\n");
adamc@860 442 goto done;
adamc@860 443 }
adamc@860 444
adamc@860 445 memcpy(&body[body_read], r->contentData, this_len);
adamc@860 446 body_read += this_len;
adamc@860 447 }
adamc@860 448
adamc@861 449 body[body_read] = 0;
adamc@861 450
adamc@860 451 if (!(method = search_nvps(hs.nvps, "REQUEST_METHOD"))) {
adamc@860 452 write_stderr(out, "REQUEST_METHOD not set\n");
adamc@860 453 goto done;
adamc@860 454 }
adamc@860 455
adamc@860 456 if (!(path = search_nvps(hs.nvps, "SCRIPT_NAME"))) {
adamc@860 457 write_stderr(out, "SCRIPT_NAME not set\n");
adamc@860 458 goto done;
adamc@860 459 }
adamc@861 460
adamc@1134 461 if ((path_info = search_nvps(hs.nvps, "PATH_INFO"))) {
adamc@860 462 int len1 = strlen(path), len2 = strlen(path_info);
adamc@860 463 int len = len1 + len2 + 1;
adamc@860 464
adamc@860 465 if (len > path_size) {
adamc@860 466 path_size = len;
adamc@860 467 path_buf = realloc(path_buf, path_size);
adamc@860 468 }
adamc@860 469
adamc@860 470 sprintf(path_buf, "%s%s", path, path_info);
adamc@860 471 path = path_buf;
adamc@860 472 }
adamc@860 473
adamc@860 474 if (!(query_string = search_nvps(hs.nvps, "QUERY_STRING")))
adamc@860 475 query_string = "";
adamc@860 476
adamc@860 477 uw_set_headers(ctx, get_header, &hs);
adamc@861 478
adamc@860 479 {
adamc@860 480 request_result rr;
adamc@860 481
adamc@860 482 rr = uw_request(rc, ctx, method, path, query_string, body, body_read,
adamc@860 483 on_success, on_failure,
adamc@860 484 out, log_error, log_debug,
adamc@863 485 in->sock, fastcgi_send_normal, fastcgi_close);
adamc@860 486
adamc@860 487 if (rr == KEEP_OPEN)
adamc@860 488 goto done2;
adamc@860 489
adamc@860 490 uw_output(ctx, write_stdout, out);
adamc@863 491 fastcgi_close_with(out, rr);
adamc@863 492 goto done2;
adamc@860 493 }
adamc@859 494
adamc@859 495 done:
adamc@859 496 close(in->sock);
adamc@860 497 done2:
adamc@859 498 fastcgi_input_reset(in);
adamc@859 499 uw_reset(ctx);
adamc@859 500 }
adamc@1138 501
adamc@1138 502 return NULL;
adamc@859 503 }
adamc@859 504
adamc@859 505 static void help(char *cmd) {
adamc@859 506 printf("Usage: %s [-t <thread-count>]\n", cmd);
adamc@859 507 }
adamc@859 508
adamc@859 509 static void sigint(int signum) {
adamc@859 510 printf("Exiting....\n");
adamc@859 511 exit(0);
adamc@859 512 }
adamc@859 513
adamc@1094 514 static loggers ls = {&uw_application, NULL, log_error, log_debug};
adamc@859 515
adamc@859 516 int main(int argc, char *argv[]) {
adamc@859 517 // The skeleton for this function comes from Beej's sockets tutorial.
adamc@859 518 struct sockaddr_in their_addr; // connector's address information
adamc@1134 519 socklen_t sin_size;
adamc@859 520 int nthreads = 1, i, *names, opt;
adamc@859 521 char *fwsa = getenv("FCGI_WEB_SERVER_ADDRS"), *nthreads_s = getenv("URWEB_NUM_THREADS");
adamc@859 522
adamc@859 523 if (nthreads_s) {
adamc@859 524 nthreads = atoi(nthreads_s);
adamc@859 525 if (nthreads <= 0) {
adamc@859 526 fprintf(stderr, "Bad URWEB_NUM_THREADS value\n");
adamc@859 527 return 1;
adamc@859 528 }
adamc@859 529 }
adamc@859 530
adamc@859 531 signal(SIGINT, sigint);
adamc@859 532 signal(SIGPIPE, SIG_IGN);
adamc@859 533 signal(SIGUSR1, sigint);
adamc@859 534 signal(SIGTERM, sigint);
adamc@859 535
adamc@859 536 while ((opt = getopt(argc, argv, "ht:")) != -1) {
adamc@859 537 switch (opt) {
adamc@859 538 case '?':
adamc@859 539 fprintf(stderr, "Unknown command-line option");
adamc@859 540 help(argv[0]);
adamc@859 541 return 1;
adamc@859 542
adamc@859 543 case 'h':
adamc@859 544 help(argv[0]);
adamc@859 545 return 0;
adamc@859 546
adamc@859 547 case 't':
adamc@859 548 nthreads = atoi(optarg);
adamc@859 549 if (nthreads <= 0) {
adamc@859 550 fprintf(stderr, "Invalid thread count\n");
adamc@859 551 help(argv[0]);
adamc@859 552 return 1;
adamc@859 553 }
adamc@859 554 break;
adamc@859 555
adamc@859 556 default:
adamc@859 557 fprintf(stderr, "Unexpected getopt() behavior\n");
adamc@859 558 return 1;
adamc@859 559 }
adamc@859 560 }
adamc@859 561
adamc@860 562 uw_set_on_success("");
adamc@1094 563 uw_request_init(&uw_application, NULL, log_error, log_debug);
adamc@859 564
adamc@859 565 names = calloc(nthreads, sizeof(int));
adamc@859 566
adamc@859 567 sin_size = sizeof their_addr;
adamc@859 568
adamc@859 569 {
adamc@859 570 pthread_t thread;
adamc@859 571
adam@1522 572 if (pthread_create_big(&thread, NULL, client_pruner, &ls)) {
adamc@859 573 fprintf(stderr, "Error creating pruner thread\n");
adamc@859 574 return 1;
adamc@859 575 }
adamc@859 576 }
adamc@859 577
adamc@859 578 for (i = 0; i < nthreads; ++i) {
adamc@859 579 pthread_t thread;
adamc@859 580 names[i] = i;
adam@1522 581 if (pthread_create_big(&thread, NULL, worker, &names[i])) {
adamc@859 582 fprintf(stderr, "Error creating worker thread #%d\n", i);
adamc@859 583 return 1;
adamc@859 584 }
adamc@859 585 }
adamc@859 586
adamc@859 587 while (1) {
adamc@859 588 int new_fd = accept(FCGI_LISTENSOCK_FILENO, (struct sockaddr *)&their_addr, &sin_size);
adamc@859 589
adamc@859 590 if (new_fd < 0) {
adamc@859 591 fprintf(stderr, "Socket accept failed\n");
adamc@859 592 return 1;
adamc@859 593 }
adamc@859 594
adamc@859 595 if (fwsa) {
adamc@859 596 char host[100], matched = 0;
adamc@859 597 char *ips, *sep;
adamc@859 598
adamc@859 599 if (getnameinfo((struct sockaddr *)&their_addr, sin_size, host, sizeof host, NULL, 0, NI_NUMERICHOST)) {
adamc@859 600 fprintf(stderr, "Remote IP determination failed\n");
adamc@859 601 return 1;
adamc@859 602 }
adamc@859 603
adamc@1134 604 for (ips = fwsa; (sep = strchr(ips, ',')); ips = sep+1) {
adamc@859 605 if (!strncmp(ips, host, sep - ips)) {
adamc@859 606 matched = 1;
adamc@859 607 break;
adamc@859 608 }
adamc@859 609 }
adamc@859 610
adamc@859 611 if (!matched && strcmp(ips, host)) {
adamc@859 612 fprintf(stderr, "Remote address is not in FCGI_WEB_SERVER_ADDRS");
adamc@859 613 return 1;
adamc@859 614 }
adamc@859 615 }
adamc@859 616
adamc@859 617 uw_enqueue(new_fd);
adamc@859 618 }
adamc@859 619 }
adamc@1121 620
adamc@1121 621 void *uw_init_client_data() {
adamc@1121 622 return NULL;
adamc@1121 623 }
adamc@1121 624
adamc@1121 625 void uw_free_client_data(void *data) {
adamc@1121 626 }
adamc@1121 627
adamc@1121 628 void uw_copy_client_data(void *dst, void *src) {
adamc@1121 629 }
adamc@1121 630
adamc@1121 631 void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) {
adamc@1121 632 if (uw_get_app(ctx)->db_begin(ctx))
adamc@1121 633 uw_error(ctx, FATAL, "Error running SQL BEGIN");
adamc@1121 634 uw_get_app(ctx)->expunger(ctx, cli);
adam@1672 635 uw_commit(ctx);
adamc@1121 636 }
adamc@1121 637
adamc@1121 638 void uw_post_expunge(uw_context ctx, void *data) {
adamc@1121 639 }
adam@1320 640
adam@1320 641 int uw_supports_direct_status = 0;