+Version 0.13:
+
+ * FreeBSD support.
+ * Improved dirplex/patplex configuration.
+ * Added httrcall.
+
Version 0.12:
* Support chunked request-bodies from clients.
-AC_INIT(src/htparser.c)
-AM_INIT_AUTOMAKE([ashd], [0.13])
+AC_INIT([ashd], [0.13])
+AC_CONFIG_SRCDIR(src/htparser.c)
+AM_INIT_AUTOMAKE
AM_CONFIG_HEADER(config.h)
+AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC
AM_PROG_CC_C_O
AC_MSG_ERROR([*** cannot find libmagic on this system])
fi
+AH_TEMPLATE(HAVE_GLIBC_STDIO, [define to indicate system support for glibc cookie streams])
+AH_TEMPLATE(HAVE_BSD_STDIO, [define to indicate system support for BSD-style funopen streams])
+
+HAS_FOPENCOOKIE=yes
+AC_CHECK_FUNC(fopencookie, [], [HAS_FOPENCOOKIE=no])
+AC_CHECK_MEMBER([cookie_io_functions_t.read], [], [HAS_FOPENCOOKIE=no])
+
+HAS_FUNOPEN=yes
+AC_CHECK_FUNC(funopen, [], [HAS_FUNOPEN=no])
+
+if test "$HAS_FOPENCOOKIE" = yes; then
+ AC_DEFINE(HAVE_GLIBC_STDIO)
+elif test "$HAS_FUNOPEN" = yes; then
+ AC_DEFINE(HAVE_BSD_STDIO)
+else
+ AC_MSG_ERROR([*** libc support for custom stdio streams is required])
+fi
+
AH_TEMPLATE(HAVE_VALGRIND, [define to include debugging support for Valgrind])
AC_CHECK_HEADER(valgrind/memcheck.h, [AC_DEFINE(HAVE_VALGRIND)], [])
AH_TEMPLATE(HAVE_EPOLL, [define to enable epoll support])
-AC_ARG_WITH(epoll, [ --with-epoll Enable epoll(2) support])
+AC_ARG_WITH(epoll, AS_HELP_STRING([--with-epoll], [enable epoll(2) support]))
HAS_EPOLL=""
if test "$with_epoll" = no; then HAS_EPOLL=no; fi
if test -z "$HAS_EPOLL"; then
if test "$HAS_EPOLL" = yes; then
AC_DEFINE(HAVE_EPOLL)
fi
-AM_CONDITIONAL(USE_EPOLL, [test "$HAS_EPOLL" = yes ])
+
+AH_TEMPLATE(HAVE_KQUEUE, [define to enable kqueue support])
+AC_ARG_WITH(kqueue, AS_HELP_STRING([--with-kqueue], [enable kqueue(2) support]))
+HAS_KQUEUE=""
+if test "$with_kqueue" = no; then HAS_QUEUE=no; fi
+if test -z "$HAS_KQUEUE"; then
+ AC_CHECK_FUNC(kqueue, [], [HAS_KQUEUE=no])
+fi
+if test -z "$HAS_KQUEUE"; then
+ AC_CHECK_HEADER(sys/event.h, [], [HAS_KQUEUE=no])
+fi
+if test "$HAS_KQUEUE" != no; then HAS_KQUEUE=yes; fi
+if test "$with_kqueue" = yes -a "$HAS_KQUEUE" = no; then
+ AC_MSG_ERROR([*** cannot find kqueue support on this system])
+fi
+if test "$HAS_KQUEUE" = yes; then
+ AC_DEFINE(HAVE_KQUEUE)
+fi
+
+AM_CONDITIONAL(USE_EPOLL, [test "$HAS_EPOLL" = yes])
+AM_CONDITIONAL(USE_KQUEUE, [test "$HAS_KQUEUE" = yes])
AH_TEMPLATE(HAVE_XATTR, [define to compile support for filesystem extended attributes])
-AC_ARG_WITH(xattr, [ --with-xattr Enable XATTR support])
+AC_ARG_WITH(xattr, AS_HELP_STRING([--with-xattr], [enable XATTR support]))
HAS_XATTR=""
if test "$with_xattr" = no; then HAS_XATTR=no; fi
if test -z "$HAS_XATTR"; then
AC_SUBST(XATTR_LIBS)
AH_TEMPLATE(HAVE_GNUTLS, [define to use the GnuTLS library for SSL support])
-AC_ARG_WITH(gnutls, [ --with-gnutls Enable SSL support with the GnuTLS library])
+AC_ARG_WITH(gnutls, AS_HELP_STRING([--with-gnutls], [enable SSL support with the GnuTLS library]))
HAS_GNUTLS=""
if test "$with_gnutls" = no; then HAS_GNUTLS=no; fi
if test -z "$HAS_GNUTLS"; then
dist_man1_MANS = callcgi.1 dirplex.1 htparser.1 patplex.1 sendfile.1 \
userplex.1 htls.1 callscgi.1 accesslog.1 htextauth.1 \
callfcgi.1 multifscgi.1 errlogger.1 httimed.1 \
- psendfile.1
+ psendfile.1 httrcall.1
dist_man7_MANS = ashd.7
-%.7 %.1: %.doc
+.doc.1:
a2x -f manpage $<
-
-%.html: %.doc
+.doc.7:
+ a2x -f manpage $<
+.doc.html:
a2x -f xhtml $<
manpages: $(dist_man1_MANS) $(dist_man7_MANS)
-htmldoc: $(patsubst %.doc, %.html, *.doc)
+htmldoc: ${dist_man1_MANS:.1=.html} ${dist_man7_MANS:.7=.html}
EXTRA_DIST = *.doc
Note that *htextauth* will wait for the authentication program to exit
and not process any other requests until then.
+FILES
+-----
+The file `etc/extauth/vhtpasswd` in the *ashd* source distribution is
+a simple authenticator program (written in Python) that can be used
+with *htextauth*, which verifies the given credentials against a
+simple database of users with encrypted passwords. It can be used as
+is, or as a simple example of how to produce authenticator
+programs. The accompanying `mkhtpasswd` program can be used to
+maintain the password database.
+
AUTHOR
------
Fredrik Tolf <fredrik@dolda2000.com>
--- /dev/null
+httrcall(1)
+==========
+
+NAME
+----
+httrcall - Call transient ashd handlers
+
+SYNOPSIS
+--------
+*httrcall* [*-h*] 'PROGRAM' ['ARGS'...]
+
+DESCRIPTION
+-----------
+
+*httrcall* is a persistent handler, as defined in *ashd*(7), but works
+by starting a specified transient handler for every incoming
+request. Thus, it can be used to run transient handlers where normally
+only persistent handlers are accepted, such as for the program
+specified to *accesslog*(1), *htextauth*(1) or even *htparser*(1).
+
+The transient handler to call is specified by the 'PROGRAM'
+argument. Any 'ARGS' given are prepended to the usual arguments for
+transient handlers, as described in *ashd*(7).
+
+OPTIONS
+-------
+
+*-h*::
+
+ Print a brief help message to standard output and exit.
+
+AUTHOR
+------
+Fredrik Tolf <fredrik@dolda2000.com>
+
+SEE ALSO
+--------
+*ashd*(7)
--- /dev/null
+#!/usr/bin/python
+
+import sys, os, termios, hmac, hashlib, getopt, getpass
+
+def usage(out):
+ out.write("usage: mkhtpasswd [-h] FILE USERNAME\n")
+
+opts, args = getopt.getopt(sys.argv[1:], "h")
+for o, a in opts:
+ if o == "-h":
+ usage(sys.stdout)
+ sys.exit(0)
+if len(args) < 2:
+ usage(sys.stderr)
+ sys.exit(1)
+
+def hashpw(usr, pw):
+ dig = hmac.new(pw, digestmod=hashlib.sha1)
+ dig.update(usr)
+ return dig.hexdigest()
+
+if ':' in args[1]:
+ sys.stderr.write("mkhtpasswd: username cannot contain `:'\n")
+ sys.exit(1)
+
+passwds = {}
+if os.path.exists(args[0]):
+ with open(args[0]) as fp:
+ for line in fp:
+ usr, pw = line.strip().split(':')
+ passwds[usr] = pw
+
+passwds[args[1]] = hashpw(args[1], getpass.getpass())
+
+with open(args[0], "w") as fp:
+ for usr, pw in passwds.iteritems():
+ fp.write("%s:%s\n" % (usr, pw))
--- /dev/null
+#!/usr/bin/python
+
+import sys, hmac, hashlib, getopt
+
+def usage(out):
+ out.write("usage: vhtpasswd [-h] FILE\n")
+
+opts, args = getopt.getopt(sys.argv[1:], "h")
+for o, a in opts:
+ if o == "-h":
+ usage(sys.stdout)
+ sys.exit(0)
+if len(args) < 1:
+ usage(sys.stderr)
+ sys.exit(1)
+
+def hashpw(usr, pw):
+ dig = hmac.new(pw, digestmod=hashlib.sha1)
+ dig.update(usr)
+ return dig.hexdigest()
+
+def findpw(fn, name):
+ with open(fn) as fp:
+ for line in fp:
+ usr, pw = line.strip().split(':')
+ if usr == name:
+ return pw
+ return None
+
+usr = sys.stdin.readline().strip()
+gpw = sys.stdin.readline().strip()
+if findpw(args[0], usr) == hashpw(usr, gpw):
+ sys.exit(0)
+sys.exit(1)
libht_a_SOURCES = utils.c mt.c log.c req.c proc.c mtio.c resp.c cf.c
libht_a_CFLAGS = -fPIC
-libht_a_CPPFLAGS = -D_GNU_SOURCE
if USE_EPOLL
libht_a_SOURCES += mtio-epoll.c
else
+if USE_KQUEUE
+libht_a_SOURCES += mtio-kqueue.c
+else
libht_a_SOURCES += mtio-select.c
endif
+endif
pkginclude_HEADERS = utils.h mt.h log.h req.h proc.h mtio.h resp.h cf.h
return(ret);
}
-static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata)
+struct sidata {
+ struct stdchild *sd;
+ void (*sinit)(void *);
+ void *sdata;
+};
+
+static void stdinit(void *data)
+{
+ struct sidata *d = data;
+ int i;
+
+ for(i = 0; d->sd->envp[i]; i += 2)
+ putenv(sprintf2("%s=%s", d->sd->envp[i], d->sd->envp[i + 1]));
+ if(d->sinit != NULL)
+ d->sinit(d->sdata);
+}
+
+static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *sdata)
{
struct stdchild *sd = ch->pdata;
int serr;
char **args;
-
- void stdinit(void *data)
- {
- int i;
-
- for(i = 0; sd->envp[i]; i += 2)
- putenv(sprintf2("%s=%s", sd->envp[i], sd->envp[i + 1]));
- if(chinit != NULL)
- chinit(data);
- }
+ struct sidata idat;
if(sd->type == CH_SOCKET) {
+ idat = (struct sidata) {.sd = sd, .sinit = chinit, sdata = sdata};
if(sd->fd < 0) {
args = expandargs(sd);
- sd->fd = stdmkchild(args, stdinit, idata);
+ sd->fd = stdmkchild(args, stdinit, &idat);
freeca(args);
}
if(sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT)) {
/* Assume that the child has crashed and restart it. */
close(sd->fd);
args = expandargs(sd);
- sd->fd = stdmkchild(args, stdinit, idata);
+ sd->fd = stdmkchild(args, stdinit, &idat);
freeca(args);
if(!sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT))
goto ok;
}
} else if(sd->type == CH_FORK) {
args = expandargs(sd);
- if(stdforkserve(args, req, fd, chinit, idata) < 0) {
+ if(stdforkserve(args, req, fd, chinit, sdata) < 0) {
freeca(args);
return(-1);
}
--- /dev/null
+/*
+ ashd - A Sane HTTP Daemon
+ Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/event.h>
+#include <errno.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <log.h>
+#include <utils.h>
+#include <mt.h>
+#include <mtio.h>
+
+static struct blocker *blockers;
+
+struct blocker {
+ struct blocker *n, *p, *n2, *p2;
+ int fd, reg;
+ int ev;
+ time_t to;
+ struct muth *th;
+};
+
+static int qfd = -1, fdln = 0;
+static int exitstatus;
+static struct blocker **fdlist;
+
+static int regfd(struct blocker *bl)
+{
+ struct blocker *o;
+ int prev;
+ struct kevent evd;
+
+ if(bl->fd >= fdln) {
+ if(fdlist) {
+ fdlist = srealloc(fdlist, sizeof(*fdlist) * (bl->fd + 1));
+ memset(fdlist + fdln, 0, sizeof(*fdlist) * (bl->fd + 1 - fdln));
+ fdln = bl->fd + 1;
+ } else {
+ fdlist = szmalloc(sizeof(*fdlist) * (fdln = (bl->fd + 1)));
+ }
+ }
+ for(prev = 0, o = fdlist[bl->fd]; o; o = o->n2)
+ prev |= o->ev;
+ if((bl->ev & EV_READ) && !(prev & EV_READ)) {
+ evd = (struct kevent) {
+ .flags = EV_ADD,
+ .ident = bl->fd,
+ .filter = EVFILT_READ,
+ };
+ if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) {
+ /* XXX?! Whatever to do, really? */
+ flog(LOG_ERR, "kevent(EV_ADD, EVFILT_READ) on fd %i: %s", bl->fd, strerror(errno));
+ return(-1);
+ }
+ }
+ if((bl->ev & EV_WRITE) && !(prev & EV_WRITE)) {
+ evd = (struct kevent) {
+ .flags = EV_ADD,
+ .ident = bl->fd,
+ .filter = EVFILT_WRITE,
+ };
+ if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) {
+ /* XXX?! Whatever to do, really? */
+ flog(LOG_ERR, "kevent(EV_ADD, EVFILT_WRITE) on fd %i: %s", bl->fd, strerror(errno));
+ return(-1);
+ }
+ }
+ bl->n2 = fdlist[bl->fd];
+ bl->p2 = NULL;
+ if(fdlist[bl->fd] != NULL)
+ fdlist[bl->fd]->p2 = bl;
+ fdlist[bl->fd] = bl;
+ bl->reg = 1;
+ return(0);
+}
+
+static void remfd(struct blocker *bl)
+{
+ struct blocker *o;
+ struct kevent evd;
+ int left;
+
+ if(!bl->reg)
+ return;
+ if(bl->n2)
+ bl->n2->p2 = bl->p2;
+ if(bl->p2)
+ bl->p2->n2 = bl->n2;
+ if(bl == fdlist[bl->fd])
+ fdlist[bl->fd] = bl->n2;
+ for(left = 0, o = fdlist[bl->fd]; o; o = o->n2)
+ left |= o->ev;
+ if((bl->ev & EV_READ) && !(left & EV_READ)) {
+ evd = (struct kevent) {
+ .flags = EV_DELETE,
+ .ident = bl->fd,
+ .filter = EVFILT_READ,
+ };
+ if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) {
+ /* XXX?! Whatever to do, really? */
+ flog(LOG_ERR, "kevent(EV_DELETE, EVFILT_READ) on fd %i: %s", bl->fd, strerror(errno));
+ }
+ }
+ if((bl->ev & EV_WRITE) && !(left & EV_WRITE)) {
+ evd = (struct kevent) {
+ .flags = EV_DELETE,
+ .ident = bl->fd,
+ .filter = EVFILT_WRITE,
+ };
+ if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) {
+ /* XXX?! Whatever to do, really? */
+ flog(LOG_ERR, "kevent(EV_DELETE, EVFILT_WRITE) on fd %i: %s", bl->fd, strerror(errno));
+ }
+ }
+ bl->reg = 0;
+}
+
+int block(int fd, int ev, time_t to)
+{
+ struct blocker *bl;
+ int rv;
+
+ omalloc(bl);
+ bl->fd = fd;
+ bl->ev = ev;
+ if(to > 0)
+ bl->to = time(NULL) + to;
+ bl->th = current;
+ if((qfd >= 0) && regfd(bl)) {
+ free(bl);
+ return(-1);
+ }
+ bl->n = blockers;
+ if(blockers)
+ blockers->p = bl;
+ blockers = bl;
+ rv = yield();
+ if(bl->n)
+ bl->n->p = bl->p;
+ if(bl->p)
+ bl->p->n = bl->n;
+ if(bl == blockers)
+ blockers = bl->n;
+ remfd(bl);
+ free(bl);
+ return(rv);
+}
+
+int ioloop(void)
+{
+ struct blocker *bl, *nbl;
+ struct kevent evs[16];
+ int i, fd, nev, ev;
+ time_t now, timeout;
+ struct timespec *toval;
+
+ exitstatus = 0;
+ qfd = kqueue();
+ fcntl(qfd, F_SETFD, FD_CLOEXEC);
+ for(bl = blockers; bl; bl = nbl) {
+ nbl = bl->n;
+ if(regfd(bl))
+ resume(bl->th, -1);
+ }
+ while(blockers != NULL) {
+ timeout = 0;
+ for(bl = blockers; bl; bl = bl->n) {
+ if((bl->to != 0) && ((timeout == 0) || (timeout > bl->to)))
+ timeout = bl->to;
+ }
+ now = time(NULL);
+ if(timeout == 0)
+ toval = NULL;
+ else if(timeout > now)
+ toval = &(struct timespec){.tv_sec = timeout - now};
+ else
+ toval = &(struct timespec){.tv_sec = 1};
+ if(exitstatus)
+ break;
+ nev = kevent(qfd, NULL, 0, evs, sizeof(evs) / sizeof(*evs), toval);
+ if(nev < 0) {
+ if(errno != EINTR) {
+ flog(LOG_CRIT, "ioloop: kevent errored out: %s", strerror(errno));
+ /* To avoid CPU hogging in case it's bad, which it
+ * probably is. */
+ sleep(1);
+ }
+ continue;
+ }
+ for(i = 0; i < nev; i++) {
+ fd = (int)evs[i].ident;
+ ev = (evs[i].filter == EVFILT_READ)?EV_READ:EV_WRITE;
+ for(bl = fdlist[fd]; bl; bl = nbl) {
+ nbl = bl->n2;
+ if(ev & bl->ev)
+ resume(bl->th, ev);
+ }
+ }
+ now = time(NULL);
+ for(bl = blockers; bl; bl = nbl) {
+ nbl = bl->n;
+ if((bl->to != 0) && (bl->to <= now))
+ resume(bl->th, 0);
+ }
+ }
+ for(bl = blockers; bl; bl = bl->n)
+ remfd(bl);
+ close(qfd);
+ qfd = -1;
+ return(exitstatus);
+}
+
+void exitioloop(int status)
+{
+ exitstatus = status;
+}
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
#include <log.h>
#include <utils.h>
#include <mt.h>
return(0);
}
+#if defined(HAVE_GLIBC_STDIO)
static cookie_io_functions_t iofuns = {
.read = mtread,
.write = mtwrite,
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
return(ret);
}
+#elif defined(HAVE_BSD_STDIO)
+static int bsd2mtread(void *cookie, char *buf, int len)
+{
+ return(mtread(cookie, buf, len));
+}
+
+static int bsd2mtwrite(void *cookie, const char *buf, int len)
+{
+ return(mtwrite(cookie, buf, len));
+}
+
+FILE *mtstdopen(int fd, int issock, int timeout, char *mode)
+{
+ struct stdiofd *d;
+ FILE *ret;
+ int r, w;
+
+ if(!strcmp(mode, "r")) {
+ r = 1; w = 0;
+ } else if(!strcmp(mode, "w")) {
+ r = 0; w = 1;
+ } else if(!strcmp(mode, "r+")) {
+ r = w = 1;
+ } else {
+ return(NULL);
+ }
+ omalloc(d);
+ d->fd = fd;
+ d->sock = issock;
+ d->timeout = timeout;
+ ret = funopen(d, r?bsd2mtread:NULL, w?bsd2mtwrite:NULL, NULL, mtclose);
+ if(!ret)
+ free(d);
+ else
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+ return(ret);
+}
+#else
+#error "No stdio implementation for this system"
+#endif
return(sprintf3("%s, %i %s %i %02i:%02i:%02i GMT", days[(tm->tm_wday + 6) % 7], tm->tm_mday, months[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec));
}
+static int gtoi(char *bstr, regmatch_t g)
+{
+ int i, n;
+
+ for(i = g.rm_so, n = 0; i < g.rm_eo; i++)
+ n = (n * 10) + (bstr[i] - '0');
+ return(n);
+}
+
+static int gstrcmp(char *bstr, regmatch_t g, char *str)
+{
+ if(g.rm_eo - g.rm_so != strlen(str))
+ return(1);
+ return(strncasecmp(bstr + g.rm_so, str, g.rm_eo - g.rm_so));
+}
+
time_t parsehttpdate(char *date)
{
static regex_t *spec = NULL;
struct tm tm;
int tz;
- int gtoi(regmatch_t g)
- {
- int i, n;
-
- for(i = g.rm_so, n = 0; i < g.rm_eo; i++)
- n = (n * 10) + (date[i] - '0');
- return(n);
- }
-
- int gstrcmp(regmatch_t g, char *str) {
- if(g.rm_eo - g.rm_so != strlen(str))
- return(1);
- return(strncasecmp(date + g.rm_so, str, g.rm_eo - g.rm_so));
- }
-
if(spec == NULL) {
omalloc(spec);
if(regcomp(spec, "^[A-Z]{3}, +([0-9]+) +([A-Z]{3}) +([0-9]+) +([0-9]{2}):([0-9]{2}):([0-9]{2}) +(([A-Z]+)|[+-]([0-9]{2})([0-9]{2}))$", REG_EXTENDED | REG_ICASE)) {
}
if(regexec(spec, date, 11, g, 0))
return(0);
- tm.tm_mday = gtoi(g[1]);
- tm.tm_year = gtoi(g[3]) - 1900;
- tm.tm_hour = gtoi(g[4]);
- tm.tm_min = gtoi(g[5]);
- tm.tm_sec = gtoi(g[6]);
+ tm.tm_mday = gtoi(date, g[1]);
+ tm.tm_year = gtoi(date, g[3]) - 1900;
+ tm.tm_hour = gtoi(date, g[4]);
+ tm.tm_min = gtoi(date, g[5]);
+ tm.tm_sec = gtoi(date, g[6]);
tm.tm_mon = -1;
for(i = 0; i < 12; i++) {
- if(!gstrcmp(g[2], months[i])) {
+ if(!gstrcmp(date, g[2], months[i])) {
tm.tm_mon = i;
break;
}
return(0);
if(g[8].rm_so > 0) {
- if(!gstrcmp(g[8], "GMT"))
+ if(!gstrcmp(date, g[8], "GMT"))
tz = 0;
else
return(0);
} else if((g[9].rm_so > 0) && (g[10].rm_so > 0)) {
- tz = gtoi(g[9]) * 3600 + gtoi(g[10]) * 60;
+ tz = gtoi(date, g[9]) * 3600 + gtoi(date, g[10]) * 60;
if(date[g[7].rm_so] == '-')
tz = -tz;
} else {
/errlogger
/psendfile
/httimed
+/httrcall
bin_PROGRAMS = htparser sendfile callcgi patplex userplex htls \
callscgi accesslog htextauth callfcgi multifscgi \
- errlogger httimed psendfile
+ errlogger httimed psendfile httrcall
htparser_SOURCES = htparser.c htparser.h plaintcp.c ssl-gnutls.c
static struct timeval now;
static volatile int reopen = 0;
-static void qputs(char *s, FILE *o)
+static void qputs(char *sp, FILE *o)
{
+ unsigned char *s = (unsigned char *)sp;
+
for(; *s; s++) {
if(*s == '\"') {
fputs("\\\"", o);
} else if(*s == '\t') {
fputs("\\t", o);
} else if((*s < 32) || (*s >= 128)) {
- fprintf(o, "\\x%02x", (int)(unsigned char)*s);
+ fprintf(o, "\\x%02x", (int)*s);
} else {
fputc(*s, o);
}
return(sstrdup(file));
}
-static pid_t forkchild(int inpath, char *prog, char *file, char *method, char *url, char *rest, int *infd, int *outfd)
+static pid_t forkchild(int inpath, char **prog, char *file, char *method, char *url, char *rest, int *infd, int *outfd)
{
char *qp, **env, *name;
int inp[2], outp[2];
pid_t pid;
char *pi;
+ int (*execfun)(const char *, char *const []);
pipe(inp);
pipe(outp);
* This is (understandably) missing from the CGI
* specification, but PHP seems to require it.
*/
- putenv(sprintf2("SCRIPT_FILENAME=%s", absolutify(file)));
- if(inpath)
- execlp(prog, prog, file, NULL);
- else
- execl(prog, prog, file, NULL);
+ execfun = inpath?execvp:execv;
+ if(file != NULL)
+ putenv(sprintf2("SCRIPT_FILENAME=%s", absolutify(file)));
+ execfun(prog[0], prog);
exit(127);
}
close(inp[0]);
static void usage(void)
{
- flog(LOG_ERR, "usage: callcgi [-c] [-p PROGRAM] METHOD URL REST");
+ flog(LOG_ERR, "usage: callcgi [-c] [-p PROGRAM] [-P PROGRAM ARGS... ;] METHOD URL REST");
}
int main(int argc, char **argv, char **envp)
{
int c;
- char *file, *prog, *sp;
- int inpath, cd;
+ char *file, *sp;
+ struct charvbuf prog;
+ int inpath, addfile, cd;
int infd, outfd;
FILE *in, *out;
char **headers;
environ = envp;
signal(SIGPIPE, SIG_IGN);
- prog = NULL;
+ bufinit(prog);
inpath = 0;
+ addfile = 1;
cd = 0;
- while((c = getopt(argc, argv, "cp:")) >= 0) {
+ while((c = getopt(argc, argv, "cp:P:")) >= 0) {
switch(c) {
case 'c':
cd = 1;
break;
case 'p':
- prog = optarg;
+ bufadd(prog, optarg);
+ inpath = 1;
+ break;
+ case 'P':
+ prog.d = 0;
+ bufadd(prog, optarg);
+ while(1) {
+ if(optind >= argc) {
+ flog(LOG_ERR, "callcgi: unterminated argument list for -P");
+ exit(1);
+ }
+ if(!strcmp(argv[optind], ";")) {
+ optind++;
+ break;
+ }
+ bufadd(prog, argv[optind++]);
+ }
+ if(prog.d == 0) {
+ flog(LOG_ERR, "callcgi: -P option needs at least a program name");
+ exit(1);
+ }
inpath = 1;
+ addfile = 0;
break;
default:
usage();
usage();
exit(1);
}
- if((file = getenv("REQ_X_ASH_FILE")) == NULL) {
+ if(((file = getenv("REQ_X_ASH_FILE")) == NULL) && (prog.d == 0)) {
flog(LOG_ERR, "callcgi: needs to be called with the X-Ash-File header");
exit(1);
}
}
}
- if(prog == NULL)
- prog = file;
- child = forkchild(inpath, prog, file, argv[optind], argv[optind + 1], argv[optind + 2], &infd, &outfd);
+ if(prog.d == 0)
+ bufadd(prog, file);
+ if(addfile && (file != NULL))
+ bufadd(prog, file);
+ bufadd(prog, NULL);
+ child = forkchild(inpath, prog.b, file, argv[optind], argv[optind + 1], argv[optind + 2], &infd, &outfd);
in = fdopen(infd, "w");
passdata(stdin, in); /* Ignore errors, perhaps? */
fclose(in);
kill(child, SIGINT);
if(waitpid(child, &estat, 0) == child) {
if(WCOREDUMP(estat))
- flog(LOG_WARNING, "CGI handler `%s' dumped core", prog);
+ flog(LOG_WARNING, "CGI handler `%s' dumped core", prog.b[0]);
if(WIFEXITED(estat) && !WEXITSTATUS(estat))
return(0);
else
#include <sys/un.h>
#include <netinet/in.h>
#include <netdb.h>
-#include <sys/signal.h>
+#include <signal.h>
#include <errno.h>
#ifdef HAVE_CONFIG_H
#include <sys/un.h>
#include <netinet/in.h>
#include <netdb.h>
-#include <sys/signal.h>
+#include <signal.h>
#include <errno.h>
#ifdef HAVE_CONFIG_H
--- /dev/null
+/*
+ ashd - A Sane HTTP Daemon
+ Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <utils.h>
+#include <log.h>
+#include <req.h>
+#include <proc.h>
+#include <resp.h>
+
+static char **prog;
+
+static void serve(struct hthead *req, int fd)
+{
+ if(stdforkserve(prog, req, fd, NULL, NULL) < 0)
+ simpleerror(fd, 500, "Server Error", "The server appears to be overloaded.");
+}
+
+static void chldhandler(int sig)
+{
+ pid_t pid;
+ int st;
+
+ while((pid = waitpid(-1, &st, WNOHANG)) > 0) {
+ if(WCOREDUMP(st))
+ flog(LOG_WARNING, "child process %i dumped core", pid);
+ }
+}
+
+static void usage(FILE *out)
+{
+ fprintf(out, "usage: httrcall [-h] PROGRAM [ARGS...]\n");
+}
+
+int main(int argc, char **argv)
+{
+ int c;
+ struct hthead *req;
+ int fd;
+
+ while((c = getopt(argc, argv, "+h")) >= 0) {
+ switch(c) {
+ case 'h':
+ usage(stdout);
+ exit(0);
+ default:
+ usage(stderr);
+ exit(1);
+ }
+ }
+ if(argc < optind - 1) {
+ usage(stderr);
+ exit(1);
+ }
+ prog = argv + optind;
+ signal(SIGCHLD, chldhandler);
+ while(1) {
+ if((fd = recvreq(0, &req)) < 0) {
+ if(errno == EINTR)
+ continue;
+ if(errno != 0)
+ flog(LOG_ERR, "recvreq: %s", strerror(errno));
+ break;
+ }
+ serve(req, fd);
+ freehthead(req);
+ close(fd);
+ }
+ return(0);
+}