annotate src/c/driver.c @ 818:066493f7f008

Change List.mapM' to avoid leaving functions around
author Adam Chlipala <adamc@hcoop.net>
date Thu, 21 May 2009 11:45:04 -0400
parents 5f49a6b759cb
children 19fdeef40ada
rev   line source
adamc@737 1 #define _GNU_SOURCE
adamc@737 2
adamc@116 3 #include <stdio.h>
adamc@116 4 #include <string.h>
adamc@502 5 #include <stdlib.h>
adamc@116 6 #include <sys/types.h>
adamc@116 7 #include <sys/socket.h>
adamc@116 8 #include <netinet/in.h>
adamc@472 9 #include <unistd.h>
adamc@502 10 #include <signal.h>
adamc@116 11
adamc@138 12 #include <pthread.h>
adamc@138 13
adamc@734 14 #include <mhash.h>
adamc@734 15
adamc@244 16 #include "urweb.h"
adamc@117 17
adamc@311 18 int uw_backlog = 10;
adamc@311 19 int uw_bufsize = 1024;
adamc@116 20
adamc@138 21 typedef struct node {
adamc@138 22 int fd;
adamc@138 23 struct node *next;
adamc@138 24 } *node;
adamc@138 25
adamc@138 26 static node front = NULL, back = NULL;
adamc@138 27
adamc@138 28 static int empty() {
adamc@138 29 return front == NULL;
adamc@138 30 }
adamc@138 31
adamc@138 32 static void enqueue(int fd) {
adamc@138 33 node n = malloc(sizeof(struct node));
adamc@138 34
adamc@138 35 n->fd = fd;
adamc@138 36 n->next = NULL;
adamc@138 37 if (back)
adamc@138 38 back->next = n;
adamc@138 39 else
adamc@138 40 front = n;
adamc@138 41 back = n;
adamc@138 42 }
adamc@138 43
adamc@138 44 static int dequeue() {
adamc@138 45 int ret = front->fd;
adamc@138 46
adamc@138 47 front = front->next;
adamc@138 48 if (!front)
adamc@138 49 back = NULL;
adamc@138 50
adamc@138 51 return ret;
adamc@138 52 }
adamc@138 53
adamc@138 54 static pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
adamc@138 55 static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
adamc@138 56
adamc@167 57 #define MAX_RETRIES 5
adamc@167 58
adamc@424 59 static int try_rollback(uw_context ctx) {
adamc@671 60 int r = uw_rollback(ctx);
adamc@424 61
adamc@424 62 if (r) {
adamc@424 63 printf("Error running SQL ROLLBACK\n");
adamc@424 64 uw_reset(ctx);
adamc@424 65 uw_write(ctx, "HTTP/1.1 500 Internal Server Error\n\r");
adamc@424 66 uw_write(ctx, "Content-type: text/plain\r\n\r\n");
adamc@424 67 uw_write(ctx, "Error running SQL ROLLBACK\n");
adamc@424 68 }
adamc@424 69
adamc@424 70 return r;
adamc@424 71 }
adamc@424 72
adamc@698 73 static uw_context new_context() {
adamc@687 74 uw_context ctx = uw_init();
adamc@698 75 int retries_left = MAX_RETRIES;
adamc@698 76
adamc@272 77 while (1) {
adamc@311 78 failure_kind fk = uw_begin_init(ctx);
adamc@272 79
adamc@272 80 if (fk == SUCCESS) {
adamc@272 81 printf("Database connection initialized.\n");
adamc@272 82 break;
adamc@272 83 } else if (fk == BOUNDED_RETRY) {
adamc@272 84 if (retries_left) {
adamc@311 85 printf("Initialization error triggers bounded retry: %s\n", uw_error_message(ctx));
adamc@272 86 --retries_left;
adamc@272 87 } else {
adamc@311 88 printf("Fatal initialization error (out of retries): %s\n", uw_error_message(ctx));
adamc@311 89 uw_free(ctx);
adamc@272 90 return NULL;
adamc@272 91 }
adamc@272 92 } else if (fk == UNLIMITED_RETRY)
adamc@311 93 printf("Initialization error triggers unlimited retry: %s\n", uw_error_message(ctx));
adamc@272 94 else if (fk == FATAL) {
adamc@311 95 printf("Fatal initialization error: %s\n", uw_error_message(ctx));
adamc@311 96 uw_free(ctx);
adamc@272 97 return NULL;
adamc@272 98 } else {
adamc@698 99 printf("Unknown uw_begin_init return code!\n");
adamc@311 100 uw_free(ctx);
adamc@272 101 return NULL;
adamc@272 102 }
adamc@272 103 }
adamc@116 104
adamc@698 105 return ctx;
adamc@698 106 }
adamc@698 107
adamc@734 108 #define KEYSIZE 16
adamc@734 109 #define PASSSIZE 4
adamc@734 110
adamc@734 111 #define HASH_ALGORITHM MHASH_SHA256
adamc@734 112 #define HASH_BLOCKSIZE 32
adamc@734 113 #define KEYGEN_ALGORITHM KEYGEN_MCRYPT
adamc@734 114
adamc@734 115 int uw_hash_blocksize = HASH_BLOCKSIZE;
adamc@734 116
adamc@734 117 static int password[PASSSIZE];
adamc@734 118 static unsigned char private_key[KEYSIZE];
adamc@734 119
adamc@734 120 static void init_crypto() {
adamc@734 121 KEYGEN kg = {{HASH_ALGORITHM, HASH_ALGORITHM}};
adamc@734 122 int i;
adamc@734 123
adamc@734 124 assert(mhash_get_block_size(HASH_ALGORITHM) == HASH_BLOCKSIZE);
adamc@734 125
adamc@734 126 for (i = 0; i < PASSSIZE; ++i)
adamc@734 127 password[i] = rand();
adamc@734 128
adamc@734 129 if (mhash_keygen_ext(KEYGEN_ALGORITHM, kg,
adamc@734 130 private_key, sizeof(private_key),
adamc@734 131 (unsigned char*)password, sizeof(password)) < 0) {
adamc@734 132 printf("Key generation failed\n");
adamc@734 133 exit(1);
adamc@734 134 }
adamc@734 135 }
adamc@734 136
adamc@734 137 void uw_sign(const char *in, char *out) {
adamc@734 138 MHASH td;
adamc@734 139
adamc@734 140 td = mhash_hmac_init(HASH_ALGORITHM, private_key, sizeof(private_key),
adamc@734 141 mhash_get_hash_pblock(HASH_ALGORITHM));
adamc@734 142
adamc@734 143 mhash(td, in, strlen(in));
adamc@734 144 if (mhash_hmac_deinit(td, out) < 0)
adamc@734 145 printf("Signing failed");
adamc@734 146 }
adamc@734 147
adamc@698 148 static void *worker(void *data) {
adamc@698 149 int me = *(int *)data, retries_left = MAX_RETRIES;
adamc@698 150 uw_context ctx = new_context();
adamc@742 151 size_t buf_size = 2;
adamc@737 152 char *buf = malloc(buf_size);
adamc@793 153 size_t path_copy_size = 0;
adamc@793 154 char *path_copy = malloc(path_copy_size);
adamc@698 155
adamc@116 156 while (1) {
adamc@737 157 char *back = buf, *s, *post;
adamc@667 158 int sock, dont_close = 0;
adamc@116 159
adamc@138 160 pthread_mutex_lock(&queue_mutex);
adamc@138 161 while (empty())
adamc@138 162 pthread_cond_wait(&queue_cond, &queue_mutex);
adamc@138 163 sock = dequeue();
adamc@138 164 pthread_mutex_unlock(&queue_mutex);
adamc@138 165
adamc@138 166 printf("Handling connection with thread #%d.\n", me);
adamc@138 167
adamc@138 168 while (1) {
adamc@167 169 unsigned retries_left = MAX_RETRIES;
adamc@737 170 int r;
adamc@737 171
adamc@742 172 if (back - buf == buf_size - 1) {
adamc@737 173 char *new_buf;
adamc@737 174 buf_size *= 2;
adamc@737 175 new_buf = realloc(buf, buf_size);
adamc@737 176 back = new_buf + (back - buf);
adamc@737 177 buf = new_buf;
adamc@737 178 }
adamc@737 179
adamc@742 180 r = recv(sock, back, buf_size - 1 - (back - buf), 0);
adamc@138 181
adamc@138 182 if (r < 0) {
adamc@138 183 fprintf(stderr, "Recv failed\n");
adamc@138 184 break;
adamc@138 185 }
adamc@138 186
adamc@138 187 if (r == 0) {
adamc@138 188 printf("Connection closed.\n");
adamc@138 189 break;
adamc@138 190 }
adamc@138 191
adamc@730 192 //printf("Received %d bytes.\n", r);
adamc@138 193
adamc@138 194 back += r;
adamc@138 195 *back = 0;
adamc@730 196
adamc@138 197 if (s = strstr(buf, "\r\n\r\n")) {
adamc@424 198 failure_kind fk;
adamc@741 199 int is_post = 0, do_normal_send = 1;
adamc@737 200 char *boundary = NULL;
adamc@737 201 size_t boundary_len;
adamc@793 202 char *cmd, *path, *headers, *inputs, *after_headers;
adamc@138 203
adamc@737 204 //printf("All: %s\n", buf);
adamc@737 205
adamc@475 206 s[2] = 0;
adamc@730 207 after_headers = s + 4;
adamc@144 208
adamc@138 209 if (!(s = strstr(buf, "\r\n"))) {
adamc@138 210 fprintf(stderr, "No newline in buf\n");
adamc@138 211 break;
adamc@138 212 }
adamc@138 213
adamc@138 214 *s = 0;
adamc@457 215 headers = s + 2;
adamc@138 216 cmd = s = buf;
adamc@401 217
adamc@737 218 //printf("Read: %s\n", buf);
adamc@138 219
adamc@138 220 if (!strsep(&s, " ")) {
adamc@138 221 fprintf(stderr, "No first space in HTTP command\n");
adamc@138 222 break;
adamc@138 223 }
adamc@138 224
adamc@730 225 uw_set_headers(ctx, headers);
adamc@730 226
adamc@730 227 if (!strcmp(cmd, "POST")) {
adamc@730 228 char *clen_s = uw_Basis_requestHeader(ctx, "Content-length");
adamc@730 229 if (!clen_s) {
adamc@737 230 fprintf(stderr, "No Content-length with POST\n");
adamc@730 231 goto done;
adamc@730 232 }
adamc@730 233 int clen = atoi(clen_s);
adamc@730 234 if (clen < 0) {
adamc@737 235 fprintf(stderr, "Negative Content-length with POST\n");
adamc@730 236 goto done;
adamc@730 237 }
adamc@730 238
adamc@730 239 while (back - after_headers < clen) {
adamc@742 240 if (back - buf == buf_size - 1) {
adamc@737 241 char *new_buf;
adamc@737 242 buf_size *= 2;
adamc@737 243 new_buf = realloc(buf, buf_size);
adamc@742 244
adamc@737 245 back = new_buf + (back - buf);
adamc@742 246 headers = new_buf + (headers - buf);
adamc@742 247 uw_headers_moved(ctx, headers);
adamc@742 248 after_headers = new_buf + (after_headers - buf);
adamc@742 249 s = new_buf + (s - buf);
adamc@742 250
adamc@737 251 buf = new_buf;
adamc@737 252 }
adamc@737 253
adamc@742 254 r = recv(sock, back, buf_size - 1 - (back - buf), 0);
adamc@730 255
adamc@730 256 if (r < 0) {
adamc@730 257 fprintf(stderr, "Recv failed\n");
adamc@730 258 goto done;
adamc@730 259 }
adamc@730 260
adamc@730 261 if (r == 0) {
adamc@730 262 printf("Connection closed.\n");
adamc@730 263 goto done;
adamc@730 264 }
adamc@730 265
adamc@730 266 back += r;
adamc@730 267 *back = 0;
adamc@730 268 }
adamc@730 269
adamc@730 270 is_post = 1;
adamc@737 271
adamc@737 272 clen_s = uw_Basis_requestHeader(ctx, "Content-type");
adamc@737 273 if (clen_s && !strncasecmp(clen_s, "multipart/form-data", 19)) {
adamc@737 274 if (strncasecmp(clen_s + 19, "; boundary=", 11)) {
adamc@737 275 fprintf(stderr, "Bad multipart boundary spec");
adamc@737 276 break;
adamc@737 277 }
adamc@737 278
adamc@737 279 boundary = clen_s + 28;
adamc@737 280 boundary[0] = '-';
adamc@737 281 boundary[1] = '-';
adamc@737 282 boundary_len = strlen(boundary);
adamc@737 283 }
adamc@730 284 } else if (strcmp(cmd, "GET")) {
adamc@730 285 fprintf(stderr, "Not ready for non-GET/POST command: %s\n", cmd);
adamc@138 286 break;
adamc@138 287 }
adamc@138 288
adamc@138 289 path = s;
adamc@138 290 if (!strsep(&s, " ")) {
adamc@138 291 fprintf(stderr, "No second space in HTTP command\n");
adamc@138 292 break;
adamc@138 293 }
adamc@138 294
adamc@668 295 if (!strcmp(path, "/.msgs")) {
adamc@668 296 char *id = uw_Basis_requestHeader(ctx, "UrWeb-Client");
adamc@668 297 char *pass = uw_Basis_requestHeader(ctx, "UrWeb-Pass");
adamc@668 298
adamc@668 299 if (id && pass) {
adamc@682 300 unsigned idn = atoi(id);
adamc@668 301 uw_client_connect(idn, atoi(pass), sock);
adamc@668 302 dont_close = 1;
adamc@682 303 fprintf(stderr, "Processed request for messages by client %u\n\n", idn);
adamc@668 304 }
adamc@703 305 else {
adamc@703 306 fprintf(stderr, "Missing fields in .msgs request: %s, %s\n\n", id, pass);
adamc@703 307 }
adamc@667 308 break;
adamc@667 309 }
adamc@667 310
adamc@737 311 if (boundary) {
adamc@737 312 char *part = after_headers, *after_sub_headers, *header, *after_header;
adamc@737 313 size_t part_len;
adamc@144 314
adamc@737 315 part = strstr(part, boundary);
adamc@737 316 if (!part) {
adamc@737 317 fprintf(stderr, "Missing first multipart boundary\n");
adamc@737 318 break;
adamc@737 319 }
adamc@737 320 part += boundary_len;
adamc@145 321
adamc@737 322 while (1) {
adamc@737 323 char *name = NULL, *filename = NULL, *type = NULL;
adamc@737 324
adamc@737 325 if (part[0] == '-' && part[1] == '-')
adamc@737 326 break;
adamc@737 327
adamc@737 328 if (*part != '\r') {
adamc@737 329 fprintf(stderr, "No \\r after multipart boundary\n");
adamc@737 330 goto done;
adamc@144 331 }
adamc@737 332 ++part;
adamc@737 333 if (*part != '\n') {
adamc@737 334 fprintf(stderr, "No \\n after multipart boundary\n");
adamc@737 335 goto done;
adamc@737 336 }
adamc@737 337 ++part;
adamc@737 338
adamc@737 339 if (!(after_sub_headers = strstr(part, "\r\n\r\n"))) {
adamc@737 340 fprintf(stderr, "Missing end of headers after multipart boundary\n");
adamc@737 341 goto done;
adamc@737 342 }
adamc@737 343 after_sub_headers[2] = 0;
adamc@737 344 after_sub_headers += 4;
adamc@737 345
adamc@737 346 for (header = part; after_header = strstr(header, "\r\n"); header = after_header + 2) {
adamc@737 347 char *colon, *after_colon;
adamc@737 348
adamc@737 349 *after_header = 0;
adamc@737 350 if (!(colon = strchr(header, ':'))) {
adamc@737 351 fprintf(stderr, "Missing colon in multipart sub-header\n");
adamc@737 352 goto done;
adamc@737 353 }
adamc@737 354 *colon++ = 0;
adamc@737 355 if (*colon++ != ' ') {
adamc@737 356 fprintf(stderr, "No space after colon in multipart sub-header\n");
adamc@737 357 goto done;
adamc@737 358 }
adamc@737 359
adamc@737 360 if (!strcasecmp(header, "Content-Disposition")) {
adamc@737 361 if (strncmp(colon, "form-data; ", 11)) {
adamc@737 362 fprintf(stderr, "Multipart data is not \"form-data\"\n");
adamc@737 363 goto done;
adamc@737 364 }
adamc@737 365
adamc@737 366 for (colon += 11; after_colon = strchr(colon, '='); colon = after_colon) {
adamc@737 367 char *data;
adamc@737 368 after_colon[0] = 0;
adamc@737 369 if (after_colon[1] != '"') {
adamc@737 370 fprintf(stderr, "Disposition setting is missing initial quote\n");
adamc@737 371 goto done;
adamc@737 372 }
adamc@737 373 data = after_colon+2;
adamc@737 374 if (!(after_colon = strchr(data, '"'))) {
adamc@737 375 fprintf(stderr, "Disposition setting is missing final quote\n");
adamc@737 376 goto done;
adamc@737 377 }
adamc@737 378 after_colon[0] = 0;
adamc@737 379 ++after_colon;
adamc@737 380 if (after_colon[0] == ';' && after_colon[1] == ' ')
adamc@737 381 after_colon += 2;
adamc@737 382
adamc@737 383 if (!strcasecmp(colon, "name"))
adamc@737 384 name = data;
adamc@737 385 else if (!strcasecmp(colon, "filename"))
adamc@737 386 filename = data;
adamc@737 387 }
adamc@737 388 } else if (!strcasecmp(header, "Content-Type")) {
adamc@737 389 type = colon;
adamc@737 390 }
adamc@737 391 }
adamc@737 392
adamc@737 393 part = memmem(after_sub_headers, back - after_sub_headers, boundary, boundary_len);
adamc@737 394 if (!part) {
adamc@737 395 fprintf(stderr, "Missing boundary after multipart payload\n");
adamc@737 396 goto done;
adamc@737 397 }
adamc@737 398 part[-2] = 0;
adamc@737 399 part_len = part - after_sub_headers - 2;
adamc@737 400 part[0] = 0;
adamc@737 401 part += boundary_len;
adamc@737 402
adamc@737 403 if (filename) {
adamc@740 404 uw_Basis_file f = {filename, type, {part_len, after_sub_headers}};
adamc@737 405
adamc@801 406 if (uw_set_file_input(ctx, name, f)) {
adamc@801 407 puts(uw_error_message(ctx));
adamc@801 408 goto done;
adamc@801 409 }
adamc@801 410 } else if (uw_set_input(ctx, name, after_sub_headers)) {
adamc@801 411 puts(uw_error_message(ctx));
adamc@801 412 goto done;
adamc@801 413 }
adamc@737 414 }
adamc@737 415 }
adamc@737 416 else {
adamc@737 417 if (is_post)
adamc@737 418 inputs = after_headers;
adamc@737 419 else if (inputs = strchr(path, '?'))
adamc@737 420 *inputs++ = 0;
adamc@737 421
adamc@737 422 if (inputs) {
adamc@737 423 char *name, *value;
adamc@737 424
adamc@737 425 while (*inputs) {
adamc@737 426 name = inputs;
adamc@737 427 if (inputs = strchr(inputs, '&'))
adamc@737 428 *inputs++ = 0;
adamc@737 429 else
adamc@737 430 inputs = strchr(name, 0);
adamc@737 431
adamc@737 432 if (value = strchr(name, '=')) {
adamc@737 433 *value++ = 0;
adamc@801 434 if (uw_set_input(ctx, name, value)) {
adamc@801 435 puts(uw_error_message(ctx));
adamc@801 436 goto done;
adamc@801 437 }
adamc@737 438 }
adamc@801 439 else if (uw_set_input(ctx, name, "")) {
adamc@801 440 puts(uw_error_message(ctx));
adamc@801 441 goto done;
adamc@801 442 }
adamc@737 443 }
adamc@144 444 }
adamc@144 445 }
adamc@144 446
adamc@138 447 printf("Serving URI %s....\n", path);
adamc@138 448
adamc@167 449 while (1) {
adamc@793 450 size_t path_len = strlen(path);
adamc@793 451
adamc@462 452 uw_write_header(ctx, "HTTP/1.1 200 OK\r\n");
adamc@167 453
adamc@793 454 if (path_len + 1 > path_copy_size) {
adamc@793 455 path_copy_size = path_len + 1;
adamc@793 456 path_copy = realloc(path_copy, path_copy_size);
adamc@793 457 }
adamc@400 458 strcpy(path_copy, path);
adamc@458 459 fk = uw_begin(ctx, path_copy);
adamc@741 460 if (fk == SUCCESS || fk == RETURN_BLOB) {
adamc@671 461 uw_commit(ctx);
adamc@167 462 break;
adamc@167 463 } else if (fk == BOUNDED_RETRY) {
adamc@167 464 if (retries_left) {
adamc@311 465 printf("Error triggers bounded retry: %s\n", uw_error_message(ctx));
adamc@167 466 --retries_left;
adamc@167 467 }
adamc@167 468 else {
adamc@311 469 printf("Fatal error (out of retries): %s\n", uw_error_message(ctx));
adamc@167 470
adamc@682 471 try_rollback(ctx);
adamc@682 472
adamc@311 473 uw_reset_keep_error_message(ctx);
adamc@464 474 uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\n\r");
adamc@464 475 uw_write_header(ctx, "Content-type: text/plain\r\n");
adamc@311 476 uw_write(ctx, "Fatal error (out of retries): ");
adamc@311 477 uw_write(ctx, uw_error_message(ctx));
adamc@311 478 uw_write(ctx, "\n");
adamc@424 479
adamc@424 480 break;
adamc@167 481 }
adamc@167 482 } else if (fk == UNLIMITED_RETRY)
adamc@311 483 printf("Error triggers unlimited retry: %s\n", uw_error_message(ctx));
adamc@167 484 else if (fk == FATAL) {
adamc@311 485 printf("Fatal error: %s\n", uw_error_message(ctx));
adamc@167 486
adamc@682 487 try_rollback(ctx);
adamc@682 488
adamc@311 489 uw_reset_keep_error_message(ctx);
adamc@464 490 uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\r\n");
adamc@548 491 uw_write_header(ctx, "Content-type: text/html\r\n");
adamc@548 492 uw_write(ctx, "<html><head><title>Fatal Error</title></head><body>");
adamc@311 493 uw_write(ctx, "Fatal error: ");
adamc@311 494 uw_write(ctx, uw_error_message(ctx));
adamc@548 495 uw_write(ctx, "\n</body></html>");
adamc@167 496
adamc@167 497 break;
adamc@167 498 } else {
adamc@311 499 printf("Unknown uw_handle return code!\n");
adamc@167 500
adamc@682 501 try_rollback(ctx);
adamc@682 502
adamc@311 503 uw_reset_keep_request(ctx);
adamc@464 504 uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\n\r");
adamc@464 505 uw_write_header(ctx, "Content-type: text/plain\r\n");
adamc@311 506 uw_write(ctx, "Unknown uw_handle return code!\n");
adamc@167 507
adamc@167 508 break;
adamc@167 509 }
adamc@167 510
adamc@424 511 if (try_rollback(ctx))
adamc@424 512 break;
adamc@682 513
adamc@682 514 uw_reset_keep_request(ctx);
adamc@167 515 }
adamc@138 516
adamc@311 517 uw_send(ctx, sock);
adamc@138 518
adamc@138 519 printf("Done with client.\n\n");
adamc@324 520 uw_memstats(ctx);
adamc@138 521 break;
adamc@138 522 }
adamc@116 523 }
adamc@116 524
adamc@730 525 done:
adamc@667 526 if (!dont_close)
adamc@667 527 close(sock);
adamc@311 528 uw_reset(ctx);
adamc@116 529 }
adamc@116 530 }
adamc@102 531
adamc@667 532 static void *client_pruner(void *data) {
adamc@698 533 uw_context ctx = new_context();
adamc@698 534
adamc@698 535 if (!ctx)
adamc@698 536 exit(1);
adamc@683 537
adamc@667 538 while (1) {
adamc@683 539 uw_prune_clients(ctx);
adamc@667 540 sleep(5);
adamc@667 541 }
adamc@667 542 }
adamc@667 543
adamc@477 544 static void help(char *cmd) {
adamc@477 545 printf("Usage: %s [-p <port>] [-t <thread-count>]\n", cmd);
adamc@477 546 }
adamc@477 547
adamc@502 548 static void sigint(int signum) {
adamc@502 549 printf("Exiting....\n");
adamc@502 550 exit(0);
adamc@502 551 }
adamc@502 552
adamc@687 553 static void initialize() {
adamc@734 554 uw_context ctx;
adamc@700 555 failure_kind fk;
adamc@687 556
adamc@734 557 init_crypto();
adamc@734 558
adamc@734 559 ctx = new_context();
adamc@734 560
adamc@698 561 if (!ctx)
adamc@698 562 exit(1);
adamc@698 563
adamc@700 564 for (fk = uw_initialize(ctx); fk == UNLIMITED_RETRY; fk = uw_initialize(ctx)) {
adamc@700 565 printf("Unlimited retry during init: %s\n", uw_error_message(ctx));
adamc@700 566 uw_db_rollback(ctx);
adamc@700 567 uw_reset(ctx);
adamc@700 568 }
adamc@700 569
adamc@700 570 if (fk != SUCCESS) {
adamc@700 571 printf("Failed to initialize database! %s\n", uw_error_message(ctx));
adamc@687 572 uw_db_rollback(ctx);
adamc@687 573 exit(1);
adamc@687 574 }
adamc@687 575
adamc@687 576 uw_free(ctx);
adamc@687 577 }
adamc@687 578
adamc@138 579 int main(int argc, char *argv[]) {
adamc@116 580 // The skeleton for this function comes from Beej's sockets tutorial.
adamc@138 581 int sockfd; // listen on sock_fd
adamc@116 582 struct sockaddr_in my_addr;
adamc@116 583 struct sockaddr_in their_addr; // connector's address information
adamc@116 584 int sin_size, yes = 1;
adamc@472 585 int uw_port = 8080, nthreads = 1, i, *names, opt;
adamc@502 586
adamc@502 587 signal(SIGINT, sigint);
adamc@505 588 signal(SIGPIPE, SIG_IGN);
adamc@505 589
adamc@477 590 while ((opt = getopt(argc, argv, "hp:t:")) != -1) {
adamc@472 591 switch (opt) {
adamc@472 592 case '?':
adamc@472 593 fprintf(stderr, "Unknown command-line option");
adamc@477 594 help(argv[0]);
adamc@472 595 return 1;
adamc@138 596
adamc@477 597 case 'h':
adamc@477 598 help(argv[0]);
adamc@477 599 return 0;
adamc@477 600
adamc@472 601 case 'p':
adamc@472 602 uw_port = atoi(optarg);
adamc@472 603 if (uw_port <= 0) {
adamc@472 604 fprintf(stderr, "Invalid port number\n");
adamc@477 605 help(argv[0]);
adamc@472 606 return 1;
adamc@472 607 }
adamc@472 608 break;
adamc@472 609
adamc@472 610 case 't':
adamc@472 611 nthreads = atoi(optarg);
adamc@472 612 if (nthreads <= 0) {
adamc@472 613 fprintf(stderr, "Invalid thread count\n");
adamc@477 614 help(argv[0]);
adamc@472 615 return 1;
adamc@472 616 }
adamc@472 617 break;
adamc@472 618
adamc@472 619 default:
adamc@472 620 fprintf(stderr, "Unexpected getopt() behavior\n");
adamc@472 621 return 1;
adamc@472 622 }
adamc@138 623 }
adamc@138 624
adamc@734 625 uw_global_init();
adamc@687 626 initialize();
adamc@687 627
adamc@138 628 names = calloc(nthreads, sizeof(int));
adamc@116 629
adamc@116 630 sockfd = socket(PF_INET, SOCK_STREAM, 0); // do some error checking!
adamc@116 631
adamc@116 632 if (sockfd < 0) {
adamc@116 633 fprintf(stderr, "Listener socket creation failed\n");
adamc@116 634 return 1;
adamc@116 635 }
adamc@116 636
adamc@116 637 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
adamc@116 638 fprintf(stderr, "Listener socket option setting failed\n");
adamc@116 639 return 1;
adamc@116 640 }
adamc@116 641
adamc@116 642 my_addr.sin_family = AF_INET; // host byte order
adamc@311 643 my_addr.sin_port = htons(uw_port); // short, network byte order
adamc@116 644 my_addr.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP
adamc@116 645 memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
adamc@116 646
adamc@116 647 if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) < 0) {
adamc@116 648 fprintf(stderr, "Listener socket bind failed\n");
adamc@116 649 return 1;
adamc@116 650 }
adamc@116 651
adamc@311 652 if (listen(sockfd, uw_backlog) < 0) {
adamc@116 653 fprintf(stderr, "Socket listen failed\n");
adamc@116 654 return 1;
adamc@116 655 }
adamc@116 656
adamc@116 657 sin_size = sizeof their_addr;
adamc@116 658
adamc@311 659 printf("Listening on port %d....\n", uw_port);
adamc@116 660
adamc@667 661 {
adamc@667 662 pthread_t thread;
adamc@667 663 int name;
adamc@667 664
adamc@667 665 if (pthread_create(&thread, NULL, client_pruner, &name)) {
adamc@667 666 fprintf(stderr, "Error creating pruner thread\n");
adamc@667 667 return 1;
adamc@667 668 }
adamc@667 669 }
adamc@667 670
adamc@138 671 for (i = 0; i < nthreads; ++i) {
adamc@138 672 pthread_t thread;
adamc@138 673 names[i] = i;
adamc@138 674 if (pthread_create(&thread, NULL, worker, &names[i])) {
adamc@138 675 fprintf(stderr, "Error creating worker thread #%d\n", i);
adamc@138 676 return 1;
adamc@138 677 }
adamc@138 678 }
adamc@138 679
adamc@116 680 while (1) {
adamc@138 681 int new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
adamc@116 682
adamc@116 683 if (new_fd < 0) {
adamc@116 684 fprintf(stderr, "Socket accept failed\n");
adamc@116 685 return 1;
adamc@116 686 }
adamc@116 687
adamc@116 688 printf("Accepted connection.\n");
adamc@138 689
adamc@138 690 pthread_mutex_lock(&queue_mutex);
adamc@138 691 enqueue(new_fd);
adamc@139 692 pthread_cond_broadcast(&queue_cond);
adamc@138 693 pthread_mutex_unlock(&queue_mutex);
adamc@116 694 }
adamc@102 695 }