diff options
Diffstat (limited to 'webfsd.c')
-rw-r--r-- | webfsd.c | 1014 |
1 files changed, 1014 insertions, 0 deletions
diff --git a/webfsd.c b/webfsd.c new file mode 100644 index 0000000..690628c --- /dev/null +++ b/webfsd.c @@ -0,0 +1,1014 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <syslog.h> +#include <fcntl.h> +#include <time.h> +#include <pwd.h> +#include <grp.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/signal.h> +#include <sys/utsname.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "httpd.h" + +/* ---------------------------------------------------------------------- */ +/* public variables - server configuration */ + +char *server_name = "webfs/" WEBFS_VERSION; + +int debug = 0; +int dontdetach = 0; +int timeout = 60; +int keepalive_time = 5; +int tcp_port = 0; +int max_dircache = 128; +char *doc_root = "."; +char *indexhtml = NULL; +char *cgipath = NULL; +char *listen_ip = NULL; +char *listen_port = "8000"; +int virtualhosts = 0; +int canonicalhost = 0; +char server_host[256]; +char user[17]; +char group[17]; +char *mimetypes = MIMEFILE; +char *pidfile = NULL; +char *logfile = NULL; +FILE *logfh = NULL; +char *userpass = NULL; +char *userdir = NULL; +int flushlog = 0; +int do_chroot = 0; +int usesyslog = 0; +int have_tty = 1; +int max_conn = 32; +int lifespan = -1; +int no_listing = 0; + +time_t now; +int slisten; + +#ifdef USE_THREADS +pthread_mutex_t lock_logfile = PTHREAD_MUTEX_INITIALIZER; +int nthreads = 1; +pthread_t *threads; +#endif + +#ifdef USE_SSL +char *certificate = "server.pem"; +char *password; +int with_ssl = 0; +SSL_CTX *ctx; +BIO *sbio, *ssl_bio; +#endif + +/* ---------------------------------------------------------------------- */ + +static int termsig,got_sighup; + +static void catchsig(int sig) +{ + if (SIGTERM == sig || SIGINT == sig) + termsig = sig; + if (SIGHUP == sig) + got_sighup = 1; +} + +/* ---------------------------------------------------------------------- */ + +static void +usage(char *name) +{ + char *h; + struct passwd *pw; + struct group *gr; + + h = strrchr(name,'/'); + fprintf(stderr, + "This is a lightweight http server for static content\n" + "\n" + "usage: %s [ options ]\n" + "\n" + "Options:\n" + " -h print this text\n" + " -4 use ipv4\n" + " -6 use ipv6\n" + " -d enable debug output [%s]\n" + " -F do not fork into background [%s]\n" + " -s enable syslog (start/stop/errors) [%s]\n" + " -t sec set network timeout [%i]\n" + " -c n set max. allowed connections [%i]\n" + " -a n set max. cached dirs [%i]\n" + " -j disable directory listings [%s]\n" +#ifdef USE_THREADS + " -y n startup n threads [%i]\n" +#endif + " -p port use tcp-port >port< [%s]\n" + " -r dir document root is >dir< [%s]\n" + " -R dir same as above + chroot to >dir<\n" + " -f file look for >file< as directory index [%s]\n" + " -n host server hostname is >host< [%s]\n" + " -N host same as above + UseCanonicalName\n" + " -i ip bind to IP-address >ip< [%s]\n" + " -v enable virtual hosts [%s]\n" + " -l log write access log to file >log< [%s]\n" + " -L log same as above + flush every line\n" + " -m file read mime types from >file< [%s]\n" + " -k file use >file< as pidfile [%s]\n" + " -b user:pass password protect the exported\n" + " files (basic authentication)\n" + " -e sec limit live span of files to sec\n" + " seconds (using expires header)\n" +#ifdef USE_SSL + " -S enable SSL mode\n" + " -C file SSL-Certificate file [%s]\n" + " -P pass SSL-Certificate password\n" +#endif + " -x dir CGI script directory (relative to\n" + " document root) [%s]\n" + " -~ dir user home directory (will expand\n" + " /~user/path to $HOME/dir/path\n", + h ? h+1 : name, + debug ? "on" : "off", + dontdetach ? "on" : "off", + usesyslog ? "on" : "off", + timeout, max_conn, max_dircache, + no_listing ? "on" : "off", +#ifdef USE_THREADS + nthreads, +#endif + listen_port, doc_root, + indexhtml ? indexhtml : "none", + server_host, + listen_ip ? listen_ip : "any", + virtualhosts ? "on" : "off", + logfile ? logfile : "none", + mimetypes, + pidfile ? pidfile : "none", +#ifdef USE_SSL + certificate, +#endif + cgipath ? cgipath : "none"); + if (getuid() == 0) { + pw = getpwuid(0); + gr = getgrgid(getgid()); + fprintf(stderr, + " -u user run as user >user< [%s]\n" + " -g group run as group >group< [%s]\n", + pw ? pw->pw_name : "???", + gr ? gr->gr_name : "???"); + } + exit(1); +} + +static void run_as(int id) +{ + if (-1 == seteuid(id)) { + fprintf(stderr,"seteuid(%d): %s\n",id,strerror(errno)); + exit(1); + } + if (debug) + fprintf(stderr,"run_as: uid=%d euid=%d\n",getuid(),geteuid()); +} + +static void +fix_ug(void) +{ + struct passwd *pw = NULL; + struct group *gr = NULL; + + /* root is allowed to use any uid/gid, + * others will get their real uid/gid */ + if (0 == getuid() && strlen(user) > 0) { + if (NULL == (pw = getpwnam(user))) + pw = getpwuid(atoi(user)); + } else { + pw = getpwuid(getuid()); + } + if (0 == getuid() && strlen(group) > 0) { + if (NULL == (gr = getgrnam(group))) + gr = getgrgid(atoi(group)); + } else { + gr = getgrgid(getgid()); + } + + if (NULL == pw) { + xerror(LOG_ERR,"user unknown",NULL); + exit(1); + } + if (NULL == gr) { + xerror(LOG_ERR,"group unknown",NULL); + exit(1); + } + + /* chroot to $DOCUMENT_ROOT (must be done here as getpwuid needs + /etc and chroot works as root only) */ + if (do_chroot) { + chdir(doc_root); + if (-1 == chroot(doc_root)) { + xperror(LOG_ERR,"chroot",NULL); + exit(1); + } + } + + /* set group */ + if (getegid() != gr->gr_gid || getgid() != gr->gr_gid) { + setgid(gr->gr_gid); + setgroups(0, NULL); + } + if (getegid() != gr->gr_gid || getgid() != gr->gr_gid) { + xerror(LOG_ERR,"setgid failed",NULL); + exit(1); + } + strncpy(group,gr->gr_name,16); + + /* set user */ + if (geteuid() != pw->pw_uid || getuid() != pw->pw_uid) + setuid(pw->pw_uid); + if (geteuid() != pw->pw_uid || getuid() != pw->pw_uid) { + xerror(LOG_ERR,"setuid failed",NULL); + exit(1); + } + strncpy(user,pw->pw_name,16); + + if (debug) + fprintf(stderr,"fix_ug: uid=%d euid=%d / gid=%d egid=%d\n", + getuid(),geteuid(),getgid(),getegid()); +} + +/* ---------------------------------------------------------------------- */ + +static void +access_log(struct REQUEST *req, time_t now) +{ + char timestamp[32]; + + DO_LOCK(lock_logfile); + if (NULL == logfh) { + DO_UNLOCK(lock_logfile); + return; + } + + /* common log format: host ident authuser date request status bytes */ + strftime(timestamp,31,"[%d/%b/%Y:%H:%M:%S +0000]",gmtime(&now)); + if (0 == req->status) + req->status = 400; /* bad request */ + if (400 == req->status) { + fprintf(logfh,"%s - - %s \"-\" 400 %d\n", + req->peerhost, + timestamp, + req->bc); + } else { + fprintf(logfh,"%s - - %s \"%s %s HTTP/%d.%d\" %d %d\n", + req->peerhost, + timestamp, + req->type, + req->uri, + req->major, + req->minor, + req->status, + req->bc); + } + if (flushlog) + fflush(logfh); + DO_UNLOCK(lock_logfile); +} + +/* + * loglevel usage + * ERR : fatal errors (which are followed by exit(1)) + * WARNING: this should'nt happen error (oom, ...) + * NOTICE : start/stop of the daemon + * INFO : "normal" errors (canceled downloads, timeouts, + * stuff what happens all the time) + */ + +static void +syslog_init(void) +{ + openlog("webfsd",LOG_PID, LOG_DAEMON); +} + +static void +syslog_start(void) +{ + syslog(LOG_NOTICE, + "started (listen on %s:%d, root=%s, user=%s, group=%s)\n", + listen_ip ? listen_ip : "*", + tcp_port,doc_root,user,group); +} + +static void +syslog_stop(void) +{ + if (termsig) + syslog(LOG_NOTICE,"stopped on signal %d (%s)\n", + termsig,strsignal(termsig)); + else + syslog(LOG_NOTICE,"stopped\n"); + closelog(); +} + +void +xperror(int loglevel, char *txt, char *peerhost) +{ + if (LOG_INFO == loglevel && usesyslog < 2 && !debug) + return; + if (have_tty) { + if (NULL == peerhost) + perror(txt); + else + fprintf(stderr,"%s: %s (peer=%s)\n",txt,strerror(errno), + peerhost); + } + if (usesyslog) { + if (NULL == peerhost) + syslog(loglevel,"%s: %s\n",txt,strerror(errno)); + else + syslog(loglevel,"%s: %s (peer=%s)\n",txt,strerror(errno), + peerhost); + } +} + +void +xerror(int loglevel, char *txt, char *peerhost) +{ + if (LOG_INFO == loglevel && usesyslog < 2 && !debug) + return; + if (have_tty) { + if (NULL == peerhost) + fprintf(stderr,"%s\n",txt); + else + fprintf(stderr,"%s (peer=%s)\n",txt,peerhost); + } + if (usesyslog) { + if (NULL == peerhost) + syslog(loglevel,"%s\n",txt); + else + syslog(loglevel,"%s (peer=%s)\n",txt,peerhost); + } +} + +/* ---------------------------------------------------------------------- */ +/* main loop */ + +static void* +mainloop(void *thread_arg) +{ + struct REQUEST *conns = NULL; + int curr_conn = 0; + + struct REQUEST *req,*prev,*tmp; + struct timeval tv; + int max,length; + fd_set rd,wr; + + for (;!termsig;) { + if (got_sighup) { + if (NULL != logfile && 0 != strcmp(logfile,"-")) { + if (debug) + fprintf(stderr,"got SIGHUP, reopen logfile %s\n",logfile); + DO_LOCK(lock_logfile); + if (logfh) + fclose(logfh); + if (NULL == (logfh = fopen(logfile,"a"))) + xperror(LOG_WARNING,"reopen access log",NULL); + else + close_on_exec(fileno(logfh)); + DO_UNLOCK(lock_logfile); + } + got_sighup = 0; + } + FD_ZERO(&rd); + FD_ZERO(&wr); + max = 0; + /* add listening socket */ + if (curr_conn < max_conn) { + FD_SET(slisten,&rd); + max = slisten; + } + /* add connection sockets */ + for (req = conns; req != NULL; req = req->next) { + switch (req->state) { + case STATE_KEEPALIVE: + case STATE_READ_HEADER: + FD_SET(req->fd,&rd); + if (req->fd > max) + max = req->fd; + break; + case STATE_WRITE_HEADER: + case STATE_WRITE_BODY: + case STATE_WRITE_FILE: + case STATE_WRITE_RANGES: + case STATE_CGI_BODY_OUT: + FD_SET(req->fd,&wr); +#ifdef USE_SSL + if (with_ssl) + FD_SET(req->fd,&rd); +#endif + if (req->fd > max) + max = req->fd; + break; + case STATE_CGI_HEADER: + case STATE_CGI_BODY_IN: + FD_SET(req->cgipipe,&rd); + if (req->cgipipe > max) + max = req->cgipipe; + break; + } + } + /* go! */ + tv.tv_sec = keepalive_time; + tv.tv_usec = 0; + if (-1 == select(max+1,&rd,&wr,NULL,(curr_conn > 0) ? &tv : NULL)) { + if (debug) + perror("select"); + continue; + } + now = time(NULL); + + /* new connection ? */ + if (FD_ISSET(slisten,&rd)) { + req = malloc(sizeof(struct REQUEST)); + if (NULL == req) { + /* oom: let the request sit in the listen queue */ + if (debug) + fprintf(stderr,"oom\n"); + } else { + memset(req,0,sizeof(struct REQUEST)); + if (-1 == (req->fd = accept(slisten,NULL,NULL))) { + if (EAGAIN != errno) + xperror(LOG_WARNING,"accept",NULL); + free(req); + } else { + close_on_exec(req->fd); + fcntl(req->fd,F_SETFL,O_NONBLOCK); + req->bfd = -1; + req->cgipipe = -1; + req->state = STATE_READ_HEADER; + req->ping = now; + req->next = conns; + conns = req; + curr_conn++; + if (debug) + fprintf(stderr,"%03d: new request (%d)\n",req->fd,curr_conn); +#ifdef USE_SSL + if (with_ssl) + open_ssl_session(req); +#endif + length = sizeof(req->peer); + if (-1 == getpeername(req->fd,(struct sockaddr*)&(req->peer),&length)) { + xperror(LOG_WARNING,"getpeername",NULL); + req->state = STATE_CLOSE; + } + getnameinfo((struct sockaddr*)&req->peer,length, + req->peerhost,64,req->peerserv,8, + NI_NUMERICHOST | NI_NUMERICSERV); + if (debug) + fprintf(stderr,"%03d: connect from (%s)\n", + req->fd,req->peerhost); + } + } + } + + /* check active connections */ + for (req = conns, prev = NULL; req != NULL;) { + /* handle I/O */ + switch (req->state) { + case STATE_KEEPALIVE: + case STATE_READ_HEADER: + if (FD_ISSET(req->fd,&rd)) { + req->state = STATE_READ_HEADER; + read_request(req,0); + req->ping = now; + } + break; + case STATE_WRITE_HEADER: + case STATE_WRITE_BODY: + case STATE_WRITE_FILE: + case STATE_WRITE_RANGES: + case STATE_CGI_BODY_OUT: + if (FD_ISSET(req->fd,&wr)) { + write_request(req); + req->ping = now; + } +#ifdef USE_SSL + if (with_ssl && FD_ISSET(req->fd,&rd)) { + write_request(req); + req->ping = now; + } +#endif + break; + case STATE_CGI_HEADER: + if (FD_ISSET(req->cgipipe,&rd)) { + cgi_read_header(req); + req->ping = now; + } + break; + case STATE_CGI_BODY_IN: + if (FD_ISSET(req->cgipipe,&rd)) { + write_request(req); + req->ping = now; + } + break; + } + + /* check timeouts */ + if (req->state == STATE_KEEPALIVE) { + if (now > req->ping + keepalive_time || + curr_conn > max_conn * 9 / 10) { + if (debug) + fprintf(stderr,"%03d: keepalive timeout\n",req->fd); + req->state = STATE_CLOSE; + } + } else { + if (now > req->ping + timeout) { + if (req->state == STATE_READ_HEADER) { + mkerror(req,408,0); + } else { + xerror(LOG_INFO,"network timeout",req->peerhost); + req->state = STATE_CLOSE; + } + } + } + + /* header parsing */ +header_parsing: + if (req->state == STATE_PARSE_HEADER) { + parse_request(req); + if (req->state == STATE_WRITE_HEADER) + write_request(req); + } + + /* handle finished requests */ + if (req->state == STATE_FINISHED && !req->keep_alive) + req->state = STATE_CLOSE; + if (req->state == STATE_FINISHED) { + if (logfh) + access_log(req,now); + /* cleanup */ + req->auth[0] = 0; + req->if_modified = 0; + req->if_unmodified = 0; + req->if_range = 0; + req->range_hdr = NULL; + req->ranges = 0; + if (req->r_start) { free(req->r_start); req->r_start = NULL; } + if (req->r_end) { free(req->r_end); req->r_end = NULL; } + if (req->r_head) { free(req->r_head); req->r_head = NULL; } + if (req->r_hlen) { free(req->r_hlen); req->r_hlen = NULL; } + list_free(&req->header); + + if (req->bfd != -1) { + close(req->bfd); + req->bfd = -1; + } + if (req->cgipipe != -1) { + close(req->cgipipe); + req->cgipipe = -1; + } + if (req->cgipid) { + kill(req->cgipid,SIGTERM); + req->cgipid = 0; + } + req->body = NULL; + req->written = 0; + req->head_only = 0; + req->rh = 0; + req->rb = 0; + if (req->dir) { + free_dir(req->dir); + req->dir = NULL; + } + req->hostname[0] = 0; + req->path[0] = 0; + req->query[0] = 0; + + if (req->hdata == req->lreq) { + /* ok, wait for the next one ... */ + if (debug) + fprintf(stderr,"%03d: keepalive wait\n",req->fd); + req->state = STATE_KEEPALIVE; + req->hdata = 0; + req->lreq = 0; +#ifdef TCP_CORK + if (1 == req->tcp_cork) { + req->tcp_cork = 0; + if (debug) + fprintf(stderr,"%03d: tcp_cork=%d\n",req->fd,req->tcp_cork); + setsockopt(req->fd,SOL_TCP,TCP_CORK,&req->tcp_cork,sizeof(int)); + } +#endif + } else { + /* there is a pipelined request in the queue ... */ + if (debug) + fprintf(stderr,"%03d: keepalive pipeline\n",req->fd); + req->state = STATE_READ_HEADER; + memmove(req->hreq,req->hreq+req->lreq, + req->hdata-req->lreq); + req->hdata -= req->lreq; + req->lreq = 0; + read_request(req,1); + goto header_parsing; + } + } + + /* connections to close */ + if (req->state == STATE_CLOSE) { + if (logfh) + access_log(req,now); + /* cleanup */ + close(req->fd); +#ifdef USE_SSL + if (with_ssl) + SSL_free(req->ssl_s); +#endif + if (req->bfd != -1) + close(req->bfd); + if (req->cgipipe != -1) + close(req->cgipipe); + if (req->cgipid) + kill(req->cgipid,SIGTERM); + if (req->dir) + free_dir(req->dir); + curr_conn--; + if (debug) + fprintf(stderr,"%03d: done (%d)\n",req->fd,curr_conn); + /* unlink from list */ + tmp = req; + if (prev == NULL) { + conns = req->next; + req = conns; + } else { + prev->next = req->next; + req = req->next; + } + /* free memory */ + if (tmp->r_start) free(tmp->r_start); + if (tmp->r_end) free(tmp->r_end); + if (tmp->r_head) free(tmp->r_head); + if (tmp->r_hlen) free(tmp->r_hlen); + list_free(&tmp->header); + free(tmp); + } else { + prev = req; + req = req->next; + } + } + } + return NULL; +} + +/* ---------------------------------------------------------------------- */ + +int +main(int argc, char *argv[]) +{ + struct sigaction act,old; + struct addrinfo ask,*res; + struct sockaddr_storage ss; + int c, opt, rc, ss_len, pid=0, v4 = 1, v6 = 1; + int uid,euid; + char host[INET6_ADDRSTRLEN+1]; + char serv[16]; + char mypid[12]; + + uid = getuid(); + euid = geteuid(); + if (uid != euid) + run_as(uid); + gethostname(server_host,255); + memset(&ask,0,sizeof(ask)); + ask.ai_flags = AI_CANONNAME; + if (0 == (rc = getaddrinfo(server_host, NULL, &ask, &res))) { + if (res->ai_canonname) + strcpy(server_host,res->ai_canonname); + } + + /* parse options */ + for (;;) { + if (-1 == (c = getopt(argc,argv,"hvsdF46jS" + "r:R:f:p:n:N:i:t:c:a:u:g:l:L:m:y:b:k:e:x:C:P:~:"))) + break; + switch (c) { + case 'h': + usage(argv[0]); + break; + case '4': + v4 = 1; + v6 = 0; + break; + case '6': + v4 = 0; + v6 = 1; + break; + case 's': + usesyslog++; + break; + case 'd': + debug++; + break; + case 'v': + virtualhosts++; + break; + case 'F': + dontdetach++; + break; + case 'R': + do_chroot = 1; + /* fall through */ + case 'r': + doc_root = optarg; + break; + case 'f': + indexhtml = optarg; + break; + case 'N': + canonicalhost = 1; + /* fall through */ + case 'n': + strncpy(server_host,optarg,64); + break; + case 'i': + listen_ip = optarg; + break; + case 'p': + listen_port = optarg; + break; + case 't': + timeout = atoi(optarg); + break; + case 'c': + max_conn = atoi(optarg); + break; + case 'a': + max_dircache = atoi(optarg); + break; + case 'u': + strncpy(user,optarg,16); + break; + case 'g': + strncpy(group,optarg,16); + break; + case 'L': + flushlog = 1; + /* fall through */ + case 'l': + logfile = optarg; + break; + case 'm': + mimetypes = optarg; + break; + case 'k': + pidfile = optarg; + break; + case 'b': + userpass = strdup(optarg); + memset(optarg,'x',strlen(optarg)); + break; + case 'e': + lifespan = atoi(optarg); + break; + case 'x': + if (optarg[strlen(optarg)-1] == '/') { + cgipath = optarg; + } else { + cgipath = malloc(strlen(optarg)+2); + sprintf(cgipath,"%s/",optarg); + } + break; +#ifdef USE_THREADS + case 'y': + nthreads = atoi(optarg); + break; +#endif +#ifdef USE_SSL + case 'S': + with_ssl++; + break; + case 'C': + certificate = optarg; + break; + case 'P': + password = strdup(optarg); + memset(optarg,'x',strlen(optarg)); + break; +#endif + case 'j': + no_listing = 1; + break; + case '~': + userdir = optarg; + break; + default: + exit(1); + } + } + if (usesyslog) + syslog_init(); + + /* bind to socket */ + slisten = -1; + memset(&ask,0,sizeof(ask)); + ask.ai_flags = AI_PASSIVE; + if (listen_ip) + ask.ai_flags |= AI_CANONNAME; + ask.ai_socktype = SOCK_STREAM; + + /* try ipv6 first ... */ + if (-1 == slisten && v6) { + ask.ai_family = PF_INET6; + if (0 != (rc = getaddrinfo(listen_ip, listen_port, &ask, &res))) { + if (debug) + fprintf(stderr,"getaddrinfo (ipv6): %s\n",gai_strerror(rc)); + } else { + if (-1 == (slisten = socket(res->ai_family, res->ai_socktype, + res->ai_protocol)) && debug) + xperror(LOG_ERR,"socket (ipv6)",NULL); + } + } + + /* ... failing that try ipv4 */ + if (-1 == slisten && v4) { + ask.ai_family = PF_INET; + if (0 != (rc = getaddrinfo(listen_ip, listen_port, &ask, &res))) { + fprintf(stderr,"getaddrinfo (ipv4): %s\n",gai_strerror(rc)); + exit(1); + } + if (-1 == (slisten = socket(res->ai_family, res->ai_socktype, + res->ai_protocol))) { + xperror(LOG_ERR,"socket (ipv4)",NULL); + exit(1); + } + } + + if (-1 == slisten) + exit(1); + close_on_exec(slisten); + + memcpy(&ss,res->ai_addr,res->ai_addrlen); + ss_len = res->ai_addrlen; + if (res->ai_canonname) + strcpy(server_host,res->ai_canonname); + if (0 != (rc = getnameinfo((struct sockaddr*)&ss,ss_len, + host,INET6_ADDRSTRLEN,serv,15, + NI_NUMERICHOST | NI_NUMERICSERV))) { + fprintf(stderr,"getnameinfo: %s\n",gai_strerror(rc)); + exit(1); + } + + tcp_port = atoi(serv); + opt = 1; + setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); + fcntl(slisten,F_SETFL,O_NONBLOCK); + + /* Use accept filtering, if available. */ +#ifdef SO_ACCEPTFILTER + { + struct accept_filter_arg af; + memset(&af,0,sizeof(af)); + strcpy(af.af_name,"httpready"); + setsockopt(slisten, SOL_SOCKET, SO_ACCEPTFILTER, (char*)&af, sizeof(af)); + } +#endif /* SO_ACCEPTFILTER */ + + if (uid != euid) + run_as (euid); + if (-1 == bind(slisten, (struct sockaddr*) &ss, ss_len)) { + xperror(LOG_ERR,"bind",NULL); + exit(1); + } + if (uid != euid) + run_as (uid); + if (-1 == listen(slisten, 2*max_conn)) { + xperror(LOG_ERR,"listen",NULL); + exit(1); + } + + /* init misc stuff */ + init_mime(mimetypes,"text/plain"); + init_quote(); + putenv("TZ=GMT"); tzset(); /* any better way to make mktime(3) use GMT ? */ +#ifdef USE_SSL + if (with_ssl) + init_ssl(); +#endif + + /* change user/group - also does chroot */ + if (uid != euid) + run_as (euid); + fix_ug(); + + if (logfile) { + if (0 == strcmp(logfile,"-")) { + logfh = stdout; + } else { + if (NULL == (logfh = fopen(logfile,"a"))) + xperror(LOG_WARNING,"open access log",NULL); + else + close_on_exec(fileno(logfh)); + } + } + + if (pidfile) { + if (-1 == (pid = open(pidfile,O_WRONLY | O_CREAT | O_EXCL, 0600))) { + fprintf(stderr,"open %s: %s\n",pidfile,strerror(errno)); + exit(1); + } + close_on_exec(pid); + } + + if (debug) { + fprintf(stderr, + "http server started\n" + " ipv6 : %s\n" +#ifdef USE_SSL + " ssl : %s\n" +#endif + " node : %s\n" + " ipaddr: %s\n" + " port : %d\n" + " export: %s\n" + " user : %s\n" + " group : %s\n", + res->ai_family == PF_INET6 ? "yes" : "no", +#ifdef USE_SSL + with_ssl ? "yes" : "no", +#endif + server_host,host,tcp_port,doc_root,user,group); + } + + /* run as daemon - detach from terminal */ + if ((!debug) && (!dontdetach)) { + switch (fork()) { + case -1: + xperror(LOG_ERR,"fork",NULL); + exit(1); + case 0: + close(0); close(1); close(2); setsid(); + have_tty = 0; + break; + default: + exit(0); + } + } + if (usesyslog) { + syslog_start(); + atexit(syslog_stop); + } + if (pidfile) { + sprintf(mypid,"%d",getpid()); + write(pid,mypid,strlen(mypid)); + close(pid); + } + + /* setup signal handler */ + memset(&act,0,sizeof(act)); + sigemptyset(&act.sa_mask); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE,&act,&old); + sigaction(SIGCHLD,&act,&old); + act.sa_handler = catchsig; + sigaction(SIGHUP,&act,&old); + sigaction(SIGTERM,&act,&old); + if (debug) + sigaction(SIGINT,&act,&old); + + /* go! */ +#ifdef USE_THREADS + if (nthreads > 1) { + int i; + threads = malloc(sizeof(pthread_t) * nthreads); + for (i = 1; i < nthreads; i++) { + pthread_create(threads+i,NULL,mainloop,threads+i); + pthread_detach(threads[i]); + } + } +#endif + mainloop(NULL); + +#ifdef USE_SSL + if (with_ssl) + SSL_CTX_free(ctx); +#endif + if (logfh) + fclose(logfh); + if (pidfile) + unlink(pidfile); + if (debug) + fprintf(stderr,"bye...\n"); + exit(0); +} |