Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37716804
en ru br
Репозитории ALT

Группа :: Система/Серверы
Пакет: fcgiwrap

 Главная   Изменения   Спек   Патчи   Исходники   Загрузить   Gear   Bugs and FR  Repocop 

pax_global_header00006660000000000000000000000064121034624750014516gustar00rootroot0000000000000052 comment=333ff9951b169f6a093608497b8b97f304365017
fcgiwrap-1.1.0/000075500000000000000000000000001210346247500133155ustar00rootroot00000000000000fcgiwrap-1.1.0/.gitignore000064400000000000000000000001221210346247500153000ustar00rootroot00000000000000*.cache
config.h
config.h.in
config.log
config.status
configure
fcgiwrap
Makefile
fcgiwrap-1.1.0/Makefile.in000064400000000000000000000014741210346247500153700ustar00rootroot00000000000000targetdir = $(DESTDIR)@prefix@@sbindir@
man8dir = $(DESTDIR)@prefix@@mandir@/man8
datarootdir =

.PHONY: clean distclean

all: fcgiwrap
install: all
install -d -m 755 $(targetdir)
install -m 755 fcgiwrap $(targetdir)
install -d -m 755 $(man8dir)
install -m 644 fcgiwrap.8 $(man8dir)
ifneq ("@systemdsystemunitdir@", "")
install -d -m 755 $(DESTDIR)@systemdsystemunitdir@
install -m 644 systemd/fcgiwrap.socket $(DESTDIR)@systemdsystemunitdir@
install -m 644 systemd/fcgiwrap.service $(DESTDIR)@systemdsystemunitdir@
endif

LDLIBS = -lfcgi @systemd_LIBS@
CFLAGS = @AM_CFLAGS@

fcgiwrap: fcgiwrap.c

#>+ 21
clean:
-rm -f fcgiwrap

#>+ 21
distclean: clean
-rm -rf config.log config.status autom4te.cache
-rm -f configure Makefile config.h config.h.in

#>+ 21
uninstall:
rm -f $(targetdir)/fcgiwrap $(man8dir)/fcgiwrap.8
fcgiwrap-1.1.0/README.rst000064400000000000000000000037171210346247500150140ustar00rootroot00000000000000========
fcgiwrap
========
:Info: Simple FastCGI wrapper for CGI scripts
:Homepage: http://nginx.localdomain.pl/wiki/FcgiWrap
:Author: Grzegorz Nosek <root@localdomain.pl>
:Contributors: W-Mark Kubacki <wmark@hurrikane.de>
Jordi Mallach <jordi@debian.org>

