Hi, I have implemented an SCGI patch for mathopd-1.6b9.
You use it with: Specials { SCGI { .scgi }} where the .scgi file is a unix domain socket to the SCGI application. Also, the SCGI application should have a large enough listen queue to accomodate the number of simultaneous connections configured in mathopd, or there is a possibility to get 503 code errors under load. The idea is this: * process_cgi was renamed into process_cgi_scgi. It builds the CGI environment as before, then calls either process_cgi or process_scgi. * process_cgi forkexecs the CGI script. The master process keeps a socketpair to talk with it. * process_scgi connects to an existing SCGI unix socket socket, and the master process keeps that socket to talk with it. The client_input pool is prefilled with the CGI environment variables converted to SCGI format. * calling writetochild directly at the end of process_scgi is not necessary for correctness, but I found out that it made a large difference (35%) in performance when the SCGI serves small (3KB) cached replies. * I modified get_path_info to consider cases where the open call returns ENXIO (as happens with unix sockets). When this happens, the finfo field of the request is not filled in. A new field 'valid_finfo' was added to track the validity of the finfo field. P.S. the patch also backports easily to 1.5 based versions. Hope this helps, -- Michel "Walken" Lespinasse A program is never fully debugged until the last user dies.
diff -ru mathopd-1.6b9/src/cgi.c mathopd-1.6b9-scgi/src/cgi.c --- mathopd-1.6b9/src/cgi.c 2004-04-25 12:08:57.000000000 -0700 +++ mathopd-1.6b9-scgi/src/cgi.c 2008-09-08 01:15:54.000000000 -0700 @@ -45,6 +45,7 @@ #include <stdio.h> #include <unistd.h> #include <ctype.h> +#include <sys/un.h> #include "mathopd.h" struct cgi_parameters { @@ -195,19 +196,24 @@ return 0; } -static int make_cgi_envp(struct request *r, struct cgi_parameters *cp) +static int make_cgi_envp(struct request *r, struct cgi_parameters *cp, int scgi) { char t[16]; struct simple_list *e; char path_translated[PATHLEN]; char *tmp; + tmp = r->in_content_length; + if (scgi && !tmp) + tmp = "0"; // SCGI scripts always want a CONTENT_LENGTH. + if (add("CONTENT_LENGTH", tmp, 0, cp) == -1) + return -1; + if (scgi && add("SCGI", "1", 0, cp) == -1) + return -1; if (add_http_vars(r, cp) == -1) return -1; if (add("GATEWAY_INTERFACE", "CGI/1.1", 0, cp) == -1) return -1; - if (add("CONTENT_LENGTH", r->in_content_length, 0, cp) == -1) - return -1; if (add("CONTENT_TYPE", r->in_content_type, 0, cp) == -1) return -1; if (r->user && r->user[0]) { @@ -305,9 +311,9 @@ return 0; } -static int init_cgi_env(struct request *r, struct cgi_parameters *cp) +static int init_cgi_env(struct request *r, struct cgi_parameters *cp, int scgi) { - if (make_cgi_envp(r, cp) == -1) + if (make_cgi_envp(r, cp, scgi) == -1) return -1; if (make_cgi_argv(r, cp) == -1) return -1; @@ -332,15 +338,40 @@ } } -int process_cgi(struct request *r) +static int process_scgi(struct request *, struct cgi_parameters *); +static int process_cgi(struct request *, struct cgi_parameters *); + +int process_cgi_scgi(struct request *r, int scgi) { struct cgi_parameters c; + int ok; + + c.cgi_envc = 0; + c.cgi_envp = 0; + c.cgi_argc = 0; + c.cgi_argv = 0; + if (init_cgi_env(r, &c, scgi) == -1) { + log_d("process_cgi_scgi: out of memory"); + destroy_parameters(&c); + r->status = 500; + return 0; + } + if (scgi) + ok = process_scgi (r, &c); + else + ok = process_cgi (r, &c); + destroy_parameters(&c); + return ok; +} + +int process_cgi(struct request *r, struct cgi_parameters *c) +{ uid_t u; gid_t g; int p[2], efd; pid_t pid; - if (r->curdir[0] == 0) { + if (r->curdir[0] == 0 || !r->valid_finfo) { r->status = 500; return 0; } @@ -374,19 +405,8 @@ r->status = 500; return 0; } - c.cgi_envc = 0; - c.cgi_envp = 0; - c.cgi_argc = 0; - c.cgi_argv = 0; - if (init_cgi_env(r, &c) == -1) { - log_d("process_cgi: out of memory"); - destroy_parameters(&c); - r->status = 500; - return 0; - } if (socketpair(AF_UNIX, SOCK_STREAM, 0, p) == -1) { lerror("socketpair"); - destroy_parameters(&c); r->status = 500; return 0; } @@ -397,29 +417,74 @@ if (efd == -1) { close(p[0]); close(p[1]); - destroy_parameters(&c); r->status = 500; return 0; } fcntl(efd, F_SETFD, FD_CLOEXEC); } else efd = -1; - pid = spawn(c.cgi_argv[0], c.cgi_argv, c.cgi_envp, p[1], efd, u, g, r->curdir); + pid = spawn(c->cgi_argv[0], c->cgi_argv, c->cgi_envp, p[1], efd, u, g, r->curdir); if (efd != -1) close(efd); close(p[1]); if (pid == -1) { close(p[0]); - destroy_parameters(&c); r->cn->keepalive = 0; r->status = 503; return 0; } fcntl(p[0], F_SETFL, O_NONBLOCK); init_child(r->cn, p[0]); - destroy_parameters(&c); if (debug) log_d("process_cgi: %d", p[0]); r->forked = 1; return -1; } + +int process_scgi(struct request *r, struct cgi_parameters *c) +{ + int sock; + struct sockaddr_un addr; + char **p; + int n; + struct pool *scgi; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + fcntl(sock, F_SETFL, O_NONBLOCK); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, r->path_translated, sizeof(addr.sun_path)); + addr.sun_path[sizeof(addr.sun_path)-1] = 0; + if (connect(sock, (struct sockaddr *)&addr, sizeof addr) == -1) { + log_d("scgi: could not connect to %s", addr.sun_path); + lerror("connect"); + r->status = 503; + return 0; + } + + n = 0; + for (p = c->cgi_envp; *p != NULL; p++) + if (strchr(*p, '=')) + n += strlen (*p) + 1; + + scgi = &r->cn->client_input; + if (scgi->floor + n + 16 > scgi->ceiling) { + log_d("scgi: ScriptBufSize is too small"); + r->status = 500; + return 0; + } + + init_child(r->cn, sock); + pool_print (scgi, "%u:", n); + for (p = c->cgi_envp; *p != NULL; p++) { + char * eq = strchr(*p, '='); + if (eq) { + *eq = 0; + pool_print (scgi, "%s%c%s%c", *p, 0, eq+1, 0); + } + } + pool_print (scgi, ","); + writetochild (r->cn); + + r->forked = 1; + return -1; +} diff -ru mathopd-1.6b9/src/mathopd.h mathopd-1.6b9-scgi/src/mathopd.h --- mathopd-1.6b9/src/mathopd.h 2007-07-07 09:04:12.000000000 -0700 +++ mathopd-1.6b9-scgi/src/mathopd.h 2008-09-08 00:00:19.000000000 -0700 @@ -50,6 +50,7 @@ #endif #define CGI_MAGIC_TYPE "CGI" +#define SCGI_MAGIC_TYPE "SCGI" #define IMAP_MAGIC_TYPE "Imagemap" #define REDIRECT_MAGIC_TYPE "Redirect" #define DUMP_MAGIC_TYPE "Dump" @@ -251,6 +252,7 @@ int method; int status; struct control *c; + int valid_finfo; struct stat finfo; int isindex; const char *error_file; @@ -421,7 +423,7 @@ /* cgi */ -extern int process_cgi(struct request *); +extern int process_cgi_scgi(struct request *, int); /* dump */ @@ -457,6 +459,7 @@ extern int init_cgi_headers(void); extern void pipe_run(struct connection *); extern void init_child(struct connection *, int); +extern int writetochild(struct connection *); extern int setup_child_pollfds(int, struct connection *); #if defined LINUX_SENDFILE || defined FREEBSD_SENDFILE diff -ru mathopd-1.6b9/src/request.c mathopd-1.6b9-scgi/src/request.c --- mathopd-1.6b9/src/request.c 2007-07-21 03:41:13.000000000 -0700 +++ mathopd-1.6b9-scgi/src/request.c 2008-09-08 00:11:04.000000000 -0700 @@ -323,6 +323,7 @@ lerror("fstat"); return -1; } + r->valid_finfo = 1; close_rfd(r); r->cn->rfd = fd; return 0; @@ -331,7 +332,7 @@ static int get_path_info(struct request *r) { char *p, *pa, *end, *cp, *start, *cds; - int fd, first; + int fd, first, found; size_t m; m = r->location_length; @@ -343,7 +344,7 @@ pa = r->path_args; *pa = 0; cp = end; - first = 0; + first = found = 0; while (cp >= start && cp[-1] == '/') --cp; while (cp >= start) { @@ -357,12 +358,16 @@ case ENOENT: case ENOTDIR: break; + case ENXIO: + found = 1; + break; default: log_d("cannot open %s", p); lerror("open"); return -1; } else { + found = 1; if (assign_rfd(r, fd) == -1) { close(fd); return -1; @@ -374,9 +379,9 @@ } if (cp != end) *cp = '/'; - if (fd != -1) { + if (found) { strcpy(pa, cp); - if (S_ISDIR(r->finfo.st_mode)) + if (r->valid_finfo && S_ISDIR(r->finfo.st_mode)) *cp++ = '/'; else if (first) { cds = strrchr(r->curdir, '/'); @@ -591,7 +596,7 @@ static int process_external(struct request *r) { r->num_content = -1; - return process_cgi(r); + return process_cgi_scgi(r, 0); } static int process_special(struct request *r) @@ -601,7 +606,9 @@ ct = r->content_type; r->num_content = -1; if (!strcasecmp(ct, CGI_MAGIC_TYPE)) - return process_cgi(r); + return process_cgi_scgi(r, 0); + if (!strcasecmp(ct, SCGI_MAGIC_TYPE)) + return process_cgi_scgi(r, 1); if (r->status) return 0; if (r->method == M_POST) { @@ -661,6 +668,13 @@ static int process_fd(struct request *r) { + if (!r->valid_finfo || !S_ISREG(r->finfo.st_mode)) { + close_rfd(r); + log_d("%s is not a regular file", r->path_translated); + r->error_file = r->c->error_404_file; + r->status = 404; + return 1; + } if (r->path_args[0] && r->c->path_args_ok == 0 && (r->path_args[1] || r->isindex == 0)) { close_rfd(r); if (debug) @@ -919,7 +933,7 @@ r->status = 404; return 1; } - if (S_ISDIR(r->finfo.st_mode)) { + if (r->valid_finfo && S_ISDIR(r->finfo.st_mode)) { close_rfd(r); if (r->status) return 0; @@ -953,13 +967,6 @@ r->status = 404; return 1; } - if (!S_ISREG(r->finfo.st_mode)) { - close_rfd(r); - log_d("%s is not a regular file", r->path_translated); - r->error_file = r->c->error_404_file; - r->status = 404; - return 1; - } if (get_mime(r, r->path_translated) == -1) { close_rfd(r); log_d("get_mime failed for %s", r->path_translated); @@ -1612,6 +1619,7 @@ r->status = 0; r->isindex = 0; r->c = 0; + r->valid_finfo = 0; r->error_file = 0; r->user[0] = 0; r->location_length = 0; diff -ru mathopd-1.6b9/src/stub.c mathopd-1.6b9-scgi/src/stub.c --- mathopd-1.6b9/src/stub.c 2007-07-07 09:04:12.000000000 -0700 +++ mathopd-1.6b9-scgi/src/stub.c 2008-09-08 00:11:56.000000000 -0700 @@ -404,7 +404,7 @@ return 0; } -static int writetochild(struct connection *p) +int writetochild(struct connection *p) { size_t bytestowrite; ssize_t r;