00001
00002
00003
00012 #include "webcit.h"
00013 #include "webserver.h"
00014
00015 #if HAVE_BACKTRACE
00016 #include <execinfo.h>
00017 #endif
00018
00019 #ifndef HAVE_SNPRINTF
00020 int vsnprintf(char *buf, size_t max, const char *fmt, va_list argp);
00021 #endif
00022
00023 int verbosity = 9;
00024 int msock;
00025 int is_https = 0;
00026 int follow_xff = 0;
00027 int home_specified = 0;
00028 extern void *context_loop(int);
00029 extern void *housekeeping_loop(void);
00030 extern pthread_mutex_t SessionListMutex;
00031 extern pthread_key_t MyConKey;
00032
00033 char socket_dir[PATH_MAX];
00034 static const char editor_absolut_dir[PATH_MAX]=EDITORDIR;
00035 static char static_dir[PATH_MAX];
00036 static char static_local_dir[PATH_MAX];
00037 char *static_dirs[]={
00038 (char*)static_dir,
00039 (char*)static_local_dir,
00040 (char*)editor_absolut_dir
00041 };
00042
00048 char *static_content_dirs[] = {
00049 "static",
00050 "static.local",
00051 "tiny_mce"
00052 };
00053
00054 int ndirs=3;
00055
00056
00057 char *server_cookie = NULL;
00059 int http_port = PORT_NUM;
00061 char *ctdlhost = DEFAULT_HOST;
00062 char *ctdlport = DEFAULT_PORT;
00063 int setup_wizard = 0;
00064 char wizard_filename[PATH_MAX];
00065 int running_as_daemon = 0;
00075 int ig_tcp_server(char *ip_addr, int port_number, int queue_len)
00076 {
00077 struct sockaddr_in sin;
00078 int s, i;
00079
00080 memset(&sin, 0, sizeof(sin));
00081 sin.sin_family = AF_INET;
00082 if (ip_addr == NULL) {
00083 sin.sin_addr.s_addr = INADDR_ANY;
00084 } else {
00085 sin.sin_addr.s_addr = inet_addr(ip_addr);
00086 }
00087
00088 if (sin.sin_addr.s_addr == INADDR_NONE) {
00089 sin.sin_addr.s_addr = INADDR_ANY;
00090 }
00091
00092 if (port_number == 0) {
00093 lprintf(1, "Cannot start: no port number specified.\n");
00094 exit(WC_EXIT_BIND);
00095 }
00096 sin.sin_port = htons((u_short) port_number);
00097
00098 s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto));
00099 if (s < 0) {
00100 lprintf(1, "Can't create a socket: %s\n", strerror(errno));
00101 exit(WC_EXIT_BIND);
00102 }
00104 i = 1;
00105 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
00106
00107 if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
00108 lprintf(1, "Can't bind: %s\n", strerror(errno));
00109 exit(WC_EXIT_BIND);
00110 }
00111 if (listen(s, queue_len) < 0) {
00112 lprintf(1, "Can't listen: %s\n", strerror(errno));
00113 exit(WC_EXIT_BIND);
00114 }
00115 return (s);
00116 }
00117
00118
00119
00125 int ig_uds_server(char *sockpath, int queue_len)
00126 {
00127 struct sockaddr_un addr;
00128 int s;
00129 int i;
00130 int actual_queue_len;
00131
00132 actual_queue_len = queue_len;
00133 if (actual_queue_len < 5) actual_queue_len = 5;
00134
00135 i = unlink(sockpath);
00136 if (i != 0) if (errno != ENOENT) {
00137 lprintf(1, "citserver: can't unlink %s: %s\n",
00138 sockpath, strerror(errno));
00139 exit(WC_EXIT_BIND);
00140 }
00141
00142 memset(&addr, 0, sizeof(addr));
00143 addr.sun_family = AF_UNIX;
00144 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
00145
00146 s = socket(AF_UNIX, SOCK_STREAM, 0);
00147 if (s < 0) {
00148 lprintf(1, "citserver: Can't create a socket: %s\n",
00149 strerror(errno));
00150 exit(WC_EXIT_BIND);
00151 }
00152
00153 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
00154 lprintf(1, "citserver: Can't bind: %s\n",
00155 strerror(errno));
00156 exit(WC_EXIT_BIND);
00157 }
00158
00159 if (listen(s, actual_queue_len) < 0) {
00160 lprintf(1, "citserver: Can't listen: %s\n",
00161 strerror(errno));
00162 exit(WC_EXIT_BIND);
00163 }
00164
00165 chmod(sockpath, 0777);
00166 return(s);
00167 }
00168
00169
00170
00171
00183 int client_read_to(int sock, char *buf, int bytes, int timeout)
00184 {
00185 int len, rlen;
00186 fd_set rfds;
00187 struct timeval tv;
00188 int retval;
00189
00190
00191 #ifdef HAVE_OPENSSL
00192 if (is_https) {
00193 return (client_read_ssl(buf, bytes, timeout));
00194 }
00195 #endif
00196
00197 len = 0;
00198 while (len < bytes) {
00199 FD_ZERO(&rfds);
00200 FD_SET(sock, &rfds);
00201 tv.tv_sec = timeout;
00202 tv.tv_usec = 0;
00203
00204 retval = select((sock) + 1, &rfds, NULL, NULL, &tv);
00205 if (FD_ISSET(sock, &rfds) == 0) {
00206 return (0);
00207 }
00208
00209 rlen = read(sock, &buf[len], bytes - len);
00210
00211 if (rlen < 1) {
00212 lprintf(2, "client_read() failed: %s\n",
00213 strerror(errno));
00214 return (-1);
00215 }
00216 len = len + rlen;
00217 }
00218
00219 #ifdef HTTP_TRACING
00220 write(2, "\033[32m", 5);
00221 write(2, buf, bytes);
00222 write(2, "\033[30m", 5);
00223 #endif
00224 return (1);
00225 }
00226
00232 ssize_t client_write(const void *buf, size_t count)
00233 {
00234 char *newptr;
00235 size_t newalloc;
00236
00237 if (WC->burst != NULL) {
00238 if ((WC->burst_len + count) >= WC->burst_alloc) {
00239 newalloc = (WC->burst_alloc * 2);
00240 if ((WC->burst_len + count) >= newalloc) {
00241 newalloc += count;
00242 }
00243 newptr = realloc(WC->burst, newalloc);
00244 if (newptr != NULL) {
00245 WC->burst = newptr;
00246 WC->burst_alloc = newalloc;
00247 }
00248 }
00249 if ((WC->burst_len + count) < WC->burst_alloc) {
00250 memcpy(&WC->burst[WC->burst_len], buf, count);
00251 WC->burst_len += count;
00252 return (count);
00253 }
00254 else {
00255 return(-1);
00256 }
00257 }
00258 #ifdef HAVE_OPENSSL
00259 if (is_https) {
00260 client_write_ssl((char *) buf, count);
00261 return (count);
00262 }
00263 #endif
00264 #ifdef HTTP_TRACING
00265 write(2, "\033[34m", 5);
00266 write(2, buf, count);
00267 write(2, "\033[30m", 5);
00268 #endif
00269 return (write(WC->http_sock, buf, count));
00270 }
00271
00275 void begin_burst(void)
00276 {
00277 if (WC->burst != NULL) {
00278 free(WC->burst);
00279 WC->burst = NULL;
00280 }
00281 WC->burst_len = 0;
00282 WC->burst_alloc = 32768;
00283 WC->burst = malloc(WC->burst_alloc);
00284 }
00285
00286
00291 #ifdef HAVE_ZLIB
00292 #define DEF_MEM_LEVEL 8
00293 #define OS_CODE 0x03
00294 int ZEXPORT compress_gzip(Bytef * dest,
00295 uLongf * destLen,
00296 const Bytef * source,
00297 uLong sourceLen,
00298 int level)
00299 {
00300 const int gz_magic[2] = { 0x1f, 0x8b };
00303 sprintf((char *) dest, "%c%c%c%c%c%c%c%c%c%c",
00304 gz_magic[0], gz_magic[1], Z_DEFLATED,
00305 0 , 0, 0, 0, 0 , 0 ,
00306 OS_CODE);
00307
00308
00309 z_stream stream;
00310 int err;
00311 stream.next_in = (Bytef *) source;
00312 stream.avail_in = (uInt) sourceLen;
00313 stream.next_out = dest + 10L;
00314 stream.avail_out = (uInt) * destLen;
00315 if ((uLong) stream.avail_out != *destLen)
00316 return Z_BUF_ERROR;
00317
00318 stream.zalloc = (alloc_func) 0;
00319 stream.zfree = (free_func) 0;
00320 stream.opaque = (voidpf) 0;
00321
00322 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
00323 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
00324 if (err != Z_OK)
00325 return err;
00326
00327 err = deflate(&stream, Z_FINISH);
00328 if (err != Z_STREAM_END) {
00329 deflateEnd(&stream);
00330 return err == Z_OK ? Z_BUF_ERROR : err;
00331 }
00332 *destLen = stream.total_out + 10L;
00333
00334
00335 uLong crc = crc32(0L, source, sourceLen);
00336 int n;
00337 for (n = 0; n < 4; ++n, ++*destLen) {
00338 dest[*destLen] = (int) (crc & 0xff);
00339 crc >>= 8;
00340 }
00341 uLong len = stream.total_in;
00342 for (n = 0; n < 4; ++n, ++*destLen) {
00343 dest[*destLen] = (int) (len & 0xff);
00344 len >>= 8;
00345 }
00346 err = deflateEnd(&stream);
00347 return err;
00348 }
00349 #endif
00350
00354 void end_burst(void)
00355 {
00356 size_t the_len;
00357 char *the_data;
00358
00359 if (WC->burst == NULL)
00360 return;
00361
00362 the_len = WC->burst_len;
00363 the_data = WC->burst;
00364
00365 WC->burst_len = 0;
00366 WC->burst_alloc = 0;
00367 WC->burst = NULL;
00368
00369 #ifdef HAVE_ZLIB
00370
00371 if (WC->gzip_ok) {
00372 char *compressed_data = NULL;
00373 uLongf compressed_len;
00374
00375 compressed_len = (uLongf) ((the_len * 101) / 100) + 100;
00376 compressed_data = malloc(compressed_len);
00377
00378 if (compress_gzip((Bytef *) compressed_data,
00379 &compressed_len,
00380 (Bytef *) the_data,
00381 (uLongf) the_len, Z_BEST_SPEED) == Z_OK) {
00382 wprintf("Content-encoding: gzip\r\n");
00383 free(the_data);
00384 the_data = compressed_data;
00385 the_len = compressed_len;
00386 } else {
00387 free(compressed_data);
00388 }
00389 }
00390 #endif
00391
00392 wprintf("Content-length: %d\r\n\r\n", the_len);
00393 client_write(the_data, the_len);
00394 free(the_data);
00395 return;
00396 }
00397
00398
00399
00408 int client_read(int sock, char *buf, int bytes)
00409 {
00410 return (client_read_to(sock, buf, bytes, SLEEPING));
00411 }
00412
00413
00423 int client_getln(int sock, char *buf, int bufsiz)
00424 {
00425 int i, retval;
00426
00428 for (i = 0;; i++) {
00429 retval = client_read(sock, &buf[i], 1);
00430 if (retval != 1 || buf[i] == '\n' || i == (bufsiz-1))
00431 break;
00432 if ( (!isspace(buf[i])) && (!isprint(buf[i])) ) {
00434 return(-1);
00435 }
00436 }
00437
00439 if (i == (bufsiz-1))
00440 while (buf[i] != '\n' && retval == 1)
00441 retval = client_read(sock, &buf[i], 1);
00442
00446 buf[i] = 0;
00447 while ((strlen(buf) > 0) && (!isprint(buf[strlen(buf) - 1]))) {
00448 buf[strlen(buf) - 1] = 0;
00449 }
00450 return (retval);
00451 }
00452
00457 pid_t current_child;
00458 void graceful_shutdown(int signum) {
00459 kill(current_child, signum);
00460 exit(0);
00461 }
00462
00463
00470
00471
00472
00473 void start_daemon(char *pid_file)
00474 {
00475 int status = 0;
00476 pid_t child = 0;
00477 FILE *fp;
00478 int do_restart = 0;
00479
00480 current_child = 0;
00481
00482
00483
00484
00485
00486 chdir("/");
00487
00488 signal(SIGHUP, SIG_IGN);
00489 signal(SIGINT, SIG_IGN);
00490 signal(SIGQUIT, SIG_IGN);
00491
00492 child = fork();
00493 if (child != 0) {
00494 exit(0);
00495 }
00496
00497 setsid();
00498 umask(0);
00499 freopen("/dev/null", "r", stdin);
00500 freopen("/dev/null", "w", stdout);
00501 freopen("/dev/null", "w", stderr);
00502
00503 do {
00504 current_child = fork();
00505
00506
00507 if (current_child < 0) {
00508 perror("fork");
00509 exit(errno);
00510 }
00511
00512 else if (current_child == 0) {
00513 signal(SIGTERM, graceful_shutdown);
00514 return;
00515 }
00516
00517 else {
00518 signal(SIGTERM, SIG_IGN);
00519 if (pid_file) {
00520 fp = fopen(pid_file, "w");
00521 if (fp != NULL) {
00522 fprintf(fp, "%d\n", current_child);
00523 fclose(fp);
00524 }
00525 }
00526 waitpid(current_child, &status, 0);
00527 }
00528
00529 do_restart = 0;
00530
00531
00532 if (WIFEXITED(status)) {
00533
00534
00535 if (WEXITSTATUS(status) == 0) {
00536 do_restart = 0;
00537 }
00538
00539
00540 else if ( (WEXITSTATUS(status) >= 101) && (WEXITSTATUS(status) <= 109) ) {
00541 do_restart = 0;
00542 }
00543
00544
00545 else {
00546 do_restart = 1;
00547 }
00548 }
00549
00550
00551 else {
00552 do_restart = 1;
00553 }
00554
00555 } while (do_restart);
00556
00557 if (pid_file) {
00558 unlink(pid_file);
00559 }
00560 exit(WEXITSTATUS(status));
00561 }
00562
00566 void spawn_another_worker_thread()
00567 {
00568 pthread_t SessThread;
00569 pthread_attr_t attr;
00570 int ret;
00571
00572 lprintf(3, "Creating a new thread\n");
00573
00575 pthread_attr_init(&attr);
00576 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00577
00583 if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) {
00584 lprintf(1, "pthread_attr_setstacksize: %s\n",
00585 strerror(ret));
00586 pthread_attr_destroy(&attr);
00587 }
00588
00590 if (pthread_create(&SessThread, &attr,
00591 (void *(*)(void *)) worker_entry, NULL)
00592 != 0) {
00593 lprintf(1, "Can't create thread: %s\n", strerror(errno));
00594 }
00595
00597 pthread_attr_destroy(&attr);
00598 }
00599
00605 int main(int argc, char **argv)
00606 {
00607 pthread_t SessThread;
00608 pthread_attr_t attr;
00609 int a, i;
00610 char tracefile[PATH_MAX];
00611 char ip_addr[256];
00612 char dirbuffer[PATH_MAX]="";
00613 int relh=0;
00614 int home=0;
00615 int home_specified=0;
00616 char relhome[PATH_MAX]="";
00617 char webcitdir[PATH_MAX] = DATADIR;
00618 char *pidfile = NULL;
00619 char *hdir;
00620 const char *basedir;
00621 #ifdef ENABLE_NLS
00622 char *locale = NULL;
00623 char *mo = NULL;
00624 #endif
00625 char uds_listen_path[PATH_MAX];
00627 strcpy(uds_listen_path, "");
00628
00630 #ifdef HAVE_OPENSSL
00631 while ((a = getopt(argc, argv, "h:i:p:t:x:dD:cfs")) != EOF)
00632 #else
00633 while ((a = getopt(argc, argv, "h:i:p:t:x:dD:cf")) != EOF)
00634 #endif
00635 switch (a) {
00636 case 'h':
00637 hdir = strdup(optarg);
00638 relh=hdir[0]!='/';
00639 if (!relh) safestrncpy(webcitdir, hdir,
00640 sizeof webcitdir);
00641 else
00642 safestrncpy(relhome, relhome,
00643 sizeof relhome);
00644
00645 home_specified = 1;
00646 home=1;
00647 break;
00648 case 'd':
00649 running_as_daemon = 1;
00650 break;
00651 case 'D':
00652 pidfile = strdup(optarg);
00653 running_as_daemon = 1;
00654 break;
00655 case 'i':
00656 safestrncpy(ip_addr, optarg, sizeof ip_addr);
00657 break;
00658 case 'p':
00659 http_port = atoi(optarg);
00660 if (http_port == 0) {
00661 safestrncpy(uds_listen_path, optarg, sizeof uds_listen_path);
00662 }
00663 break;
00664 case 't':
00665 safestrncpy(tracefile, optarg, sizeof tracefile);
00666 freopen(tracefile, "w", stdout);
00667 freopen(tracefile, "w", stderr);
00668 freopen(tracefile, "r", stdin);
00669 break;
00670 case 'x':
00671 verbosity = atoi(optarg);
00672 break;
00673 case 'f':
00674 follow_xff = 1;
00675 break;
00676 case 'c':
00677 server_cookie = malloc(256);
00678 if (server_cookie != NULL) {
00679 safestrncpy(server_cookie,
00680 "Set-cookie: wcserver=",
00681 256);
00682 if (gethostname
00683 (&server_cookie[strlen(server_cookie)],
00684 200) != 0) {
00685 lprintf(2, "gethostname: %s\n",
00686 strerror(errno));
00687 free(server_cookie);
00688 }
00689 }
00690 break;
00691 case 's':
00692 is_https = 1;
00693 break;
00694 default:
00695 fprintf(stderr, "usage: webserver "
00696 "[-i ip_addr] [-p http_port] "
00697 "[-t tracefile] [-c] [-f] "
00698 "[-d] "
00699 #ifdef HAVE_OPENSSL
00700 "[-s] "
00701 #endif
00702 "[remotehost [remoteport]]\n");
00703 return 1;
00704 }
00705
00706 if (optind < argc) {
00707 ctdlhost = argv[optind];
00708 if (++optind < argc)
00709 ctdlport = argv[optind];
00710 }
00711
00712
00713 if (running_as_daemon) {
00714 start_daemon(pidfile);
00715 }
00716
00718 lprintf(1, SERVER "\n");
00719 lprintf(1, "Copyright (C) 1996-2007 by the Citadel development team.\n"
00720 "This software is distributed under the terms of the "
00721 "GNU General Public License.\n\n"
00722 );
00723
00724
00726 #ifdef ENABLE_NLS
00727 initialize_locales();
00728 locale = setlocale(LC_ALL, "");
00729 mo = malloc(strlen(webcitdir) + 20);
00730 lprintf(9, "Message catalog directory: %s\n", bindtextdomain("webcit", LOCALEDIR"/locale"));
00731 free(mo);
00732 lprintf(9, "Text domain: %s\n", textdomain("webcit"));
00733 lprintf(9, "Text domain Charset: %s\n", bind_textdomain_codeset("webcit","UTF8"));
00734 #endif
00735
00736
00737
00738
00739
00740 #define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
00741 snprintf(SUBDIR,sizeof SUBDIR, "%s%s%s%s%s%s%s", \
00742 (home&!relh)?webcitdir:basedir, \
00743 ((basedir!=webcitdir)&(home&!relh))?basedir:"/", \
00744 ((basedir!=webcitdir)&(home&!relh))?"/":"", \
00745 relhome, \
00746 (relhome[0]!='\0')?"/":"",\
00747 dirbuffer,\
00748 (dirbuffer[0]!='\0')?"/":"");
00749 basedir=RUNDIR;
00750 COMPUTE_DIRECTORY(socket_dir);
00751 basedir=DATADIR "/static";
00752 COMPUTE_DIRECTORY(static_dir);
00753 basedir=DATADIR "/static.local";
00754 COMPUTE_DIRECTORY(static_local_dir);
00756 lprintf(9, "Changing directory to %s\n", socket_dir);
00757 if (chdir(webcitdir) != 0) {
00758 perror("chdir");
00759 }
00760 initialize_viewdefs();
00761 initialize_axdefs();
00762
00768 if (pthread_key_create(&MyConKey, NULL) != 0) {
00769 lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
00770 }
00771
00778 #ifdef HAVE_OPENSSL
00779 if (pthread_key_create(&ThreadSSL, NULL) != 0) {
00780 lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
00781 }
00782 #endif
00783
00790 if (strlen(uds_listen_path) > 0) {
00791 lprintf(2, "Attempting to create listener socket at %s...\n", uds_listen_path);
00792 msock = ig_uds_server(uds_listen_path, LISTEN_QUEUE_LENGTH);
00793 }
00794 else {
00795 lprintf(2, "Attempting to bind to port %d...\n", http_port);
00796 msock = ig_tcp_server(ip_addr, http_port, LISTEN_QUEUE_LENGTH);
00797 }
00798
00799 lprintf(2, "Listening on socket %d\n", msock);
00800 signal(SIGPIPE, SIG_IGN);
00801
00802 pthread_mutex_init(&SessionListMutex, NULL);
00803
00807 pthread_attr_init(&attr);
00808 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00809 pthread_create(&SessThread, &attr,
00810 (void *(*)(void *)) housekeeping_loop, NULL);
00811
00812
00816 #ifdef HAVE_OPENSSL
00817 if (is_https) {
00818 init_ssl();
00819 }
00820 #endif
00821
00823 for (i = 0; i < (MIN_WORKER_THREADS); ++i) {
00824 spawn_another_worker_thread();
00825 }
00826
00827
00828 worker_entry();
00829 return 0;
00830 }
00831
00832
00836 void worker_entry(void)
00837 {
00838 int ssock;
00839 int i = 0;
00840 int time_to_die = 0;
00841 int fail_this_transaction = 0;
00842
00843 do {
00845 fail_this_transaction = 0;
00846 ssock = accept(msock, NULL, 0);
00847 if (ssock < 0) {
00848 lprintf(2, "accept() failed: %s\n",
00849 strerror(errno));
00850 } else {
00852 i = 1;
00853 setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR,
00854 &i, sizeof(i));
00855
00857 #ifdef HAVE_OPENSSL
00858 if (is_https) {
00859 if (starttls(ssock) != 0) {
00860 fail_this_transaction = 1;
00861 close(ssock);
00862 }
00863 }
00864 #endif
00865
00866 if (fail_this_transaction == 0) {
00867
00869 context_loop(ssock);
00870
00872 #ifdef HAVE_OPENSSL
00873 if (is_https) {
00874 endtls();
00875 }
00876 #endif
00877
00879 lingering_close(ssock);
00880 }
00881
00882 }
00883
00884 } while (!time_to_die);
00885
00886 pthread_exit(NULL);
00887 }
00888
00896 int lprintf(int loglevel, const char *format, ...)
00897 {
00898 va_list ap;
00899
00900 if (loglevel <= verbosity) {
00901 va_start(ap, format);
00902 vfprintf(stderr, format, ap);
00903 va_end(ap);
00904 fflush(stderr);
00905 }
00906 return 1;
00907 }
00908
00909
00913 void wc_backtrace(void)
00914 {
00915 #ifdef HAVE_BACKTRACE
00916 void *stack_frames[50];
00917 size_t size, i;
00918 char **strings;
00919
00920
00921 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
00922 strings = backtrace_symbols(stack_frames, size);
00923 for (i = 0; i < size; i++) {
00924 if (strings != NULL)
00925 lprintf(1, "%s\n", strings[i]);
00926 else
00927 lprintf(1, "%p\n", stack_frames[i]);
00928 }
00929 free(strings);
00930 #endif
00931 }
00932