Features
========
- very lightweight (84KB of private memory per instance)
- fixes broken CR/LF in headers
- handles environment in a sane way (CGI scripts get HTTP-related env. vars from FastCGI parameters and inherit all the others from ``fcgiwrap``'s environment)
- no configuration, so you can run several sites off the same ``fcgiwrap`` pool
- passes CGI stderr output to ``fcgiwrap``'s stderr or FastCGI stderr stream

Installation
============

requirements
------------
``Makefile`` and ``configure`` script is generated by GNU *autotools*. Therefore you need the latter.

``fcgiwrap`` links to *dev-libs/fcgi* which can be obtained from http://www.fastcgi.com/ .

``fcgiwrap`` also uses (but not requires) ``systemd`` for socket activation.

procedure
---------
To install::

autoreconf -i
./configure
make
make install

*fcgiwrap* will be copied to ``/usr/local/sbin/fcgiwrap`` if you did not set
``--prefix`` for configure or ``DESTDIR`` for the makefile.

usage
-----
Most probably you will want ``fcgiwrap`` be launched by `www-servers/spawn-fcgi <http://redmine.lighttpd.net/projects/spawn-fcgi>`_. Or you could use the author's Perl launcher - see the homepage for that.

There are two modes of ``fcgiwrap`` operation:
- when *SCRIPT_FILENAME* is set, its value is treated as the script name and executed directly.
- otherwise, *DOCUMENT_ROOT* and *SCRIPT_NAME* are concatenated and split back again into the script name and *PATH_INFO*. For example, given a *DOCUMENT_ROOT* of ``/www/cgi`` and *SCRIPT_NAME* of ``/subdir/example.cgi/foobar``, ``fcgiwrap`` will execute ``/www/cgi/subdir/example.cgi`` with *PATH_INFO* of ``/foobar`` (assuming ``example.cgi`` exists and is executable).
fcgiwrap-1.1.0/configure.ac000064400000000000000000000042531210346247500156070ustar00rootroot00000000000000# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ(2.61)
AC_INIT([fcgiwrap], [1.1.0], [root@localdomain.pl])
AM_CFLAGS="-std=gnu99 -Wall -Wextra -Werror -pedantic"
if test x"$CFLAGS" = x""; then
AM_CFLAGS="$AM_CFLAGS -O2 -g3"
else
AM_CFLAGS="$AM_CFLAGS $CFLAGS"
fi

AC_SUBST([AM_CFLAGS])

# Checks for programs.
AC_PROG_CC
PKG_PROG_PKG_CONFIG

# Create the config.h.
AC_CONFIG_HEADERS([config.h])

# Checks for libraries.
AC_CHECK_LIB([fcgi], [FCGX_Init],, [AC_MSG_ERROR([FastCGI library is missing])])

# systemd support.
AC_ARG_WITH([systemd],
AS_HELP_STRING([--with-systemd], [support systemd socket activation]),
[], [with_systemd=check])
have_systemd=no
if test "x$with_systemd" != "xno"; then
PKG_CHECK_MODULES(systemd, [libsystemd-daemon],
[AC_DEFINE(HAVE_SYSTEMD, 1, [Define if systemd is available])
have_systemd=yes],
have_systemd=no)
if test "x$have_systemd" = xno -a "x$with_systemd" = xyes; then
AC_MSG_ERROR([systemd support requested but libraries not found])
fi
fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test "x$have_systemd" = "xyes"])

AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
[], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
if test "x$with_systemdsystemunitdir" != xno -a "x$have_systemd" != xno; then
AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
fi

# Checks for header files.
AC_CHECK_HEADERS([fcntl.h],, [AC_MSG_ERROR([fcntl.h header missing])])
AC_CHECK_HEADERS([limits.h stdlib.h string.h unistd.h],, [AC_MSG_ERROR([at least one important system header file is missing])])

# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T

