You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1015 lines
24 KiB
C

#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]",localtime(&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 = NULL;
req->if_unmodified = NULL;
req->if_range = NULL;
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);
memset(req->mtime, 0, sizeof(req->mtime));
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();
#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);
}