annotate src/c/fastcgi.c @ 2165:7dfce558aca2

Change 'make test' to work before 'make install'
author Adam Chlipala <adam@chlipala.net>
date Thu, 06 Aug 2015 09:57:47 -0400
parents 18ef1db770f6
children
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 {
adam@1762 43 FCGI_Record r;
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
adam@1762 79 static FCGI_Record *fastcgi_recv(FCGI_Input *i) {
adam@1762 80 if (i->used > 0) {
adam@1762 81 memmove((void*)&i->r, (void*)&i->r + i->used, i->available - i->used);
adam@1762 82 i->available -= i->used;
adam@1762 83 i->used = 0;
adam@1762 84 }
adamc@859 85
adamc@859 86 while (1) {
adamc@859 87 ssize_t n;
adamc@859 88
adam@1762 89 if (i->available >= sizeof(FCGI_Record) - 65535
adam@1762 90 && i->available >= sizeof(FCGI_Record) - 65535
adam@1762 91 + ((i->r.contentLengthB1 << 8) | i->r.contentLengthB0)
adam@1762 92 + i->r.paddingLength) {
adam@1762 93 i->used = sizeof(FCGI_Record) - 65535
adam@1762 94 + ((i->r.contentLengthB1 << 8) | i->r.contentLengthB0)
adam@1762 95 + i->r.paddingLength;
adamc@859 96
adam@1762 97 return &i->r;
adamc@859 98 }
adamc@859 99
adam@1762 100 n = recv(i->sock, (void*)&i->r + i->available, sizeof(i->r) - i->available, 0);
adamc@859 101
adamc@859 102 if (n <= 0)
adamc@859 103 return NULL;
adamc@859 104
adamc@859 105 i->available += n;
adamc@859 106 }
adamc@859 107 }
adamc@859 108
adamc@859 109 static void on_success(uw_context ctx) { }
adamc@859 110
adamc@859 111 static void on_failure(uw_context ctx) {
adamc@859 112 uw_write_header(ctx, "Status: 500 Internal Server Error\r\n");
adamc@859 113 }
adamc@859 114
adamc@863 115 static int write_stdout(void *data, const char *buf, size_t len) {
adamc@860 116 FCGI_Output *o = (FCGI_Output *)data;
adamc@860 117 while (len > 0) {
adamc@860 118 size_t len2 = len;
adamc@860 119 if (len2 > 65535)
adamc@860 120 len2 = 65535;
adamc@860 121 memcpy(o->r.contentData, buf, len2);
adamc@860 122 if (fastcgi_send(o, FCGI_STDOUT, len2)) {
adamc@860 123 fprintf(stderr, "fastcgi_send() failed in write_stdout().\n");
adamc@860 124 return -1;
adamc@860 125 }
adamc@860 126 buf += len2;
adamc@860 127 len -= len2;
adamc@860 128 }
adamc@860 129
adamc@860 130 return 0;
adamc@860 131 }
adamc@860 132
adam@1411 133 #include <errno.h>
adam@1411 134
adamc@859 135 static void write_stderr(FCGI_Output *o, const char *fmt, ...) {
adamc@859 136 int len;
adamc@859 137 va_list ap;
adamc@859 138 va_start(ap, fmt);
adamc@859 139
adamc@1134 140 len = vsnprintf((char *)o->r.contentData, 65535, fmt, ap);
adamc@859 141 if (len < 0)
adamc@860 142 fprintf(stderr, "vsnprintf() failed in write_stderr().\n");
adamc@860 143 else if (fastcgi_send(o, FCGI_STDERR, len))
adamc@860 144 fprintf(stderr, "fastcgi_send() failed in write_stderr().\n");
adamc@860 145 }
adamc@860 146
adamc@860 147 static void close_stream(FCGI_Output *o, unsigned char type) {
adamc@860 148 if (fastcgi_send(o, type, 0))
adamc@860 149 fprintf(stderr, "fastcgi_send() failed in close_stream().\n");
adamc@859 150 }
adamc@859 151
adamc@859 152 static void log_error(void *data, const char *fmt, ...) {
adamc@859 153 FCGI_Output *o = (FCGI_Output *)data;
adamc@859 154 va_list ap;
adamc@859 155 va_start(ap, fmt);
adamc@859 156
adamc@859 157 if (o) {
adamc@1134 158 int len = vsnprintf((char *)o->r.contentData, 65535, fmt, ap);
adamc@859 159 if (len < 0)
adamc@859 160 fprintf(stderr, "vsnprintf() failed in log_error().\n");
adamc@860 161 else if (fastcgi_send(o, FCGI_STDERR, len))
adamc@859 162 fprintf(stderr, "fastcgi_send() failed in log_error().\n");
adamc@859 163 } else
adamc@859 164 vfprintf(stderr, fmt, ap);
adamc@859 165 }
adamc@859 166
adamc@859 167 static void log_debug(void *data, const char *fmt, ...) {
adam@1411 168 FCGI_Output *o = (FCGI_Output *)data;
adam@1411 169 va_list ap;
adam@1411 170 va_start(ap, fmt);
adam@1411 171
adam@1411 172 if (o) {
adam@1411 173 strcpy((char *)o->r.contentData, "DEBUG: ");
adam@1411 174 int len = vsnprintf((char *)o->r.contentData + 7, 65535 - 7, fmt, ap);
adam@1411 175 if (len < 0)
adam@1411 176 fprintf(stderr, "vsnprintf() failed in log_debug().\n");
adam@1411 177 else if (fastcgi_send(o, FCGI_STDERR, len + 7)) {
adam@1411 178 len += 7;
adam@1411 179 if (len >= 65535) len = 65534;
adam@1411 180 o->r.contentData[len] = 0;
adam@1411 181 fputs((char *)o->r.contentData, stderr);
adam@1411 182 fflush(stderr);
adam@1411 183 }
adam@1411 184 } else
adam@1411 185 vfprintf(stderr, fmt, ap);
adamc@859 186 }
adamc@859 187
adamc@860 188 typedef struct {
adamc@860 189 char *name, *value;
adamc@860 190 unsigned name_len, value_len;
adamc@860 191 } nvp;
adamc@860 192
adamc@860 193 static char *search_nvps(nvp *nvps, const char *h) {
adamc@862 194 for (; nvps->name[0]; ++nvps)
adamc@860 195 if (!strcmp(h, nvps->name))
adamc@860 196 return nvps->value;
adamc@860 197
adamc@860 198 return NULL;
adamc@860 199 }
adamc@860 200
adamc@860 201 typedef struct {
adamc@860 202 nvp *nvps;
adamc@860 203 char *uppercased;
adamc@860 204 int n_nvps, uppercased_len;
adamc@860 205 } headers;
adamc@860 206
adamc@860 207 static char *get_header(void *data, const char *h) {
adamc@860 208 headers *hs = (headers *)data;
adamc@860 209 size_t len = strlen(h);
adamc@1134 210 char *s;
adamc@860 211 const char *saved_h = h;
adamc@860 212
adamc@860 213 if (len > hs->uppercased_len) {
adamc@860 214 hs->uppercased_len = len;
adamc@860 215 hs->uppercased = realloc(hs->uppercased, len + 6);
adamc@860 216 }
adamc@860 217
adamc@860 218 strcpy(hs->uppercased, "HTTP_");
adamc@860 219 for (s = hs->uppercased+5; *h; ++h)
adam@1435 220 *s++ = *h == '-' ? '_' : toupper((int)*h);
adamc@860 221 *s = 0;
adamc@860 222
adamc@860 223 if (!strcasecmp(saved_h, "Content-length")
adamc@864 224 || !strcasecmp(saved_h, "Content-type")) {
adamc@1134 225 if ((s = search_nvps(hs->nvps, hs->uppercased + 5)))
adamc@864 226 return s;
adamc@864 227 }
adamc@864 228
adamc@864 229 return search_nvps(hs->nvps, hs->uppercased);
adamc@860 230 }
adamc@860 231
adam@1799 232 static char *get_env(void *data, const char *h) {
adam@1799 233 headers *hs = (headers *)data;
adam@1799 234
adam@1799 235 return search_nvps(hs->nvps, h);
adam@1799 236 }
adam@1799 237
adamc@864 238 static int read_funny_len(unsigned char **buf, int *len) {
adamc@861 239 if (*len <= 0)
adamc@861 240 return -1;
adamc@861 241
adamc@861 242 if ((*buf)[0] >> 7 == 0) {
adamc@861 243 int r = (*buf)[0];
adamc@861 244 ++*buf;
adamc@861 245 --*len;
adamc@861 246 return r;
adamc@861 247 }
adamc@861 248 else if (*len < 4)
adamc@861 249 return -1;
adamc@861 250 else {
adamc@1049 251 int r = (((*buf)[0] & 0x7f) << 24) + ((*buf)[1] << 16) + ((*buf)[2] << 8) + (*buf)[3];
adamc@861 252 *buf += 4;
adamc@861 253 *len -= 4;
adamc@861 254 return r;
adamc@861 255 }
adamc@861 256 }
adamc@861 257
adamc@864 258 static int read_nvp(unsigned char **buf, int len, nvp *nv) {
adamc@860 259 int nameLength, valueLength;
adamc@860 260
adamc@864 261 if ((nameLength = read_funny_len(buf, &len)) < 0)
adamc@860 262 return -1;
adamc@864 263 if ((valueLength = read_funny_len(buf, &len)) < 0)
adamc@1049 264 return -2;
adamc@860 265 if (len < nameLength + valueLength)
adamc@1049 266 return -3;
adamc@860 267
adamc@862 268 if (nameLength+1 > nv->name_len) {
adamc@860 269 nv->name_len = nameLength+1;
adamc@862 270 nv->name = realloc(nv->name, nv->name_len);
adamc@860 271 }
adamc@862 272 if (valueLength+1 > nv->value_len) {
adamc@860 273 nv->value_len = valueLength+1;
adamc@862 274 nv->value = realloc(nv->value, nv->value_len);
adamc@860 275 }
adamc@860 276
adamc@864 277 memcpy(nv->name, *buf, nameLength);
adamc@860 278 nv->name[nameLength] = 0;
adamc@860 279
adamc@864 280 memcpy(nv->value, *buf + nameLength, valueLength);
adamc@860 281 nv->value[valueLength] = 0;
adamc@860 282
adamc@864 283 *buf += nameLength + valueLength;
adamc@864 284
adamc@860 285 return 0;
adamc@860 286 }
adamc@860 287
adamc@863 288 static int fastcgi_close_with(FCGI_Output *out, request_result rr) {
adamc@863 289 FCGI_EndRequestBody *erb = (FCGI_EndRequestBody *)out->r.contentData;
adamc@863 290
adamc@863 291 close_stream(out, FCGI_STDOUT);
adamc@863 292 close_stream(out, FCGI_STDERR);
adamc@863 293
adamc@863 294 if (rr == SERVED)
adamc@863 295 erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0;
adamc@863 296 else
adamc@863 297 erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0xFF;
adamc@863 298
adamc@863 299 erb->protocolStatus = FCGI_REQUEST_COMPLETE;
adamc@863 300 fastcgi_send(out, FCGI_END_REQUEST, sizeof(FCGI_EndRequestBody));
adamc@863 301 return close(out->sock);
adamc@863 302 }
adamc@863 303
adamc@863 304 static int fastcgi_close(int sock) {
adamc@863 305 FCGI_Output out;
adamc@863 306 out.sock = sock;
adamc@863 307 out.r.version = FCGI_VERSION_1;
adamc@863 308 out.r.paddingLength = 0;
adamc@863 309 out.r.reserved = 0;
adamc@863 310
adamc@863 311 return fastcgi_close_with(&out, SERVED);
adamc@863 312 }
adamc@863 313
adamc@863 314 int fastcgi_send_normal(int sock, const void *buf, ssize_t len) {
adamc@863 315 FCGI_Output out;
adamc@863 316 out.sock = sock;
adamc@863 317 out.r.version = FCGI_VERSION_1;
adamc@863 318 out.r.paddingLength = 0;
adamc@863 319 out.r.reserved = 0;
adamc@863 320
adamc@863 321 return write_stdout(&out, buf, len);
adamc@863 322 }
adamc@863 323
adamc@859 324 static void *worker(void *data) {
adamc@859 325 FCGI_Input *in = fastcgi_input();
adamc@859 326 FCGI_Output *out = fastcgi_output();
grrwlf@1997 327 uw_loggers ls = {out, log_error, log_debug};
grrwlf@1997 328 uw_context ctx = uw_request_new_context(*(int *)data, &uw_application, &ls);
adamc@859 329 uw_request_context rc = uw_new_request_context();
adamc@860 330 headers hs;
adamc@860 331 size_t body_size = 0;
adamc@860 332 char *body = malloc(0);
adamc@860 333 size_t path_size = 0;
adamc@860 334 char *path_buf = malloc(0);
adamc@860 335
adam@2106 336 hs.uppercased = malloc(6);
adamc@860 337 hs.uppercased_len = 0;
adamc@860 338 hs.nvps = malloc(sizeof(nvp));
adamc@860 339 hs.n_nvps = 1;
adamc@862 340 hs.nvps[0].name = malloc(1);
adamc@862 341 hs.nvps[0].name_len = 1;
adamc@862 342 hs.nvps[0].value = malloc(0);
adamc@862 343 hs.nvps[0].value_len = 0;
adamc@859 344
adamc@859 345 while (1) {
adamc@859 346 FCGI_Record *r;
adamc@860 347 size_t used_nvps = 0;
adamc@860 348 int body_len, body_read;
adamc@860 349 char *s;
adamc@860 350 char *method, *path, *path_info, *query_string;
adamc@859 351
adamc@859 352 in->sock = out->sock = uw_dequeue();
adamc@859 353
adamc@859 354 if (!(r = fastcgi_recv(in))) {
adamc@859 355 fprintf(stderr, "Error receiving initial message\n");
adamc@860 356 goto done;
adamc@859 357 }
adamc@861 358
adamc@859 359 if (r->type != FCGI_BEGIN_REQUEST) {
adamc@859 360 write_stderr(out, "First message is not BEGIN_REQUEST\n");
adamc@859 361 goto done;
adamc@1139 362 } else if (r->contentData[1] != FCGI_RESPONDER) {
adamc@1139 363 write_stderr(out, "Request is for a role besides RESPONDER\n");
adamc@859 364 goto done;
adamc@859 365 }
adamc@859 366
adamc@860 367 while (1) {
adamc@864 368 unsigned char *buf;
adamc@864 369 int len;
adamc@860 370
adamc@860 371 if (!(r = fastcgi_recv(in))) {
adamc@860 372 write_stderr(out, "Error receiving environment variables\n");
adamc@860 373 goto done;
adamc@860 374 }
adamc@860 375
adamc@860 376 if (r->type != FCGI_PARAMS) {
adamc@860 377 write_stderr(out, "Expected FCGI_PARAMS but got %d\n", r->type);
adamc@860 378 goto done;
adamc@860 379 }
adamc@860 380
adamc@860 381 if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0)
adamc@860 382 break;
adamc@860 383
adamc@864 384 len = (r->contentLengthB1 << 8) | r->contentLengthB0;
adamc@864 385
adamc@864 386 for (buf = r->contentData; buf < r->contentData + len; ) {
adamc@864 387 if (used_nvps == hs.n_nvps-1) {
adamc@864 388 ++hs.n_nvps;
adamc@864 389 hs.nvps = realloc(hs.nvps, hs.n_nvps * sizeof(nvp));
adamc@864 390 hs.nvps[hs.n_nvps-1].name = malloc(1);
adamc@864 391 hs.nvps[hs.n_nvps-1].value = malloc(0);
adamc@864 392 hs.nvps[hs.n_nvps-1].name_len = 1;
adamc@864 393 hs.nvps[hs.n_nvps-1].value_len = 0;
adamc@864 394 }
adamc@864 395
adamc@864 396 if (read_nvp(&buf, len - (buf - r->contentData), &hs.nvps[used_nvps]) < 0) {
adamc@864 397 write_stderr(out, "Error reading FCGI_PARAMS name-value pair\n");
adamc@864 398 goto done;
adamc@864 399 }
adamc@864 400
adamc@1049 401 //write_stderr(out, "PARAM: %s -> %s\n", hs.nvps[used_nvps].name, hs.nvps[used_nvps].value);
adamc@864 402
adamc@864 403 ++used_nvps;
adamc@860 404 }
adamc@859 405 }
adamc@860 406
adamc@862 407 hs.nvps[used_nvps].name[0] = 0;
adamc@860 408
adamc@1134 409 if ((s = get_header(&hs, "Content-Length"))) {
adamc@860 410 body_len = atoi(s);
adamc@860 411 if (body_len < 0) {
adamc@860 412 write_stderr(out, "Invalid Content-Length\n");
adamc@860 413 goto done;
adamc@860 414 }
adamc@860 415 } else
adamc@860 416 body_len = 0;
adamc@860 417
adamc@861 418 if (body_len+1 > body_size) {
adamc@861 419 body_size = body_len+1;
adamc@860 420 body = realloc(body, body_size);
adamc@860 421 }
adamc@860 422
adamc@860 423 for (body_read = 0; body_read < body_len; ) {
adamc@860 424 int this_len;
adamc@860 425
adamc@860 426 if (!(r = fastcgi_recv(in))) {
adamc@860 427 write_stderr(out, "Error receiving STDIN\n");
adamc@860 428 goto done;
adamc@860 429 }
adamc@860 430
adamc@860 431 if (r->type != FCGI_STDIN) {
adamc@860 432 write_stderr(out, "Expected FCGI_STDIN but got %d\n", r->type);
adamc@860 433 goto done;
adamc@860 434 }
adamc@860 435
adamc@860 436 if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0) {
adamc@860 437 write_stderr(out, "End of STDIN\n");
adamc@860 438 break;
adamc@860 439 }
adamc@860 440
adamc@860 441 this_len = (r->contentLengthB1 << 8) | r->contentLengthB0;
adamc@860 442
adamc@860 443 if (body_read + this_len > body_len) {
adamc@860 444 write_stderr(out, "Too much STDIN\n");
adamc@860 445 goto done;
adamc@860 446 }
adamc@860 447
adamc@860 448 memcpy(&body[body_read], r->contentData, this_len);
adamc@860 449 body_read += this_len;
adamc@860 450 }
adamc@860 451
adamc@861 452 body[body_read] = 0;
adamc@861 453
adamc@860 454 if (!(method = search_nvps(hs.nvps, "REQUEST_METHOD"))) {
adamc@860 455 write_stderr(out, "REQUEST_METHOD not set\n");
adamc@860 456 goto done;
adamc@860 457 }
adamc@860 458
adamc@860 459 if (!(path = search_nvps(hs.nvps, "SCRIPT_NAME"))) {
adamc@860 460 write_stderr(out, "SCRIPT_NAME not set\n");
adamc@860 461 goto done;
adamc@860 462 }
adamc@861 463
adamc@1134 464 if ((path_info = search_nvps(hs.nvps, "PATH_INFO"))) {
adamc@860 465 int len1 = strlen(path), len2 = strlen(path_info);
adamc@860 466 int len = len1 + len2 + 1;
adamc@860 467
adamc@860 468 if (len > path_size) {
adamc@860 469 path_size = len;
adamc@860 470 path_buf = realloc(path_buf, path_size);
adamc@860 471 }
adamc@860 472
adamc@860 473 sprintf(path_buf, "%s%s", path, path_info);
adamc@860 474 path = path_buf;
adamc@860 475 }
adamc@860 476
adamc@860 477 if (!(query_string = search_nvps(hs.nvps, "QUERY_STRING")))
adamc@860 478 query_string = "";
adamc@860 479
adamc@860 480 uw_set_headers(ctx, get_header, &hs);
adam@1799 481 uw_set_env(ctx, get_env, &hs);
adamc@861 482
adamc@860 483 {
adamc@860 484 request_result rr;
adamc@860 485
adamc@860 486 rr = uw_request(rc, ctx, method, path, query_string, body, body_read,
adamc@860 487 on_success, on_failure,
adamc@860 488 out, log_error, log_debug,
adamc@863 489 in->sock, fastcgi_send_normal, fastcgi_close);
adamc@860 490
adamc@860 491 if (rr == KEEP_OPEN)
adamc@860 492 goto done2;
adamc@860 493
adamc@860 494 uw_output(ctx, write_stdout, out);
adamc@863 495 fastcgi_close_with(out, rr);
adamc@863 496 goto done2;
adamc@860 497 }
adamc@859 498
adamc@859 499 done:
adamc@859 500 close(in->sock);
adamc@860 501 done2:
adamc@859 502 fastcgi_input_reset(in);
adamc@859 503 uw_reset(ctx);
adamc@859 504 }
adamc@1138 505
adamc@1138 506 return NULL;
adamc@859 507 }
adamc@859 508
adamc@859 509 static void help(char *cmd) {
adamc@859 510 printf("Usage: %s [-t <thread-count>]\n", cmd);
adamc@859 511 }
adamc@859 512
adamc@859 513 static void sigint(int signum) {
adamc@859 514 printf("Exiting....\n");
adamc@859 515 exit(0);
adamc@859 516 }
adamc@859 517
grrwlf@1997 518 static uw_loggers ls = {NULL, log_error, log_debug};
adamc@859 519
adamc@859 520 int main(int argc, char *argv[]) {
adamc@859 521 // The skeleton for this function comes from Beej's sockets tutorial.
adamc@859 522 struct sockaddr_in their_addr; // connector's address information
adamc@1134 523 socklen_t sin_size;
adamc@859 524 int nthreads = 1, i, *names, opt;
adamc@859 525 char *fwsa = getenv("FCGI_WEB_SERVER_ADDRS"), *nthreads_s = getenv("URWEB_NUM_THREADS");
adamc@859 526
adamc@859 527 if (nthreads_s) {
adamc@859 528 nthreads = atoi(nthreads_s);
adamc@859 529 if (nthreads <= 0) {
adamc@859 530 fprintf(stderr, "Bad URWEB_NUM_THREADS value\n");
adamc@859 531 return 1;
adamc@859 532 }
adamc@859 533 }
adamc@859 534
adamc@859 535 signal(SIGINT, sigint);
adamc@859 536 signal(SIGPIPE, SIG_IGN);
adamc@859 537 signal(SIGUSR1, sigint);
adamc@859 538 signal(SIGTERM, sigint);
adamc@859 539
adamc@859 540 while ((opt = getopt(argc, argv, "ht:")) != -1) {
adamc@859 541 switch (opt) {
adamc@859 542 case '?':
adamc@859 543 fprintf(stderr, "Unknown command-line option");
adamc@859 544 help(argv[0]);
adamc@859 545 return 1;
adamc@859 546
adamc@859 547 case 'h':
adamc@859 548 help(argv[0]);
adamc@859 549 return 0;
adamc@859 550
adamc@859 551 case 't':
adamc@859 552 nthreads = atoi(optarg);
adamc@859 553 if (nthreads <= 0) {
adamc@859 554 fprintf(stderr, "Invalid thread count\n");
adamc@859 555 help(argv[0]);
adamc@859 556 return 1;
adamc@859 557 }
adamc@859 558 break;
adamc@859 559
adamc@859 560 default:
adamc@859 561 fprintf(stderr, "Unexpected getopt() behavior\n");
adamc@859 562 return 1;
adamc@859 563 }
adamc@859 564 }
adamc@859 565
adamc@860 566 uw_set_on_success("");
grrwlf@1997 567 uw_request_init(&uw_application, &ls);
adamc@859 568
adamc@859 569 names = calloc(nthreads, sizeof(int));
adamc@859 570
adamc@859 571 sin_size = sizeof their_addr;
adamc@859 572
adamc@859 573 {
adamc@859 574 pthread_t thread;
adamc@859 575
grrwlf@1997 576 pruner_data *pd = (pruner_data *)malloc(sizeof(pruner_data));
grrwlf@1997 577 pd->app = &uw_application;
grrwlf@1997 578 pd->loggers = &ls;
grrwlf@1997 579
grrwlf@1997 580 if (pthread_create_big(&thread, NULL, client_pruner, pd)) {
adamc@859 581 fprintf(stderr, "Error creating pruner thread\n");
adamc@859 582 return 1;
adamc@859 583 }
adamc@859 584 }
adamc@859 585
adamc@859 586 for (i = 0; i < nthreads; ++i) {
adamc@859 587 pthread_t thread;
adamc@859 588 names[i] = i;
adam@1522 589 if (pthread_create_big(&thread, NULL, worker, &names[i])) {
adamc@859 590 fprintf(stderr, "Error creating worker thread #%d\n", i);
adamc@859 591 return 1;
adamc@859 592 }
adamc@859 593 }
adamc@859 594
adamc@859 595 while (1) {
adamc@859 596 int new_fd = accept(FCGI_LISTENSOCK_FILENO, (struct sockaddr *)&their_addr, &sin_size);
adamc@859 597
adamc@859 598 if (new_fd < 0) {
adamc@859 599 fprintf(stderr, "Socket accept failed\n");
adamc@859 600 return 1;
adamc@859 601 }
adamc@859 602
adamc@859 603 if (fwsa) {
adamc@859 604 char host[100], matched = 0;
adamc@859 605 char *ips, *sep;
adamc@859 606
adamc@859 607 if (getnameinfo((struct sockaddr *)&their_addr, sin_size, host, sizeof host, NULL, 0, NI_NUMERICHOST)) {
adamc@859 608 fprintf(stderr, "Remote IP determination failed\n");
adamc@859 609 return 1;
adamc@859 610 }
adamc@859 611
adamc@1134 612 for (ips = fwsa; (sep = strchr(ips, ',')); ips = sep+1) {
adamc@859 613 if (!strncmp(ips, host, sep - ips)) {
adamc@859 614 matched = 1;
adamc@859 615 break;
adamc@859 616 }
adamc@859 617 }
adamc@859 618
adamc@859 619 if (!matched && strcmp(ips, host)) {
adamc@859 620 fprintf(stderr, "Remote address is not in FCGI_WEB_SERVER_ADDRS");
adamc@859 621 return 1;
adamc@859 622 }
adamc@859 623 }
adamc@859 624
adamc@859 625 uw_enqueue(new_fd);
adamc@859 626 }
adamc@859 627 }
adamc@1121 628
adamc@1121 629 void *uw_init_client_data() {
adamc@1121 630 return NULL;
adamc@1121 631 }
adamc@1121 632
adamc@1121 633 void uw_free_client_data(void *data) {
adamc@1121 634 }
adamc@1121 635
adamc@1121 636 void uw_copy_client_data(void *dst, void *src) {
adamc@1121 637 }
adamc@1121 638
adamc@1121 639 void uw_do_expunge(uw_context ctx, uw_Basis_client cli, void *data) {
adam@1941 640 uw_ensure_transaction(ctx);
adam@1941 641 uw_get_app(ctx)->expunger(ctx, cli);
adam@1941 642
adam@1941 643 if (uw_commit(ctx))
adam@1941 644 uw_error(ctx, UNLIMITED_RETRY, "Rerunning expunge transaction");
adamc@1121 645 }
adamc@1121 646
adamc@1121 647 void uw_post_expunge(uw_context ctx, void *data) {
adamc@1121 648 }
adam@1320 649
adam@1320 650 int uw_supports_direct_status = 0;