# Checks for library functions.
AC_FUNC_FORK
AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
AC_FUNC_MALLOC
AC_CHECK_FUNCS([strchr strdup strrchr])
AC_CHECK_FUNCS([dup2 putenv select setenv strerror],, [AC_MSG_ERROR([seems as if your libraries don't provide an expected function])])

AC_CONFIG_FILES([Makefile])
AC_OUTPUT
fcgiwrap-1.1.0/fcgiwrap.8000064400000000000000000000067421210346247500152210ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*-
.TH FCGIWRAP 8 "Jun 3, 2010"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh disable hyphenation
.\" .hy enable hyphenation
.\" .ad l left justify
.\" .ad b justify to both left and right margins
.\" .nf disable filling
.\" .fi enable filling
.\" .br insert line break
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
fcgiwrap \- serve CGI applications over FastCGI
.SH SYNOPSIS
.B fcgiwrap
.I [OPTIONS]

.SH DESCRIPTION
\fBfcgiwrap\fP is a simple server for running CGI applications over
FastCGI. It hopes to provide clean CGI support to Nginx (and other web servers
that may need it).

.SH OPTIONS
.TP
.B \-c \fInumber\fP
Number of fcgiwrap processes to prefork.
.TP
.B \-f
Redirect STDERR output from executed CGI through FastCGI so it shows in the web server
error log. Otherwise it would be returned on \fBfcgiwrap\fP's STDERR, which could be redirected.
If running through \fBspawn-fcgi\fP, \fBfcgiwrap\fP's STDERR is sent to /dev/null, so this option
provides a way to get that output back.
.TP
.B \-s \fIsocket_url\fP
A URL for the listen socket to bind to. By default \fBfcgiwrap\fP expects
a listen socket to be passed on file descriptor 0, matching the FastCGI convention.
The recommended way to deploy \fBfcgiwrap\fP is to run it under a process manager that
takes care of opening the socket. However, for simple configurations and one-off
tests this option may be used. Valid socket_urls include \fIunix:/path/to/unix/socket\fP,
\fItcp:dot.ted.qu.ad:port\fP and \fItcp6:[ipv6_addr]:port\fP.
.TP
.B \-h
Show a help message and exit.

.SH ENVIRONMENT
When running, \fBfcgiwrap\fP evaluates these environment variables set by
the web server calling an fcgi-script. The variables DOCUMENT_ROOT and
SCRIPT_NAME will be concatenated and the resulting executable run as CGI
script wrapped as FastCGI, with the remainder after the script name
available as PATH_INFO. To disable PATH_INFO mangling, set up your web
server to pass SCRIPT_FILENAME, which should contain the complete path to
the script. Then PATH_INFO will not be modified.

DOCUMENT_ROOT
.RS
directory which the script resides in
.RE
SCRIPT_NAME
.RS
actual executable
.RE
SCRIPT_FILENAME
.RS
complete path to CGI script. When set, overrides DOCUMENT_ROOT and SCRIPT_NAME

.SH EXAMPLE
The fastest way to see \fBfcgiwrap\fP do something is to launch it at the command line
like this:
.br
fcgiwrap \-s unix:/var/run/fcgiwrap.sock
.br
Apart from potential permission problems etc., it should be ready to accept FastCGI
requests and run CGI scripts.

Most probably you will want to launch \fBfcgiwrap\fP by
.I spawn-fcgi
using a configuration like this:

FCGI_SOCKET=/var/run/fcgiwrap.sock
.br
FCGI_PROGRAM=/usr/sbin/fcgiwrap
.br
FCGI_USER=nginx
.br
FCGI_GROUP=www
.br
FCGI_EXTRA_OPTIONS="\-M 0700"
.br
ALLOWED_ENV="PATH"

.I Nginx
can be configured to have the arbitrary CGI
.I cgit
run as FastCGI as follows:

location / {
.br
fastcgi_param DOCUMENT_ROOT /var/www/localhost/htdocs/cgit/;
.br
fastcgi_param SCRIPT_NAME cgit;
.br
fastcgi_pass unix:/var/run/fastcgi.sock;
.br
}

.SH AUTHOR
fcgiwrap was written by Grzegorz Nosek <root@localdomain.pl>
with contributions by W-Mark Kubacki <wmark@hurrikane.de>.
.PP
This manual page was written by Jordi Mallach <jordi@debian.org>
(with contributions by Grzegorz Nosek)
for the Debian project (and may be used by others).
fcgiwrap-1.1.0/fcgiwrap.c000064400000000000000000000421331210346247500152660ustar00rootroot00000000000000/*
* Copyright (c) 2007-2013 Grzegorz Nosek
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

#define NO_FCGI_DEFINES

#include "config.h"
#include <stdarg.h>
#include <fcgi_stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <limits.h>
#include <stdbool.h>
#include <sys/wait.h>
#include <ctype.h>

#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>

#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif

/* glibc doesn't seem to export it */
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX 108
#endif

extern char **environ;
static char * const * inherited_environ;

static const char * blacklisted_env_vars[] = {
"AUTH_TYPE",
"CONTENT_LENGTH",
"CONTENT_TYPE",
"GATEWAY_INTERFACE",
"PATH_INFO",
"PATH_TRANSLATED",
"QUERY_STRING",
"REMOTE_ADDR",
"REMOTE_HOST",
"REMOTE_IDENT",
"REMOTE_USER",
"REQUEST_METHOD",
"SCRIPT_NAME",
"SERVER_NAME",
"SERVER_PORT",
"SERVER_PROTOCOL",
"SERVER_SOFTWARE",
NULL,
};

static int stderr_to_fastcgi = 0;


#define FCGI_BUF_SIZE 4096

static int write_all(int fd, char *buf, size_t size)
{
size_t nleft = size;
while (nleft > 0) {
ssize_t nwritten = write(fd, buf, nleft);
if (nwritten < 0)
return nleft - size; /* zero or negative to indicate error */

buf += nwritten;
nleft -= nwritten;
}

return size;
}

#define MAX_VA_SENTINEL INT_MIN

static int max_va(int p1, ...)
{
va_list va;
int max = p1;
int p;

va_start(va, p1);
do {
p = va_arg(va, int);
if (p > max)
max = p;
} while (p != MAX_VA_SENTINEL);
va_end(va);

return max;
}

enum reply_state_t {
REPLY_STATE_INIT,
REPLY_STATE_HEADER,
REPLY_STATE_CR,
REPLY_STATE_LF,
REPLY_STATE_2CR,
REPLY_STATE_2LF,
REPLY_STATE_BODY,
REPLY_STATE_MAX
};

enum char_class_t {
CC_NORMAL,
CC_CR,
CC_LF,
CC_MAX
};

#define ACTION_MASK (15 << 4)
#define ACTION_EMIT 0
#define ACTION_ERROR (1 << 4)
#define ACTION_END (2 << 4)
#define ACTION_SKIP (3 << 4)
#define ACTION_EXTRA_CR (4 << 4)
#define ACTION_EXTRA_LF (5 << 4)

static const unsigned char header_state_machine[REPLY_STATE_MAX][CC_MAX] = {
[REPLY_STATE_INIT] = {
[CC_NORMAL] = REPLY_STATE_HEADER,
[CC_CR] = ACTION_ERROR,
[CC_LF] = ACTION_ERROR,
},
[REPLY_STATE_HEADER] = {
[CC_NORMAL] = REPLY_STATE_HEADER,
[CC_CR] = REPLY_STATE_CR,
[CC_LF] = REPLY_STATE_LF | ACTION_EXTRA_CR,
},
[REPLY_STATE_CR] = {
[CC_NORMAL] = REPLY_STATE_HEADER | ACTION_EXTRA_LF,
[CC_CR] = REPLY_STATE_CR | ACTION_SKIP,
[CC_LF] = REPLY_STATE_LF,
},
[REPLY_STATE_LF] = {
[CC_NORMAL] = REPLY_STATE_HEADER,
[CC_CR] = REPLY_STATE_2CR,
[CC_LF] = REPLY_STATE_2LF | ACTION_EXTRA_CR,
},
[REPLY_STATE_2CR] = {
[CC_NORMAL] = REPLY_STATE_BODY | ACTION_EXTRA_LF,
[CC_CR] = REPLY_STATE_CR | ACTION_SKIP,
[CC_LF] = REPLY_STATE_2LF,
},
[REPLY_STATE_2LF] = {
[CC_NORMAL] = REPLY_STATE_BODY | ACTION_END,
[CC_CR] = REPLY_STATE_BODY | ACTION_END,
[CC_LF] = REPLY_STATE_BODY | ACTION_END,
},
[REPLY_STATE_BODY] = {
[CC_NORMAL] = REPLY_STATE_BODY | ACTION_END,
[CC_CR] = REPLY_STATE_BODY | ACTION_END,
[CC_LF] = REPLY_STATE_BODY | ACTION_END,
},
};

struct fcgi_context {
int fd_stdin;
int fd_stdout;
int fd_stderr;
unsigned int reply_state;
pid_t cgi_pid;
};

static void fcgi_finish(struct fcgi_context *fc, const char* msg)
{
if (fc->reply_state == REPLY_STATE_INIT) {
FCGI_puts("Status: 502 Bad Gateway\nContent-type: text/plain\n");
FCGI_printf("An error occurred while %s\n", msg);
}

if (fc->fd_stdin >= 0) close(fc->fd_stdin);
if (fc->fd_stdout >= 0) close(fc->fd_stdout);
if (fc->fd_stderr >= 0) close(fc->fd_stderr);

if (fc->cgi_pid)
kill(SIGTERM, fc->cgi_pid);
}

static const char * fcgi_pass_fd(struct fcgi_context *fc, int *fdp, FCGI_FILE *ffp, char *buf, size_t bufsize)
{
ssize_t nread;
char *p = buf;
unsigned char cclass, next_state;

nread = read(*fdp, buf, bufsize);
if (nread > 0) {
while (p < buf + nread) {
if (*p == '\r') {
cclass = CC_CR;
} else if (*p == '\n') {
cclass = CC_LF;
} else {
cclass = CC_NORMAL;
}
next_state = header_state_machine[fc->reply_state][cclass];
fc->reply_state = next_state & ~ACTION_MASK;
switch(next_state & ACTION_MASK) {
case ACTION_ERROR:
return "parsing CGI reply";

case ACTION_END:
goto out_of_loop;

case ACTION_SKIP:
goto next_char;

case ACTION_EXTRA_CR:
if (FCGI_fputc('\r', ffp) == EOF) return "writing CGI reply";
break;

case ACTION_EXTRA_LF:
if (FCGI_fputc('\n', ffp) == EOF) return "writing CGI reply";
break;
}
if (FCGI_fputc(*p, ffp) == EOF) {
return "writing CGI reply";
}
next_char:
p++;
}
out_of_loop:
if (p < buf + nread) {
if (FCGI_fwrite(p, 1, buf + nread - p, ffp) != (size_t)(buf + nread - p)) {
return "writing CGI reply";
}
}
} else {
if (nread < 0) {
return "reading CGI reply";
}
close(*fdp);
*fdp = -1;
}

return NULL;
}

static const char * fcgi_pass_raw_fd(int *fdp, int fd_out, char *buf, size_t bufsize)
{
ssize_t nread;

nread = read(*fdp, buf, bufsize);
if (nread > 0) {
if (write_all(fd_out, buf, nread) != nread) {
return "writing CGI reply";
}
} else {
if (nread < 0) {
return "reading CGI reply";
}
close(*fdp);
*fdp = -1;
}
return NULL;
}

static bool fcgi_pass_request(struct fcgi_context *fc)
{
char buf[FCGI_BUF_SIZE];
ssize_t nread;

/* eat the whole request and pass it to CGI */
while ((nread = FCGI_fread(buf, 1, sizeof(buf), FCGI_stdin)) > 0) {
if (write_all(fc->fd_stdin, buf, nread) <= 0) {
fcgi_finish(fc, "reading the request");
return false;
}
}
close(fc->fd_stdin);
fc->fd_stdin = -1;

return true;
}

static void fcgi_pass(struct fcgi_context *fc)
{
char buf[FCGI_BUF_SIZE];
fd_set rset;
int maxfd = 1 + max_va(fc->fd_stdout, fc->fd_stderr, MAX_VA_SENTINEL);
int nready;
const char *err;

if (!fcgi_pass_request(fc))
return;

/* now pass CGI reply back */
while (fc->fd_stdout >= 0 || fc->fd_stderr >= 0) {
FD_ZERO(&rset);
if (fc->fd_stdout >= 0) FD_SET(fc->fd_stdout, &rset);
if (fc->fd_stderr >= 0) FD_SET(fc->fd_stderr, &rset);
nready = select(maxfd, &rset, NULL, NULL, NULL);
if (nready < 0) {
if (errno == EAGAIN) continue;
fcgi_finish(fc, "waiting for CGI reply");
return;
}
if (fc->fd_stdout >= 0 && FD_ISSET(fc->fd_stdout, &rset)) {
err = fcgi_pass_fd(fc, &fc->fd_stdout, FCGI_stdout, buf, sizeof(buf));
if (err) {
fcgi_finish(fc, err);
return;
}
}
if (fc->fd_stderr >= 0 && FD_ISSET(fc->fd_stderr, &rset)) {
if (stderr_to_fastcgi)
err = fcgi_pass_fd(fc, &fc->fd_stderr, FCGI_stderr, buf, sizeof(buf));
else
err = fcgi_pass_raw_fd(&fc->fd_stderr, 2, buf, sizeof(buf));
if (err) {
fcgi_finish(fc, err);
return;
}
}
}

fc->cgi_pid = 0;

fcgi_finish(fc, "reading CGI reply (no response received)");
}

static int check_file_perms(const char *path)
{
struct stat ls;
struct stat fs;

if (lstat(path, &ls) < 0) {
return -ENOENT;
} else if (S_ISREG(ls.st_mode)) {
if (ls.st_mode & S_IXUSR) {
return 0;
} else {
return -EACCES;
}
} else if (!S_ISLNK(ls.st_mode)) {
return -EACCES;
}

if (stat(path, &fs) < 0) {
return -ENOENT;
} else if (S_ISREG(fs.st_mode)) {
if (fs.st_mode & S_IXUSR) {
return 0;
} else {
return -EACCES;
}
} else {
return -EACCES;
}
}

static char *get_cgi_filename(void) /* and fixup environment */
{
int buflen = 1, docrootlen;
char *buf = NULL;
char *docroot, *scriptname, *p;

int rf_len;
char *pathinfo = NULL;

if ((p = getenv("SCRIPT_FILENAME"))) {
if (check_file_perms(p) != 0)
goto err;
return strdup(p);
}

if ((p = getenv("DOCUMENT_ROOT"))) {
docroot = p;
docrootlen = strlen(p);
buflen += docrootlen;
} else {
goto err;
}

if ((p = getenv("SCRIPT_NAME"))) {
buflen += strlen(p);
scriptname = p;
} else {
goto err;
}

buf = malloc(buflen);
if (!buf) goto err;

strcpy(buf, docroot);
strcpy(buf + docrootlen, scriptname);
pathinfo = strdup(buf);
if (!pathinfo) {
goto err;
}

while(1) {
switch(check_file_perms(buf)) {
case -EACCES:
goto err;
case 0:
rf_len = strlen(buf);
if (rf_len < buflen - 1) {
setenv("PATH_INFO", pathinfo + rf_len, 1);
setenv("SCRIPT_NAME", buf + docrootlen, 1);
} else {
unsetenv("PATH_INFO");
}
free(pathinfo);
return buf;
default:
p = strrchr(buf, '/');
if (!p) goto err;
*p = 0;
}
}

err:
free(pathinfo);
free(buf);
return NULL;
}

static int blacklisted_env(const char *var_name, const char *var_name_end)
{
const char **p;

if (var_name_end - var_name > 4 && !strncmp(var_name, "HTTP", 4)) {
/* HTTP_*, HTTPS */
return 1;
}

for (p = blacklisted_env_vars; *p; p++) {
if (!strcmp(var_name, *p)) {
return 1;
}
}

return 0;
}

static void inherit_environment(void)
{
char * const * p;
char *q;

for (p = inherited_environ; *p; p++) {
q = strchr(*p, '=');
if (!q) {
fprintf(stderr, "Suspect value in environment: %s\n", *p);
continue;
}
*q = 0;

if (!getenv(*p) && !blacklisted_env(*p, q)) {
*q = '=';
putenv(*p);
}

*q = '=';
}
}

static void cgi_error(const char *message, const char *reason, const char *filename)
{
printf("Status: %s\r\nContent-Type: text/plain\r\n\r\n%s\r\n",
message, message);
fflush(stdout);
if (filename) {
fprintf(stderr, "%s (%s)\n", reason, filename);
} else {
fputs(reason, stderr);
fputc('\n', stderr);
}
_exit(99);
}

static void handle_fcgi_request(void)
{
int pipe_in[2];
int pipe_out[2];
int pipe_err[2];
char *filename;
char *last_slash;
pid_t pid;

struct fcgi_context fc;

if (pipe(pipe_in) < 0) goto err_pipein;
if (pipe(pipe_out) < 0) goto err_pipeout;
if (pipe(pipe_err) < 0) goto err_pipeerr;

switch((pid = fork())) {
case -1:
goto err_fork;

case 0: /* child */
close(pipe_in[1]);
close(pipe_out[0]);
close(pipe_err[0]);

dup2(pipe_in[0], 0);
dup2(pipe_out[1], 1);
dup2(pipe_err[1], 2);

close(pipe_in[0]);
close(pipe_out[1]);
close(pipe_err[1]);

close(FCGI_fileno(FCGI_stdout));

signal(SIGCHLD, SIG_DFL);
signal(SIGPIPE, SIG_DFL);

filename = get_cgi_filename();
inherit_environment();
if (!filename)
cgi_error("403 Forbidden", "Cannot get script name, are DOCUMENT_ROOT and SCRIPT_NAME (or SCRIPT_FILENAME) set and is the script executable?", NULL);

last_slash = strrchr(filename, '/');
if (!last_slash)
cgi_error("403 Forbidden", "Script name must be a fully qualified path", filename);

*last_slash = 0;
if (chdir(filename) < 0)
cgi_error("403 Forbidden", "Cannot chdir to script directory", filename);

*last_slash = '/';

execl(filename, filename, (void *)NULL);
cgi_error("502 Bad Gateway", "Cannot execute script", filename);

default: /* parent */
close(pipe_in[0]);
close(pipe_out[1]);
close(pipe_err[1]);

fc.fd_stdin = pipe_in[1];
fc.fd_stdout = pipe_out[0];
fc.fd_stderr = pipe_err[0];
fc.reply_state = REPLY_STATE_INIT;
fc.cgi_pid = pid;

fcgi_pass(&fc);
}
return;

err_fork:
close(pipe_err[0]);
close(pipe_err[1]);

err_pipeerr:
close(pipe_out[0]);
close(pipe_out[1]);

err_pipeout:
close(pipe_in[0]);
close(pipe_in[1]);

err_pipein:

FCGI_puts("Status: 502 Bad Gateway\nContent-type: text/plain\n");
FCGI_puts("System error");
}

static void fcgiwrap_main(void)
{
signal(SIGCHLD, SIG_IGN);
signal(SIGPIPE, SIG_IGN);

inherited_environ = environ;

while (FCGI_Accept() >= 0) {
handle_fcgi_request();
}
}

static volatile sig_atomic_t nrunning;

static void sigchld_handler(int dummy)
{
int status;

while ((dummy = waitpid(-1, &status, WNOHANG)) != -1) {
/* sanity check */
if (nrunning > 0)
nrunning--;

/* we _should_ print something about the exit code
* but the sighandler context is _very_ bad for this
*/
}
}

static void prefork(int nchildren)
{
int startup = 1;

if (nchildren == 1) {
return;
}

signal(SIGCHLD, sigchld_handler);

while (1) {
while (nrunning < nchildren) {
pid_t pid = fork();
if (pid == 0) {
return;
} else if (pid != -1) {
nrunning++;
} else {
if (startup) {
fprintf(stderr, "Failed to prefork: %s\n", strerror(errno));
exit(1);
} else {
fprintf(stderr, "Failed to fork: %s\n", strerror(errno));
break;
}
}
}
startup = 0;
pause();
}
}

static int listen_on_fd(int fd) {
int one = 1;

if (listen(fd, 511) < 0) {
perror("Failed to listen");
return -1;
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0) {
perror("Failed to enable SO_REUSEADDR");
return -1;
}
if (dup2(fd, 0) < 0) {
perror("Failed to move socket to fd 0");
return -1;
}
if (close(fd) < 0) {
perror("Failed to close original socket");
return -1;
}

return 0;
}

static int setup_socket(char *url) {
char *p = url;
char *q;
int fd;
int port;
size_t sockaddr_size;

union {
struct sockaddr sa;
struct sockaddr_un sa_un;
struct sockaddr_in sa_in;
struct sockaddr_in6 sa_in6;
} sa;

if (!strncmp(p, "unix:", sizeof("unix:") - 1)) {
p += sizeof("unix:") - 1;

if (strlen(p) >= UNIX_PATH_MAX) {
fprintf(stderr, "Socket path too long, exceeds %d characters\n",
UNIX_PATH_MAX);
return -1;
}

sockaddr_size = sizeof sa.sa_un;
sa.sa_un.sun_family = AF_UNIX;
strcpy(sa.sa_un.sun_path, p);
} else if (!strncmp(p, "tcp:", sizeof("tcp:") - 1)) {
p += sizeof("tcp:") - 1;

q = strchr(p, ':');
if (!q) {
goto invalid_url;
}
port = atoi(q+1);
if (port <= 0 || port > 65535) {
goto invalid_url;
}
sockaddr_size = sizeof sa.sa_in;
sa.sa_in.sin_family = AF_INET;
sa.sa_in.sin_port = htons(port);
*q = 0;
if (inet_pton(AF_INET, p, &sa.sa_in.sin_addr) < 1) {
goto invalid_url;
}
} else if (!strncmp(p, "tcp6:[", sizeof("tcp6:[") - 1)) {
p += sizeof("tcp6:[") - 1;
q = strchr(p, ']');
if (!q || !q[0] || q[1] != ':') {
goto invalid_url;
}
port = atoi(q+2);
if (port <= 0 || port > 65535) {
goto invalid_url;
}
sockaddr_size = sizeof sa.sa_in6;
sa.sa_in6.sin6_family = AF_INET6;
sa.sa_in6.sin6_port = htons(port);
*q = 0;
if (inet_pton(AF_INET6, p, &sa.sa_in6.sin6_addr) < 1) {
goto invalid_url;
}
} else {
invalid_url:
fprintf(stderr, "Valid socket URLs are:\n"
"unix:/path/to/socket for Unix sockets\n"
"tcp:dot.ted.qu.ad:port for IPv4 sockets\n"
"tcp6:[ipv6_addr]:port for IPv6 sockets\n");
return -1;
}

fd = socket(sa.sa.sa_family, SOCK_STREAM, 0);
if (fd < 0) {
perror("Failed to create socket");
return -1;
}
if (bind(fd, &sa.sa, sockaddr_size) < 0) {
perror("Failed to bind");
return -1;
}

return listen_on_fd(fd);
}

int main(int argc, char **argv)
{
int nchildren = 1;
char *socket_url = NULL;
int c;

while ((c = getopt(argc, argv, "c:hfs:")) != -1) {
switch (c) {
case 'f':
stderr_to_fastcgi++;
break;
case 'h':
printf("Usage: %s [OPTION]\nInvokes CGI scripts as FCGI.\n\n"
PACKAGE_NAME" version "PACKAGE_VERSION"\n\n"
"Options are:\n"
" -f\t\t\tSend CGI's stderr over FastCGI\n"
" -c <number>\t\tNumber of processes to prefork\n"
" -s <socket_url>\tSocket to bind to (say -s help for help)\n"
" -h\t\t\tShow this help message and exit\n"
"\nReport bugs to Grzegorz Nosek <"PACKAGE_BUGREPORT">.\n"
PACKAGE_NAME" home page: <http://nginx.localdomain.pl/wiki/FcgiWrap>\n",
argv[0]
);
return 0;
case 'c':
nchildren = atoi(optarg);
break;
case 's':
socket_url = strdup(optarg);
break;
case '?':
if (optopt == 'c' || optopt == 's')
fprintf(stderr, "Option -%c requires an argument.\n", optopt);
else if (isprint(optopt))
fprintf(stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf(stderr,
"Unknown option character `\\x%x'.\n",
optopt);
return 1;
default:
abort();
}
}

#ifdef HAVE_SYSTEMD
if (sd_listen_fds(true) > 0) {
/* systemd woke us up. we should never see more than one FD passed to us. */
if (listen_on_fd(SD_LISTEN_FDS_START) < 0) {
return 1;
}
} else
#endif
if (socket_url) {
if (setup_socket(socket_url) < 0) {
return 1;
}
free(socket_url);
}

prefork(nchildren);
fcgiwrap_main();
return 0;
}
fcgiwrap-1.1.0/systemd/000075500000000000000000000000001210346247500150055ustar00rootroot00000000000000fcgiwrap-1.1.0/systemd/fcgiwrap.service000064400000000000000000000002371210346247500201730ustar00rootroot00000000000000[Unit]
Description=Simple CGI Server
After=nss-user-lookup.target

[Service]
ExecStart=/usr/sbin/fcgiwrap
User=http
Group=http

[Install]
Also=fcgiwrap.socket
fcgiwrap-1.1.0/systemd/fcgiwrap.socket000064400000000000000000000001601210346247500200160ustar00rootroot00000000000000[Unit]
Description=fcgiwrap Socket

[Socket]
ListenStream=/run/fcgiwrap.sock

[Install]
WantedBy=sockets.target
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin