Merge branch 'master' of fludd.seatribe.se:/usr/local/src/ashd
authorFredrik Tolf <fredrik@dolda2000.com>
Sun, 2 Mar 2014 00:26:03 +0000 (01:26 +0100)
committerFredrik Tolf <fredrik@dolda2000.com>
Sun, 2 Mar 2014 00:26:03 +0000 (01:26 +0100)
19 files changed:
ChangeLog
configure.in
doc/Makefile.am
doc/htextauth.doc
doc/httrcall.doc [new file with mode: 0644]
etc/extauth/mkhtpasswd [new file with mode: 0755]
etc/extauth/vhtpasswd [new file with mode: 0755]
lib/Makefile.am
lib/cf.c
lib/mtio-kqueue.c [new file with mode: 0644]
lib/mtio.c
lib/resp.c
src/.gitignore
src/Makefile.am
src/accesslog.c
src/callcgi.c
src/callfcgi.c
src/callscgi.c
src/httrcall.c [new file with mode: 0644]

index ae73ffd..4036329 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Version 0.13:
+
+       * FreeBSD support.
+       * Improved dirplex/patplex configuration.
+       * Added httrcall.
+
 Version 0.12:
 
        * Support chunked request-bodies from clients.
index b8fa47b..80d2389 100644 (file)
@@ -1,6 +1,8 @@
-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
@@ -19,11 +21,29 @@ if test "$HAS_MAGIC" = no; then
        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
@@ -39,10 +59,30 @@ fi
 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
@@ -62,7 +102,7 @@ fi
 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
index 3c4fe3d..baf94f3 100644 (file)
@@ -1,18 +1,19 @@
 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
index b985492..8c3b6f7 100644 (file)
@@ -78,6 +78,16 @@ the client.
 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>
diff --git a/doc/httrcall.doc b/doc/httrcall.doc
new file mode 100644 (file)
index 0000000..c4fa48e
--- /dev/null
@@ -0,0 +1,38 @@
+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)
diff --git a/etc/extauth/mkhtpasswd b/etc/extauth/mkhtpasswd
new file mode 100755 (executable)
index 0000000..923ab07
--- /dev/null
@@ -0,0 +1,37 @@
+#!/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))
diff --git a/etc/extauth/vhtpasswd b/etc/extauth/vhtpasswd
new file mode 100755 (executable)
index 0000000..422206d
--- /dev/null
@@ -0,0 +1,34 @@
+#!/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)
index d48cfb6..30de42a 100644 (file)
@@ -2,11 +2,14 @@ lib_LIBRARIES = libht.a
 
 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
index bd4aba3..aae8485 100644 (file)
--- a/lib/cf.c
+++ b/lib/cf.c
@@ -355,26 +355,35 @@ static char **expandargs(struct stdchild *sd)
     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)) {
@@ -383,7 +392,7 @@ static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit
                /* 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;
@@ -406,7 +415,7 @@ static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit
        }
     } 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);
        }
diff --git a/lib/mtio-kqueue.c b/lib/mtio-kqueue.c
new file mode 100644 (file)
index 0000000..1235973
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+    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;
+}
index 3b728d0..70a0949 100644 (file)
@@ -16,6 +16,9 @@
     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>
@@ -24,9 +27,6 @@
 #include <errno.h>
 #include <sys/socket.h>
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
 #include <log.h>
 #include <utils.h>
 #include <mt.h>
@@ -107,6 +107,7 @@ static int mtclose(void *cookie)
     return(0);
 }
 
+#if defined(HAVE_GLIBC_STDIO)
 static cookie_io_functions_t iofuns = {
     .read = mtread,
     .write = mtwrite,
@@ -129,3 +130,43 @@ FILE *mtstdopen(int fd, int issock, int timeout, char *mode)
        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
index 25fb2cb..bae24bf 100644 (file)
@@ -190,6 +190,22 @@ char *fmthttpdate(time_t time)
     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;
@@ -200,21 +216,6 @@ time_t parsehttpdate(char *date)
     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)) {
@@ -225,15 +226,15 @@ time_t parsehttpdate(char *date)
     }
     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;
        }
@@ -242,12 +243,12 @@ time_t parsehttpdate(char *date)
        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 {
index 582f3e7..1ecc66e 100644 (file)
@@ -12,3 +12,4 @@
 /errlogger
 /psendfile
 /httimed
+/httrcall
index 5c4f895..5422e74 100644 (file)
@@ -2,7 +2,7 @@ SUBDIRS = dirplex
 
 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
 
index f17eebe..88b61b3 100644 (file)
@@ -46,8 +46,10 @@ static char *format;
 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);
@@ -58,7 +60,7 @@ static void qputs(char *s, FILE *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);
        }
index 2da1161..67a6d14 100644 (file)
@@ -85,12 +85,13 @@ static char *absolutify(char *file)
     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);
@@ -170,11 +171,10 @@ static pid_t forkchild(int inpath, char *prog, char *file, char *method, char *u
         * 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]);
@@ -336,14 +336,15 @@ static void sendheaders(char **headers, FILE *out)
 
 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;
@@ -353,17 +354,39 @@ int main(int argc, char **argv, char **envp)
     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();
@@ -375,7 +398,7 @@ int main(int argc, char **argv, char **envp)
        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);
     }
@@ -394,9 +417,12 @@ int main(int argc, char **argv, char **envp)
        }
     }
     
-    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);
@@ -412,7 +438,7 @@ int main(int argc, char **argv, char **envp)
        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
index 6a8b3f0..9f30f00 100644 (file)
@@ -37,7 +37,7 @@
 #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
index 0ad3203..b78827c 100644 (file)
@@ -32,7 +32,7 @@
 #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
diff --git a/src/httrcall.c b/src/httrcall.c
new file mode 100644 (file)
index 0000000..0d7d845
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+    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);
+}