From ff967d6332cf78c3f35c1de24f4d1356bcfd06b9 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Mon, 21 Nov 2011 17:07:38 +0100 Subject: [PATCH] Added scripts to help building the libfcgi.dll and .lib from modified source of libfcgi Extracted into "libfcgi" only the files needed to compile the Windows .dll and .lib --- library/server/libfcgi/Clib/README.md | 21 + library/server/libfcgi/Clib/build_win32.bat | 18 + library/server/libfcgi/Clib/build_win64.bat | 18 + .../server/libfcgi/Clib/libfcgi/LICENSE.TERMS | 28 + library/server/libfcgi/Clib/libfcgi/README.md | 14 + .../server/libfcgi/Clib/libfcgi/doc/README | 400 ++ .../libfcgi/Clib/libfcgi/doc/README.win64 | 83 + .../Clib/libfcgi/doc/fcgi-2.4.0-x64.patch | 5747 +++++++++++++++++ .../server/libfcgi/Clib/libfcgi/fcgi_config.h | 112 + .../libfcgi/Clib/libfcgi/include/Makefile.am | 8 + .../libfcgi/Clib/libfcgi/include/Makefile.in | 279 + .../libfcgi/Clib/libfcgi/include/fastcgi.h | 136 + .../Clib/libfcgi/include/fcgi_config.h | 39 + .../Clib/libfcgi/include/fcgi_config_x86.h | 39 + .../libfcgi/Clib/libfcgi/include/fcgi_stdio.h | 246 + .../libfcgi/Clib/libfcgi/include/fcgiapp.h | 622 ++ .../libfcgi/Clib/libfcgi/include/fcgimisc.h | 38 + .../libfcgi/Clib/libfcgi/include/fcgio.h | 151 + .../libfcgi/Clib/libfcgi/include/fcgios.h | 130 + .../libfcgi/Clib/libfcgi/libfcgi/Makefile.am | 27 + .../libfcgi/Clib/libfcgi/libfcgi/Makefile.in | 454 ++ .../libfcgi/Clib/libfcgi/libfcgi/fcgi_stdio.c | 825 +++ .../libfcgi/Clib/libfcgi/libfcgi/fcgiapp.c | 2349 +++++++ .../libfcgi/Clib/libfcgi/libfcgi/fcgio.cpp | 203 + .../libfcgi/Clib/libfcgi/libfcgi/libfcgi.mak | 311 + .../libfcgi/Clib/libfcgi/libfcgi/os_unix.c | 1293 ++++ .../libfcgi/Clib/libfcgi/libfcgi/os_win32.c | 1918 ++++++ .../libfcgi/spec/lib/win64/msc/libfcgi.dll | Bin 51200 -> 47616 bytes .../libfcgi/spec/lib/win64/msc/libfcgi.lib | Bin 27710 -> 27710 bytes 29 files changed, 15509 insertions(+) create mode 100644 library/server/libfcgi/Clib/README.md create mode 100644 library/server/libfcgi/Clib/build_win32.bat create mode 100644 library/server/libfcgi/Clib/build_win64.bat create mode 100644 library/server/libfcgi/Clib/libfcgi/LICENSE.TERMS create mode 100644 library/server/libfcgi/Clib/libfcgi/README.md create mode 100644 library/server/libfcgi/Clib/libfcgi/doc/README create mode 100644 library/server/libfcgi/Clib/libfcgi/doc/README.win64 create mode 100644 library/server/libfcgi/Clib/libfcgi/doc/fcgi-2.4.0-x64.patch create mode 100644 library/server/libfcgi/Clib/libfcgi/fcgi_config.h create mode 100644 library/server/libfcgi/Clib/libfcgi/include/Makefile.am create mode 100644 library/server/libfcgi/Clib/libfcgi/include/Makefile.in create mode 100644 library/server/libfcgi/Clib/libfcgi/include/fastcgi.h create mode 100644 library/server/libfcgi/Clib/libfcgi/include/fcgi_config.h create mode 100644 library/server/libfcgi/Clib/libfcgi/include/fcgi_config_x86.h create mode 100644 library/server/libfcgi/Clib/libfcgi/include/fcgi_stdio.h create mode 100644 library/server/libfcgi/Clib/libfcgi/include/fcgiapp.h create mode 100644 library/server/libfcgi/Clib/libfcgi/include/fcgimisc.h create mode 100644 library/server/libfcgi/Clib/libfcgi/include/fcgio.h create mode 100644 library/server/libfcgi/Clib/libfcgi/include/fcgios.h create mode 100644 library/server/libfcgi/Clib/libfcgi/libfcgi/Makefile.am create mode 100644 library/server/libfcgi/Clib/libfcgi/libfcgi/Makefile.in create mode 100644 library/server/libfcgi/Clib/libfcgi/libfcgi/fcgi_stdio.c create mode 100644 library/server/libfcgi/Clib/libfcgi/libfcgi/fcgiapp.c create mode 100644 library/server/libfcgi/Clib/libfcgi/libfcgi/fcgio.cpp create mode 100644 library/server/libfcgi/Clib/libfcgi/libfcgi/libfcgi.mak create mode 100644 library/server/libfcgi/Clib/libfcgi/libfcgi/os_unix.c create mode 100644 library/server/libfcgi/Clib/libfcgi/libfcgi/os_win32.c diff --git a/library/server/libfcgi/Clib/README.md b/library/server/libfcgi/Clib/README.md new file mode 100644 index 00000000..6348592c --- /dev/null +++ b/library/server/libfcgi/Clib/README.md @@ -0,0 +1,21 @@ +== libFCGI for Eiffel libFCGI wrapper library == + +=== On Windows === + +The Eiffel libFCGI wrapper needs a modified version of libFCGI (provided by http://www.fastcgi.com/devkit/libfcgi/) + +To get the full source code: + rd /q/s libfcgi + git clone https://github.com/EiffelSoftware/libfcgi libfcgi + +And then to build the needed .dll and .lib file, use either: + build_win32.bat + or build_win64.bat + +=== On other platorms === + +You can use the original version of libfcgi + +For instance, on Ubuntu (or any debian): +> sudo apt-get install libfcgi-dev + diff --git a/library/server/libfcgi/Clib/build_win32.bat b/library/server/libfcgi/Clib/build_win32.bat new file mode 100644 index 00000000..2eae0679 --- /dev/null +++ b/library/server/libfcgi/Clib/build_win32.bat @@ -0,0 +1,18 @@ +rem This script is to help compiling the Win x86 version of libfcgi +setlocal + +set E_libFCGI_OUTDIR=%~dp0_win32 + +set CL_FLAGS= /Ilibfcgi\include /nologo /W3 /WX- /O2 /Ob2 /D _SCL_SECURE_NO_WARNINGS /D _CRT_SECURE_NO_WARNINGS /D NDEBUG /D WIN32 /D _CONSOLE /D LIBFCGI_EXPORTS /D WINVER=0x501 /D _WIN32_WINNT=0x501 /D _SECURE_SCL=0 /D _VC80_UPGRADE=0x0600 /D _WINDLL /D _MBCS /GF /Gm- /EHsc /MD /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Gd /errorReport:queue /Fo"%E_libFCGI_OUTDIR%/" + +set LINK_FLAGS= /ERRORREPORT:QUEUE /OUT:"%E_libFCGI_OUTDIR%\libfcgi.dll" /INCREMENTAL:NO /NOLOGO Ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /TLBID:1 /DYNAMICBASE:NO /IMPLIB:"%E_libFCGI_OUTDIR%\libfcgi.lib" /MACHINE:X86 + +mkdir %E_libFCGI_OUTDIR% +copy libfcgi\include\fcgi_config_x86.h libfcgi\include\fcgi_config.h +CL.exe /c %CL_FLAGS% /TC libfcgi\libfcgi\fcgi_stdio.c libfcgi\libfcgi\fcgiapp.c libfcgi\libfcgi\os_win32.c +CL.exe /c %CL_FLAGS% /TP libfcgi\libfcgi\fcgio.cpp +link.exe %LINK_FLAGS% /DLL %E_libFCGI_OUTDIR%\fcgi_stdio.obj %E_libFCGI_OUTDIR%\fcgiapp.obj %E_libFCGI_OUTDIR%\os_win32.obj %E_libFCGI_OUTDIR%\fcgio.obj + +copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\windows\msc + +endlocal diff --git a/library/server/libfcgi/Clib/build_win64.bat b/library/server/libfcgi/Clib/build_win64.bat new file mode 100644 index 00000000..303632cf --- /dev/null +++ b/library/server/libfcgi/Clib/build_win64.bat @@ -0,0 +1,18 @@ +rem This script is to help compiling the Win64 version of libfcgi +setlocal + +set E_libFCGI_OUTDIR=%~dp0_win64 + +set CL_FLAGS= /Ilibfcgi\include /nologo /W3 /WX- /O2 /Ob2 /D _WIN64 /D _SCL_SECURE_NO_WARNINGS /D _CRT_SECURE_NO_WARNINGS /D NDEBUG /D WIN32 /D _CONSOLE /D LIBFCGI_EXPORTS /D WINVER=0x501 /D _WIN32_WINNT=0x501 /D _SECURE_SCL=0 /D _VC80_UPGRADE=0x0600 /D _WINDLL /D _MBCS /GF /Gm- /EHsc /MD /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Gd /errorReport:queue /Fo"%E_libFCGI_OUTDIR%/" + +set LINK_FLAGS= /ERRORREPORT:QUEUE /OUT:"%E_libFCGI_OUTDIR%\libfcgi.dll" /INCREMENTAL:NO /NOLOGO Ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /TLBID:1 /DYNAMICBASE:NO /IMPLIB:"%E_libFCGI_OUTDIR%\libfcgi.lib" /MACHINE:X64 + +mkdir %E_libFCGI_OUTDIR% +copy libfcgi\include\fcgi_config_x86.h libfcgi\include\fcgi_config.h +CL.exe /c %CL_FLAGS% /TC libfcgi\libfcgi\fcgi_stdio.c libfcgi\libfcgi\fcgiapp.c libfcgi\libfcgi\os_win32.c +CL.exe /c %CL_FLAGS% /TP libfcgi\libfcgi\fcgio.cpp +link.exe %LINK_FLAGS% /DLL %E_libFCGI_OUTDIR%\fcgi_stdio.obj %E_libFCGI_OUTDIR%\fcgiapp.obj %E_libFCGI_OUTDIR%\os_win32.obj %E_libFCGI_OUTDIR%\fcgio.obj + +copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\win64\msc + +endlocal diff --git a/library/server/libfcgi/Clib/libfcgi/LICENSE.TERMS b/library/server/libfcgi/Clib/libfcgi/LICENSE.TERMS new file mode 100644 index 00000000..7e6bdfde --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/LICENSE.TERMS @@ -0,0 +1,28 @@ +This FastCGI application library source and object code (the +"Software") and its documentation (the "Documentation") are +copyrighted by Open Market, Inc ("Open Market"). The following terms +apply to all files associated with the Software and Documentation +unless explicitly disclaimed in individual files. + +Open Market permits you to use, copy, modify, distribute, and license +this Software and the Documentation for any purpose, provided that +existing copyright notices are retained in all copies and that this +notice is included verbatim in any distributions. No written +agreement, license, or royalty fee is required for any of the +authorized uses. Modifications to this Software and Documentation may +be copyrighted by their authors and need not follow the licensing +terms described here. If modifications to this Software and +Documentation have new licensing terms, the new terms must be clearly +indicated on the first page of each file where they apply. + +OPEN MARKET MAKES NO EXPRESS OR IMPLIED WARRANTY WITH RESPECT TO THE +SOFTWARE OR THE DOCUMENTATION, INCLUDING WITHOUT LIMITATION ANY +WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN +NO EVENT SHALL OPEN MARKET BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY +DAMAGES ARISING FROM OR RELATING TO THIS SOFTWARE OR THE +DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES OR SIMILAR DAMAGES, INCLUDING LOST PROFITS OR +LOST DATA, EVEN IF OPEN MARKET HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS". +OPEN MARKET HAS NO LIABILITY IN CONTRACT, TORT, NEGLIGENCE OR +OTHERWISE ARISING OUT OF THIS SOFTWARE OR THE DOCUMENTATION. diff --git a/library/server/libfcgi/Clib/libfcgi/README.md b/library/server/libfcgi/Clib/libfcgi/README.md new file mode 100644 index 00000000..0913afab --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/README.md @@ -0,0 +1,14 @@ +== libFCGI source code for the libFCGI Eiffel wrapper == + +This folder contains the source code required to compile the Windows versions of libfcgi + +It is a __modified__ version to enable us to compile libfcgi on __Windows 64bits__ + +We used the patch located under doc\fcgi-2.4.0-x64.patch applied against the original version of libFCGI from http://www.fastcgi.com/devkit/libfcgi/ + +This folder contains only the files related to libfcgi on Windows. + +You can get the full source code at https://github.com/EiffelSoftware/libfcgi + + +SEE ALSO in the folder "doc" the original README files diff --git a/library/server/libfcgi/Clib/libfcgi/doc/README b/library/server/libfcgi/Clib/libfcgi/doc/README new file mode 100644 index 00000000..6cd00f11 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/doc/README @@ -0,0 +1,400 @@ +FastCGI Developer's Kit README +------------------------------ + + $Id: README,v 1.21 2003/01/19 17:19:41 robs Exp $ + Copyright (c) 1996 Open Market, Inc. + See the file "LICENSE.TERMS" for information on usage and redistribution + of this file, and for a DISCLAIMER OF ALL WARRANTIES. + +Basic Directions +---------------- + +Unix: + + ./configure + make + make install + +Win32: + + nmake -f Makefile.nt + + (or use the MSVC++ project files in the Win32 directory) + + +CHANGES +------- + +For more detail regarding changes, please consult the cvs log available +on http://fastcgi.com/. + + +2.4.0 +----- + + *) When closing connections, shutdown() the send side of TCP sockets to + prevent a TCP RST from trashing the reciept of data on the client (when + the client continues to send data to the application). + + *) [WIN32] force an exit from the ShutdownRequestThread when a shutdown is + signaled and NamedPipes are in use. + + *) Use streamsize and char_type in the C++ API. + + *) [WIN32] Eliminate the (partial and broken) use of OverlappedIO - this + was causing a loose spin in acceptNamedPipe(). + + *) Fix a bug that caused an assert to pop when an async file descriptor was + numbered greater than 16. Kevin Eye [eye@buffalo.edu] + + *) Update the echo-cpp example to show the restoral of the original + streambufs. Trub, Vladimir [vtrub@purolator.com] + + *) Fix a bug a that caused the lib to crash under certain circumstances + when an error occured on a read + + *) Test for iostreams that support a streambuf assigment operator + + *) (WIN32) Fixed initialization of the accept mutex when OpenSocket() was used. + Niklas Bergh [niklas.bergh@tific.com] + + +2.2.2 +----- + + *) Added support for shared libraries. + + *) Added support for a graceful shutdown via an event under Win32. + + *) Added default signal handlers for PIPE, USR1, and TERM. + + *) Fix some minor bugs in the 0S_ layer. + + *) Fixed the C++ streambuf implementation. + + +Changes with devkit 2.1.1 +------------------------- + + *) Fixed an unintentional sign extension during promotion in Java's + FCGIInputStream.read(). Takayuki Tachikawa + + *) Cleaned up warnings in examples (mostly main() complaints). + + *) Removed examples/tiny-cgi.c (it wasn't a FastCGI application?!). + + *) Remove some debugging code and clean up some gcc warnings in cgi-fcgi.c. + + *) Add multithread support to the fcgiapp lib and an example multithreaded + application, threaded.c. Based on work by Dennis Payne + and Gene Sokolov . + + *) Remove the printf() and #include of stdio.h from examples/echo2.c. + + *) Remove the static initialization of _fcgi_sF[] because on glibc 2.x based + systems stdin/stdout/stderr are no longer static. + + *) Flush FastCGI buffers at application exit. + + << INSERT OTHER STUFF HERE >> + + +What's New: Version 2.0b2, 04 April 1997 +-------------------------------------- + +Some additional bug fixes, mostly on NT port. The following list +of the bugs that have been and fixed: + 1. Updated build_no_shell.bat to create a FcgiBin directory under the + top level of the FastCGI kit and copy all executables and the + FastCGI dll there. This makes it easier to use. + 2. Corrected the Unix version of OS_SpawnChild so that it didn't close + the listenFd when forking off child processes. This code would + affect the cgi-fcgi application on Unix. The problem is that it + could only start one fastcgi process. Any other processes would not + get the listen file descriptor and they would die. + 3. Corrected cgi-fcgi.c so that it properly handled large posts. The + bug was introduced with the asynchronous I/O model implemented for + the Windows NT port. The problem was not clearing a bit indicating + that a read had completed. This caused the application to stall. + 4. Corrected OS_DoIo, the function used for scheduling I/O for cgi-fcgi. + It had a bug where it wasn't creating a copy of the file descriptors + used for I/O. This would cause the master list of FDs to watch to be + reset and thus would hang the application because we would no longer + watch for I/O on those file descriptors. (This problem was specific to + Unix and only happened with the cgi-fcgi application.) + 5. Cleaned up several compilation warnings present on OSF. + + +What's New: Version 2.0b1, 24 March 1997 +-------------------------------------- + +This "beta" release adds the functionality of "cgi-fcgi" to the +Windows NT platform and allows for creation of FastCGI applications +running in Win32 environment. There is almost no new documentation +provided, but will become part of this kit in the official release. + 1. Added FastCGI libraries running on Windows NT 3.51+ + 2. Rename errno to FCGI_errno in the FCGX_Stream, which was causing + problems on some Linux platforms and NT. + 3. Fixed a parenthesis problem in FCGI_gets + + +What's New: Version 1.5.1, 12 December 1996 +-------------------------------------- + +This release introduces mostly bug fixes, without any additional +functionality to the kit. + 1. Conditional compilation for the hp-ux compiler. + 2. Loop around the accept() call to eliminate "OS Error: Interrupted + System Call" message from appearing in the error logs. + 3. Casting of the FCGI_Header to (char *), which eliminates the + assertion failure "bufPtr->size>0". + + +What's New: Version 1.5, 12 June 1996 +-------------------------------------- + +General: + + Added a white paper on FastCGI application performance to the + doc directory. Generally brought the other docs up to date. + + Rearranged the kit to put more emphasis on running FastCGI-capable + servers and less on running cgi-fcgi. Added + examples/conf/om-httpd.config, a config file that demonstrates all + of the example apps. (Would like to have similar configs for NCSA + and Apache.) + + Added the tiny-authorizer and sample-store applications to + the examples. These are explained in the index.html. + + In addition to everything else it does, sample-store demonstrates + a bug in the Open Market WebServer 2.0: When an Authorizer + application denies access, the server tacks some extra junk onto + the end of the page the application returns. A little ugly but + not fatal. + +C libraries: + + Added the functions FCGX_Finish and FCGI_Finish. These functions + finish the current request from the HTTP server but do not begin a + new request. These functions make it possible for applications to + perform other processing between requests. An application must not + use its stdin, stdout, stderr, or environ between calling + FCGI_Finish and calling FCGI_Accept. See doc/FCGI_Finish.3 for + more information. The application examples/sample-store.c demonstrates + the use of FCGI_Finish. + + Added conditional 'extern "C"' stuff to the .h files fcgi_stdio.h, + fcgiapp.h, and fcgiappmisc.h for the benefit of C++ applications + (suggested by Jim McCarthy). + + Fixed two bugs in FCGX_VFPrintF (reported by Ben Laurie). These + bugs affected processing of %f format specifiers and of all format + specifiers containing a precision spec (e.g "%12.4g"). + + Fixed a bug in FCGX_Accept in which the environment variable + FCGI_WEBSERVER_ADDRS was being read rather than the specified + FCGI_WEB_SERVER_ADDRS. Fixed a bug in FCGX_Accept in which the + wrong storage was freed when FCGI_WEB_SERVER_ADDRS contained more + than one address or if the address check failed. + + Changed FCGX_Accept to avoid depending upon accept(2) returning the + correct value of sin_family in the socketaddr structure for an + AF_UNIX connection (SCO returns the wrong value, as reported by Paul + Mahoney). + + Changed the error retry logic in FCGX_Accept. FCGX_Accept now + returns -1 only in case of operating system errors that occur while + accepting a connection (e.g. out of file descriptors). Other errors + cause the current connection to be dropped and a new connection to + be attempted. + +Perl: + + Changed FCGI.xs to make it insensitive to Perl's treatment of + environ (we hope). Changed FCGI::accept so the initial environment + variables are not unset on the first call to FCGI::accept (or on + subsequent calls either). Added the echo-perl example + program. Added a workaround for the "empty initial environment bug" + to tiny-perl-fcgi. Changed the example Perl scripts to use a new + symbolic link ./perl, avoiding the HP-UX 32 character limit on the + first line of a command interpreter file. + + Because the FastCGI-enabled Perl interpreter uses the C fcgi_stdio + library, it picks up all the changes listed above for C. There's + a new Perl subroutine FCGI::finish. + +Tcl: + + Fixed a bug in tclFCGI.c that caused the request environment + variables to be lost. Changed FCGI_Accept so the initial + environment variables are not unset on the first call to FCGI_Accept + (or on subsequent calls either). Added the echo-tcl example + program. Fixed another bug that caused Tcl to become confused by + file opens; as a side effect of this change, writes to stdout/stderr + that occur in an app running as FastCGI before FCGI_Accept is called + are no-ops rather than crashing Tcl. Changed the example Tcl + scripts to use a new symbolic link ./tclsh, avoiding the HP-UX 32 + character limit on the first line of a command interpreter file. + + Because the FastCGI-enabled Tcl interpreter uses the C fcgi_stdio + library, it picks up all the changes listed above for C; there's + a new Tcl command FCGI_Finish. + +Java: + + Fixed a sign-extension bug in FCGIMessage.java that caused bad encodings + of names and values in name-value pairs for lengths in [128..255]. + Made small cleanups in the Java example programs to make them more + consistent with the other examples. + + + +What's New: Version 1.4, 10 May 1996 +-------------------------------------- + +Includes Java classes and Java examples. + + + +What's New: Version 1.3.1, 6 May 1996 +-------------------------------------- + +New, simplified, license terms. Includes an expanded whitepaper that +describes FastCGI support in Open Market's Secure WebServer 2.0. +Includes Open Market FastCGI 1.0 Programmer's Guide. Includes +"FastCGI: A High-Performance Gateway Interface", a position paper +presented at the workshop "Programming the Web - a search for APIs", +Fifth International World Wide Web Conference, 6 May 1996, Paris, +France. + + + +What's New: Version 1.3, 29 April 1996 +-------------------------------------- + +First public release; new license terms on all files. + +Changed cgi-fcgi.c to use SO_REUSEADDR when creating the listening socket; +this avoids the need to wait through the TIME_WAIT state on all the TCP +connections made by the previous instance of an external application +you are restarting. + + + +What's New: Version 1.2.2, 15 April 1996 +---------------------------------------- + +Partially fixed a bug in Perl's FCGI::accept (source file FCGI.xs). +The per-request environment variables were being lost. Now the +per-request environment variables show up correctly, except that if +the Perl application has an empty initial environment, the environment +variables associated with the *first* request are lost. Therefore, +when starting Perl, always set some environment variable using the +AppClass -initial-env option, or by running cgi-fcgi in a non-empty +environment. + + + +What's New: Version 1.2.1, 22 March 1996 +---------------------------------------- + +Fixed a bug in FCGI_Accept. If your application running as FastCGI +opened a file before calling FCGI_Accept, it would decide that it +was really running as CGI. Things went downhill quickly after that! + +Also added advisory locking to serialize calls to accept on shared +listening sockets on Solaris and IRIX, to work around problems +with concurrent accept calls on these platforms. + + + +What's New: Version 1.2, 20 March 1996 +-------------------------------------- + +1. This version of the kit implements the most recent draft +of the protocol spec. Enhancements to the protocol include +a BEGIN_REQUEST record that simplifies request ID management +and transmits role and keep-alive information, and a simplified +end-of-stream indication. + +The protocol spec has been revised to describe exactly what's +been implemented, leaving out the features that we hope to +introduce in later releases. + +At the application level, the visible change is the FCGI_ROLE +variable that's available to applications. This allows an application +to check that it has been invoked in the expected role. A single +application can be written to respond in several roles. The +FCGI_Accept.3 manpage contains more information. + +2. We introduced the new "module" prefix FCGX in order to simplify +the relationship between fcgi_stdio and fcgiapp. + +A growing number of functions are provided in both fcgi_stdio and +fcgiapp versions. Rather than inventing an ad hoc solution for each +naming conflict (as we did with FCGI_accept and FCGI_Accept), we've +bitten the bullet and systematically renamed *all* the fcgapp +primitives with the prefix FCGX_. In fcgi_stdio, we've renamed +FCGI_accept to FCGI_Accept. So all functions that are common in the +two libraries have the same name modulo the different prefixes. + +The Accept function visible in Tcl is now called FCGI_Accept, not +FCGI_accept. + +The Accept function visible in Perl is now FCGI::accept. All +lower case names for functions and all upper case names for +modules appears to be a Perl convention, so we conform. + +3. The kit now fully supports the Responder, Authorizer, +and Filter roles. + +The Filter role required a new function, FCGI_StartFilterData. +FCGI_StartFilterData changes the input stream from reading +FCGI_STDIN data to reading FCGI_DATA data. The manpage +gives full details. + +Another new function, FCGI_SetExitStatus, is primarily for +the Responder role but is available to all. FCGI_SetExitStatus +allows an application to set a nonzero "exit" status +before completing a request and calling FCGI_Accept again. +The manpage gives full details. + +These two new functions are provided at both the fcgi_stdio interface +and the basic fcgiapp interface. Naturally, the fcgiapp versions are +called FCGX_StartFilterData and FCGX_SetExitStatus. + +4. The fcgiapp interface changed slightly in order to treat +the streams and environment data more symmetrically. + +FCGX_Accept now returns an environment pointer, rather than requiring +a call to FCGX_GetAllParams to retrieve an environment pointer. +FCGX_GetParam takes an explicit environment pointer argument. +FCGX_GetAllParams is eliminated. See the documentation in the header +file for complete information. + +fcgiapp also added the procedure FCGX_IsCGI, providing a standardized +test of whether the app was started as CGI or FastCGI. + +5. We've ported the kits to vendor-supported ANSI C compilers +on Sun (Solaris 2.X), HP, and Digital platforms. GCC can be +selected on these platforms by performing SETENV CC gcc before +running configure. + + + +What's New: Version 1.1, 30 Jan 1996 +------------------------------------ + +1. More platforms: Digital UNIX, IBM AIX, Silicon Graphics IRIX, +Sun SunOS 4.1.4. + +2. Perl and Tcl: Simple recipes for producing Perl and Tcl +interpreters that run as FastCGI applications. No source +code changes are needed to Perl and Tcl. Documented +in separate documents, accessible via the index page. + + + +Version 1.0, 10 Jan 1996 +------------------------ diff --git a/library/server/libfcgi/Clib/libfcgi/doc/README.win64 b/library/server/libfcgi/Clib/libfcgi/doc/README.win64 new file mode 100644 index 00000000..c29cae11 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/doc/README.win64 @@ -0,0 +1,83 @@ +ChangeLog from 2.4.0 to 2.4.0-x64: + +* Fixed to support 64bit build of library and sample executables for Windows. +* Fixed socklen_t detection failure problem in configure script for Linux. + + +What we've done to support 64bit build: + +* Converted Win32/*.{dsw,dsp} files to Win32/*.{sln,vcproj} files + (Visual Studio 2008 solution/project files). +* Added platform "x64" for Visual Studio solution. +* Fixed type size problem (pointer, size_t, etc.) + to support both 32/64 bit build + using intptr_t, uintptr_t, and so on. +* Fixed to check range of each numeric variable with ASSERT() + before using type cast for demotion(narrowing). +* Replaced several standard functions to recommended ones. + getpid() --> _getpid(), and so on. +* Fixed several functions to support both 32/64 bit build. + AlignInt8(), AlignPtr8(), and so on. +* Removed almost all build warnings on Visual Studio 2008. + with PreprocessorDefinitions: + _SCL_SECURE_NO_WARNINGS and _CRT_SECURE_NO_WARNINGS. +* Removed almost all build warnings on Linux. + + +Current status: + +* Tested the patch on Windows Server 2008 SP1 (64bit). + with apache httpd 2.2.10 (32bit) and mod_fastcgi-SNAP-0811090952. + Both 32/64 bit sample executables are working. + We could not build and test two samples: threaded and log-dump. +* Tested the patch on CentOS 5.3 (64bit) with gcc-4.3.3. + For 32bit build, we used gcc -m32 option. + Both 32/64 bit sample executables are working except log-dump. + We could not find out the usage of log-dump + so that we could not test it. + + +How to build: + +1. For Windows + +1-1. Extract fastcgi-2.4.0.tar.gz + +> tar xzf fastcgi-2.4.0.tar.gz + +1-2. Apply this fastcgi-2.4.0-x64.patch + +> cd fastcgi-2.4.0 +> patch -p1 < ../fastcgi-2.4.0-x64.patch + +1-3. Open fastcgi-2.4.0/Win32/FastCGI.sln with Visual Studio 2008 and build. + +Debug and Release build on Win32 and x64 are supported. + +Build with 'nmake' is not supported, since we could not find out +suitable project converter and we can use 'devenv' command line instead. + + +2. For Linux + +2-1. Do the same process as (1-1). +2-2. Do the same process as (1-2). + +2-3. Remake configure script. + +> libtoolize -c -f +> aclocal +> autoheader +> automake -a -c -f +> autoconf + +2-4. Configure and make + +> ./configure +> make +> make install + +If you need, NDEBUG preprocessor definition should be specified +to eliminate ASSERT check for release build. + + diff --git a/library/server/libfcgi/Clib/libfcgi/doc/fcgi-2.4.0-x64.patch b/library/server/libfcgi/Clib/libfcgi/doc/fcgi-2.4.0-x64.patch new file mode 100644 index 00000000..9a16cf7d --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/doc/fcgi-2.4.0-x64.patch @@ -0,0 +1,5747 @@ +diff -Nrub fcgi-2.4.0/README.win64 fcgi-2.4.0-x64/README.win64 +--- fcgi-2.4.0/README.win64 1970-01-01 09:00:00.000000000 +0900 ++++ fcgi-2.4.0-x64/README.win64 2009-07-02 13:59:42.136851000 +0900 +@@ -0,0 +1,83 @@ ++ChangeLog from 2.4.0 to 2.4.0-x64: ++ ++* Fixed to support 64bit build of library and sample executables for Windows. ++* Fixed socklen_t detection failure problem in configure script for Linux. ++ ++ ++What we've done to support 64bit build: ++ ++* Converted Win32/*.{dsw,dsp} files to Win32/*.{sln,vcproj} files ++ (Visual Studio 2008 solution/project files). ++* Added platform "x64" for Visual Studio solution. ++* Fixed type size problem (pointer, size_t, etc.) ++ to support both 32/64 bit build ++ using intptr_t, uintptr_t, and so on. ++* Fixed to check range of each numeric variable with ASSERT() ++ before using type cast for demotion(narrowing). ++* Replaced several standard functions to recommended ones. ++ getpid() --> _getpid(), and so on. ++* Fixed several functions to support both 32/64 bit build. ++ AlignInt8(), AlignPtr8(), and so on. ++* Removed almost all build warnings on Visual Studio 2008. ++ with PreprocessorDefinitions: ++ _SCL_SECURE_NO_WARNINGS and _CRT_SECURE_NO_WARNINGS. ++* Removed almost all build warnings on Linux. ++ ++ ++Current status: ++ ++* Tested the patch on Windows Server 2008 SP1 (64bit). ++ with apache httpd 2.2.10 (32bit) and mod_fastcgi-SNAP-0811090952. ++ Both 32/64 bit sample executables are working. ++ We could not build and test two samples: threaded and log-dump. ++* Tested the patch on CentOS 5.3 (64bit) with gcc-4.3.3. ++ For 32bit build, we used gcc -m32 option. ++ Both 32/64 bit sample executables are working except log-dump. ++ We could not find out the usage of log-dump ++ so that we could not test it. ++ ++ ++How to build: ++ ++1. For Windows ++ ++1-1. Extract fastcgi-2.4.0.tar.gz ++ ++> tar xzf fastcgi-2.4.0.tar.gz ++ ++1-2. Apply this fastcgi-2.4.0-x64.patch ++ ++> cd fastcgi-2.4.0 ++> patch -p1 < ../fastcgi-2.4.0-x64.patch ++ ++1-3. Open fastcgi-2.4.0/Win32/FastCGI.sln with Visual Studio 2008 and build. ++ ++Debug and Release build on Win32 and x64 are supported. ++ ++Build with 'nmake' is not supported, since we could not find out ++suitable project converter and we can use 'devenv' command line instead. ++ ++ ++2. For Linux ++ ++2-1. Do the same process as (1-1). ++2-2. Do the same process as (1-2). ++ ++2-3. Remake configure script. ++ ++> libtoolize -c -f ++> aclocal ++> autoheader ++> automake -a -c -f ++> autoconf ++ ++2-4. Configure and make ++ ++> ./configure ++> make ++> make install ++ ++If you need, NDEBUG preprocessor definition should be specified ++to eliminate ASSERT check for release build. ++ ++ +diff -Nrub fcgi-2.4.0/Win32/FastCGI.sln fcgi-2.4.0-x64/Win32/FastCGI.sln +--- fcgi-2.4.0/Win32/FastCGI.sln 1970-01-01 09:00:00.000000000 +0900 ++++ fcgi-2.4.0-x64/Win32/FastCGI.sln 2009-07-02 13:59:25.506079500 +0900 +@@ -0,0 +1,122 @@ ++ ++Microsoft Visual Studio Solution File, Format Version 10.00 ++# Visual Studio 2008 ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "authorizer", "authorizer.vcproj", "{239D906A-62A8-469E-92DF-56905B8E69A1}" ++ ProjectSection(ProjectDependencies) = postProject ++ {F162F443-9841-498C-8476-42034E8A60CE} = {F162F443-9841-498C-8476-42034E8A60CE} ++ EndProjectSection ++EndProject ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cgifcgi", "cgifcgi.vcproj", "{C344735B-630F-4D1F-BB09-251C462ADA64}" ++ ProjectSection(ProjectDependencies) = postProject ++ {F162F443-9841-498C-8476-42034E8A60CE} = {F162F443-9841-498C-8476-42034E8A60CE} ++ EndProjectSection ++EndProject ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echo", "echo.vcproj", "{55EAF1D1-E623-44E9-AE25-A7C170BE2D32}" ++ ProjectSection(ProjectDependencies) = postProject ++ {F162F443-9841-498C-8476-42034E8A60CE} = {F162F443-9841-498C-8476-42034E8A60CE} ++ EndProjectSection ++EndProject ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echo_cpp", "echo-cpp.vcproj", "{66A92564-9D0C-485E-BF22-8CE643C37E6C}" ++ ProjectSection(ProjectDependencies) = postProject ++ {F162F443-9841-498C-8476-42034E8A60CE} = {F162F443-9841-498C-8476-42034E8A60CE} ++ EndProjectSection ++EndProject ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echox", "echox.vcproj", "{81E2800A-94C3-403C-BD72-D544CA6E19D1}" ++ ProjectSection(ProjectDependencies) = postProject ++ {F162F443-9841-498C-8476-42034E8A60CE} = {F162F443-9841-498C-8476-42034E8A60CE} ++ EndProjectSection ++EndProject ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfcgi", "libfcgi.vcproj", "{F162F443-9841-498C-8476-42034E8A60CE}" ++EndProject ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "logdump", "logdump.vcproj", "{9A4F793B-B2C7-428A-BB41-10D9DA3E1741}" ++ ProjectSection(ProjectDependencies) = postProject ++ {F162F443-9841-498C-8476-42034E8A60CE} = {F162F443-9841-498C-8476-42034E8A60CE} ++ EndProjectSection ++EndProject ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "size", "size.vcproj", "{9B8472A2-3CB4-449B-AEB4-DD185BD2494E}" ++ ProjectSection(ProjectDependencies) = postProject ++ {F162F443-9841-498C-8476-42034E8A60CE} = {F162F443-9841-498C-8476-42034E8A60CE} ++ EndProjectSection ++EndProject ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "threaded", "threaded.vcproj", "{0133C5DE-15A5-44FB-8457-964980A6C3F6}" ++ ProjectSection(ProjectDependencies) = postProject ++ {F162F443-9841-498C-8476-42034E8A60CE} = {F162F443-9841-498C-8476-42034E8A60CE} ++ EndProjectSection ++EndProject ++Global ++ GlobalSection(SolutionConfigurationPlatforms) = preSolution ++ Debug|Win32 = Debug|Win32 ++ Debug|x64 = Debug|x64 ++ Release|Win32 = Release|Win32 ++ Release|x64 = Release|x64 ++ EndGlobalSection ++ GlobalSection(ProjectConfigurationPlatforms) = postSolution ++ {239D906A-62A8-469E-92DF-56905B8E69A1}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {239D906A-62A8-469E-92DF-56905B8E69A1}.Debug|Win32.Build.0 = Debug|Win32 ++ {239D906A-62A8-469E-92DF-56905B8E69A1}.Debug|x64.ActiveCfg = Debug|x64 ++ {239D906A-62A8-469E-92DF-56905B8E69A1}.Debug|x64.Build.0 = Debug|x64 ++ {239D906A-62A8-469E-92DF-56905B8E69A1}.Release|Win32.ActiveCfg = Release|Win32 ++ {239D906A-62A8-469E-92DF-56905B8E69A1}.Release|Win32.Build.0 = Release|Win32 ++ {239D906A-62A8-469E-92DF-56905B8E69A1}.Release|x64.ActiveCfg = Release|x64 ++ {239D906A-62A8-469E-92DF-56905B8E69A1}.Release|x64.Build.0 = Release|x64 ++ {C344735B-630F-4D1F-BB09-251C462ADA64}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {C344735B-630F-4D1F-BB09-251C462ADA64}.Debug|Win32.Build.0 = Debug|Win32 ++ {C344735B-630F-4D1F-BB09-251C462ADA64}.Debug|x64.ActiveCfg = Debug|x64 ++ {C344735B-630F-4D1F-BB09-251C462ADA64}.Debug|x64.Build.0 = Debug|x64 ++ {C344735B-630F-4D1F-BB09-251C462ADA64}.Release|Win32.ActiveCfg = Release|Win32 ++ {C344735B-630F-4D1F-BB09-251C462ADA64}.Release|Win32.Build.0 = Release|Win32 ++ {C344735B-630F-4D1F-BB09-251C462ADA64}.Release|x64.ActiveCfg = Release|x64 ++ {C344735B-630F-4D1F-BB09-251C462ADA64}.Release|x64.Build.0 = Release|x64 ++ {55EAF1D1-E623-44E9-AE25-A7C170BE2D32}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {55EAF1D1-E623-44E9-AE25-A7C170BE2D32}.Debug|Win32.Build.0 = Debug|Win32 ++ {55EAF1D1-E623-44E9-AE25-A7C170BE2D32}.Debug|x64.ActiveCfg = Debug|x64 ++ {55EAF1D1-E623-44E9-AE25-A7C170BE2D32}.Debug|x64.Build.0 = Debug|x64 ++ {55EAF1D1-E623-44E9-AE25-A7C170BE2D32}.Release|Win32.ActiveCfg = Release|Win32 ++ {55EAF1D1-E623-44E9-AE25-A7C170BE2D32}.Release|Win32.Build.0 = Release|Win32 ++ {55EAF1D1-E623-44E9-AE25-A7C170BE2D32}.Release|x64.ActiveCfg = Release|x64 ++ {55EAF1D1-E623-44E9-AE25-A7C170BE2D32}.Release|x64.Build.0 = Release|x64 ++ {66A92564-9D0C-485E-BF22-8CE643C37E6C}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {66A92564-9D0C-485E-BF22-8CE643C37E6C}.Debug|Win32.Build.0 = Debug|Win32 ++ {66A92564-9D0C-485E-BF22-8CE643C37E6C}.Debug|x64.ActiveCfg = Debug|x64 ++ {66A92564-9D0C-485E-BF22-8CE643C37E6C}.Debug|x64.Build.0 = Debug|x64 ++ {66A92564-9D0C-485E-BF22-8CE643C37E6C}.Release|Win32.ActiveCfg = Release|Win32 ++ {66A92564-9D0C-485E-BF22-8CE643C37E6C}.Release|Win32.Build.0 = Release|Win32 ++ {66A92564-9D0C-485E-BF22-8CE643C37E6C}.Release|x64.ActiveCfg = Release|x64 ++ {66A92564-9D0C-485E-BF22-8CE643C37E6C}.Release|x64.Build.0 = Release|x64 ++ {81E2800A-94C3-403C-BD72-D544CA6E19D1}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {81E2800A-94C3-403C-BD72-D544CA6E19D1}.Debug|Win32.Build.0 = Debug|Win32 ++ {81E2800A-94C3-403C-BD72-D544CA6E19D1}.Debug|x64.ActiveCfg = Debug|x64 ++ {81E2800A-94C3-403C-BD72-D544CA6E19D1}.Debug|x64.Build.0 = Debug|x64 ++ {81E2800A-94C3-403C-BD72-D544CA6E19D1}.Release|Win32.ActiveCfg = Release|Win32 ++ {81E2800A-94C3-403C-BD72-D544CA6E19D1}.Release|Win32.Build.0 = Release|Win32 ++ {81E2800A-94C3-403C-BD72-D544CA6E19D1}.Release|x64.ActiveCfg = Release|x64 ++ {81E2800A-94C3-403C-BD72-D544CA6E19D1}.Release|x64.Build.0 = Release|x64 ++ {F162F443-9841-498C-8476-42034E8A60CE}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {F162F443-9841-498C-8476-42034E8A60CE}.Debug|Win32.Build.0 = Debug|Win32 ++ {F162F443-9841-498C-8476-42034E8A60CE}.Debug|x64.ActiveCfg = Debug|x64 ++ {F162F443-9841-498C-8476-42034E8A60CE}.Debug|x64.Build.0 = Debug|x64 ++ {F162F443-9841-498C-8476-42034E8A60CE}.Release|Win32.ActiveCfg = Release|Win32 ++ {F162F443-9841-498C-8476-42034E8A60CE}.Release|Win32.Build.0 = Release|Win32 ++ {F162F443-9841-498C-8476-42034E8A60CE}.Release|x64.ActiveCfg = Release|x64 ++ {F162F443-9841-498C-8476-42034E8A60CE}.Release|x64.Build.0 = Release|x64 ++ {9A4F793B-B2C7-428A-BB41-10D9DA3E1741}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {9A4F793B-B2C7-428A-BB41-10D9DA3E1741}.Debug|x64.ActiveCfg = Debug|x64 ++ {9A4F793B-B2C7-428A-BB41-10D9DA3E1741}.Release|Win32.ActiveCfg = Release|Win32 ++ {9A4F793B-B2C7-428A-BB41-10D9DA3E1741}.Release|x64.ActiveCfg = Release|x64 ++ {9B8472A2-3CB4-449B-AEB4-DD185BD2494E}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {9B8472A2-3CB4-449B-AEB4-DD185BD2494E}.Debug|Win32.Build.0 = Debug|Win32 ++ {9B8472A2-3CB4-449B-AEB4-DD185BD2494E}.Debug|x64.ActiveCfg = Debug|x64 ++ {9B8472A2-3CB4-449B-AEB4-DD185BD2494E}.Debug|x64.Build.0 = Debug|x64 ++ {9B8472A2-3CB4-449B-AEB4-DD185BD2494E}.Release|Win32.ActiveCfg = Release|Win32 ++ {9B8472A2-3CB4-449B-AEB4-DD185BD2494E}.Release|Win32.Build.0 = Release|Win32 ++ {9B8472A2-3CB4-449B-AEB4-DD185BD2494E}.Release|x64.ActiveCfg = Release|x64 ++ {9B8472A2-3CB4-449B-AEB4-DD185BD2494E}.Release|x64.Build.0 = Release|x64 ++ {0133C5DE-15A5-44FB-8457-964980A6C3F6}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {0133C5DE-15A5-44FB-8457-964980A6C3F6}.Debug|x64.ActiveCfg = Debug|x64 ++ {0133C5DE-15A5-44FB-8457-964980A6C3F6}.Release|Win32.ActiveCfg = Release|Win32 ++ {0133C5DE-15A5-44FB-8457-964980A6C3F6}.Release|x64.ActiveCfg = Release|x64 ++ EndGlobalSection ++ GlobalSection(SolutionProperties) = preSolution ++ HideSolutionNode = FALSE ++ EndGlobalSection ++EndGlobal +diff -Nrub fcgi-2.4.0/Win32/authorizer.vcproj fcgi-2.4.0-x64/Win32/authorizer.vcproj +--- fcgi-2.4.0/Win32/authorizer.vcproj 1970-01-01 09:00:00.000000000 +0900 ++++ fcgi-2.4.0-x64/Win32/authorizer.vcproj 2009-07-02 13:59:25.509009000 +0900 +@@ -0,0 +1,458 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -Nrub fcgi-2.4.0/Win32/cgifcgi.vcproj fcgi-2.4.0-x64/Win32/cgifcgi.vcproj +--- fcgi-2.4.0/Win32/cgifcgi.vcproj 1970-01-01 09:00:00.000000000 +0900 ++++ fcgi-2.4.0-x64/Win32/cgifcgi.vcproj 2009-07-02 13:59:25.511938500 +0900 +@@ -0,0 +1,458 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -Nrub fcgi-2.4.0/Win32/echo-cpp.vcproj fcgi-2.4.0-x64/Win32/echo-cpp.vcproj +--- fcgi-2.4.0/Win32/echo-cpp.vcproj 1970-01-01 09:00:00.000000000 +0900 ++++ fcgi-2.4.0-x64/Win32/echo-cpp.vcproj 2009-07-02 13:59:25.513891500 +0900 +@@ -0,0 +1,458 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -Nrub fcgi-2.4.0/Win32/echo.vcproj fcgi-2.4.0-x64/Win32/echo.vcproj +--- fcgi-2.4.0/Win32/echo.vcproj 1970-01-01 09:00:00.000000000 +0900 ++++ fcgi-2.4.0-x64/Win32/echo.vcproj 2009-07-02 13:59:25.515844500 +0900 +@@ -0,0 +1,462 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -Nrub fcgi-2.4.0/Win32/echox.vcproj fcgi-2.4.0-x64/Win32/echox.vcproj +--- fcgi-2.4.0/Win32/echox.vcproj 1970-01-01 09:00:00.000000000 +0900 ++++ fcgi-2.4.0-x64/Win32/echox.vcproj 2009-07-02 13:59:25.518774000 +0900 +@@ -0,0 +1,458 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -Nrub fcgi-2.4.0/Win32/libfcgi.vcproj fcgi-2.4.0-x64/Win32/libfcgi.vcproj +--- fcgi-2.4.0/Win32/libfcgi.vcproj 1970-01-01 09:00:00.000000000 +0900 ++++ fcgi-2.4.0-x64/Win32/libfcgi.vcproj 2009-07-02 13:59:25.521703500 +0900 +@@ -0,0 +1,712 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -Nrub fcgi-2.4.0/Win32/logdump.vcproj fcgi-2.4.0-x64/Win32/logdump.vcproj +--- fcgi-2.4.0/Win32/logdump.vcproj 1970-01-01 09:00:00.000000000 +0900 ++++ fcgi-2.4.0-x64/Win32/logdump.vcproj 2009-07-02 13:59:25.523656500 +0900 +@@ -0,0 +1,462 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -Nrub fcgi-2.4.0/Win32/size.vcproj fcgi-2.4.0-x64/Win32/size.vcproj +--- fcgi-2.4.0/Win32/size.vcproj 1970-01-01 09:00:00.000000000 +0900 ++++ fcgi-2.4.0-x64/Win32/size.vcproj 2009-07-02 13:59:25.525609500 +0900 +@@ -0,0 +1,458 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -Nrub fcgi-2.4.0/Win32/threaded.vcproj fcgi-2.4.0-x64/Win32/threaded.vcproj +--- fcgi-2.4.0/Win32/threaded.vcproj 1970-01-01 09:00:00.000000000 +0900 ++++ fcgi-2.4.0-x64/Win32/threaded.vcproj 2009-07-02 13:59:25.528539000 +0900 +@@ -0,0 +1,462 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -Nrub fcgi-2.4.0/acinclude.m4 fcgi-2.4.0-x64/acinclude.m4 +--- fcgi-2.4.0/acinclude.m4 2001-12-21 12:12:50.000000000 +0900 ++++ fcgi-2.4.0-x64/acinclude.m4 2009-07-02 13:59:25.530492000 +0900 +@@ -27,14 +27,12 @@ + [Define if there's a fileno() prototype in stdio.h])], + AC_MSG_RESULT([no])) + +- if test "$HAVE_SYS_SOCKET_H"; then + AC_MSG_CHECKING([for socklen_t in sys/socket.h]) + AC_EGREP_HEADER([socklen_t], [sys/socket.h], + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_SOCKLEN], [1], + [Define if the socklen_t typedef is in sys/socket.h])], + AC_MSG_RESULT([no])) +- fi + + #-------------------------------------------------------------------- + # Do we need cross-process locking on this platform? +diff -Nrub fcgi-2.4.0/cgi-fcgi/cgi-fcgi.c fcgi-2.4.0-x64/cgi-fcgi/cgi-fcgi.c +--- fcgi-2.4.0/cgi-fcgi/cgi-fcgi.c 2001-09-01 10:14:28.000000000 +0900 ++++ fcgi-2.4.0-x64/cgi-fcgi/cgi-fcgi.c 2009-07-02 13:59:25.534398000 +0900 +@@ -33,6 +33,8 @@ + #include + #else + extern char **environ; ++#include ++#include + #endif + + #ifdef HAVE_SYS_PARAM_H +@@ -52,6 +54,11 @@ + #include "fastcgi.h" + #include "fcgios.h" + ++#ifdef _WIN32 ++#define ACCESS _access ++#else ++#define ACCESS access ++#endif + + static int wsReadPending = 0; + static int fcgiReadPending = 0; +@@ -86,8 +93,11 @@ + static int GetPtr(char **ptr, int n, Buffer *pBuf) + { + int result; ++ intptr_t ptrDiff; + *ptr = pBuf->next; +- result = min(n, pBuf->stop - pBuf->next); ++ ptrDiff = pBuf->stop - pBuf->next; ++ ASSERT(0 <= ptrDiff && ptrDiff <= INT_MAX); ++ result = min(n, (int)ptrDiff); + pBuf->next += result; + return result; + } +@@ -220,7 +230,7 @@ + /* Touch unused parameters to avoid warnings */ + dc = NULL; + +- assert(fcgiReadPending == TRUE); ++ ASSERT(fcgiReadPending == TRUE); + fcgiReadPending = FALSE; + count = bytesRead; + +@@ -250,7 +260,7 @@ + * First priority is to complete the header. + */ + count = GetPtr(&ptr, sizeof(header) - headerLen, &fromAS); +- assert(count > 0); ++ ASSERT(count > 0); + memcpy(&header + headerLen, ptr, count); + headerLen += count; + if(headerLen < sizeof(header)) { +@@ -378,9 +388,9 @@ + /* Touch unused parameters to avoid warnings */ + dc = NULL; + +- assert(fromWS.next == fromWS.stop); +- assert(fromWS.next == &fromWS.buff[0]); +- assert(wsReadPending == TRUE); ++ ASSERT(fromWS.next == fromWS.stop); ++ ASSERT(fromWS.next == &fromWS.buff[0]); ++ ASSERT(wsReadPending == TRUE); + wsReadPending = FALSE; + + if(bytesRead < 0) { +@@ -412,13 +422,13 @@ + + static void AppServerWriteHandler(ClientData dc, int bytesWritten) + { +- int length = fromWS.stop - fromWS.next; ++ intptr_t length = fromWS.stop - fromWS.next; + + /* Touch unused parameters to avoid warnings */ + dc = NULL; + +- assert(length > 0); +- assert(fcgiWritePending == TRUE); ++ ASSERT(length > 0); ++ ASSERT(fcgiWritePending == TRUE); + + fcgiWritePending = FALSE; + if(bytesWritten < 0) { +@@ -449,16 +459,18 @@ + */ + static void ScheduleIo(void) + { +- int length; ++ intptr_t length; + + /* + * Move data between standard in and the FastCGI connection. + */ ++ length = fromWS.stop - fromWS.next; + if(!fcgiWritePending && appServerSock != -1 && +- ((length = fromWS.stop - fromWS.next) != 0)) { +- if(OS_AsyncWrite(appServerSock, 0, fromWS.next, length, ++ (length != 0)) { ++ ASSERT(0 <= length && length <= INT_MAX); ++ if(OS_AsyncWrite(appServerSock, 0, fromWS.next, (int)length, + AppServerWriteHandler, +- (ClientData)appServerSock) == -1) { ++ (ClientData)(intptr_t)appServerSock) == -1) { + FCGIexit(OS_Errno); + } else { + fcgiWritePending = TRUE; +@@ -474,7 +486,7 @@ + + if(OS_AsyncRead(appServerSock, 0, fromAS.next, BUFFLEN, + AppServerReadHandler, +- (ClientData)appServerSock) == -1) { ++ (ClientData)(intptr_t)appServerSock) == -1) { + FCGIexit(OS_Errno); + } else { + fcgiReadPending = TRUE; +@@ -518,7 +530,7 @@ + exit(OS_Errno); + } + +- if(access(appPath, X_OK) == -1) { ++ if(ACCESS(appPath, X_OK) == -1) { + fprintf(stderr, "%s is not executable\n", appPath); + exit(1); + } +@@ -556,6 +568,7 @@ + unsigned char *headerBuffPtr, + int *headerLenPtr) { + unsigned char *startHeaderBuffPtr = headerBuffPtr; ++ intptr_t ptrDiff; + + ASSERT(nameLen >= 0); + if (nameLen < 0x80) { +@@ -575,7 +588,9 @@ + *headerBuffPtr++ = (unsigned char) (valueLen >> 8); + *headerBuffPtr++ = (unsigned char) valueLen; + } +- *headerLenPtr = headerBuffPtr - startHeaderBuffPtr; ++ ptrDiff = headerBuffPtr - startHeaderBuffPtr; ++ ASSERT(0 <= ptrDiff && ptrDiff <= INT_MAX); ++ *headerLenPtr = (int)ptrDiff; + } + + +@@ -639,8 +654,8 @@ + exit(-1); + } + if((av[ac] = (char *)malloc(strlen(tp1)+1)) == NULL) { +- fprintf(stderr, "Cannot allocate %d bytes\n", +- strlen(tp1)+1); ++ fprintf(stderr, "Cannot allocate %llu bytes\n", ++ (unsigned long long)strlen(tp1)+1); + exit(-1); + } + strcpy(av[ac++], tp1); +@@ -658,7 +673,7 @@ + } else if (!strcmp(argv[i], "-jitcgi")) { + DebugBreak(); + } else if (!strcmp(argv[i], "-dbgfcgi")) { +- putenv("DEBUG_FCGI=TRUE"); ++ _putenv("DEBUG_FCGI=TRUE"); + #endif + } else if(!strcmp(argv[i], "-start")) { + *doBindPtr = FALSE; +@@ -724,11 +739,13 @@ + FCGX_Stream *paramsStream; + int numFDs; + unsigned char headerBuff[8]; +- int headerLen, valueLen; ++ int headerLen; ++ intptr_t valueLen; + char *equalPtr; + FCGI_BeginRequestRecord beginRecord; + int doBind, doStart, nServers; + char appPath[MAXPATHLEN], bindPath[MAXPATHLEN]; ++ intptr_t ptrDiff; + + if(ParseArgs(argc, argv, &doBind, &doStart, + (char *) &bindPath, (char *) &appPath, &nServers)) { +@@ -803,14 +820,17 @@ + exit(1000); + } + valueLen = strlen(equalPtr + 1); ++ ASSERT(0 <= valueLen && valueLen <= INT_MAX); ++ ptrDiff = equalPtr - *envp; ++ ASSERT(0 <= ptrDiff && ptrDiff < INT_MAX); + FCGIUtil_BuildNameValueHeader( +- equalPtr - *envp, +- valueLen, ++ (int)ptrDiff, ++ (int)valueLen, + &headerBuff[0], + &headerLen); + if(FCGX_PutStr((char *) &headerBuff[0], headerLen, paramsStream) < 0 +- || FCGX_PutStr(*envp, equalPtr - *envp, paramsStream) < 0 +- || FCGX_PutStr(equalPtr + 1, valueLen, paramsStream) < 0) { ++ || FCGX_PutStr(*envp, (int)ptrDiff, paramsStream) < 0 ++ || FCGX_PutStr(equalPtr + 1, (int)valueLen, paramsStream) < 0) { + exit(FCGX_GetError(paramsStream)); + } + } +diff -Nrub fcgi-2.4.0/examples/echo-cpp.cpp fcgi-2.4.0-x64/examples/echo-cpp.cpp +--- fcgi-2.4.0/examples/echo-cpp.cpp 2002-02-25 09:46:17.000000000 +0900 ++++ fcgi-2.4.0-x64/examples/echo-cpp.cpp 2009-07-02 13:59:25.538304000 +0900 +@@ -40,6 +40,12 @@ + #include "fcgio.h" + #include "fcgi_config.h" // HAVE_IOSTREAM_WITHASSIGN_STREAMBUF + ++#ifdef _WIN32 ++#define GETPID _getpid ++#else ++#define GETPID getpid ++#endif ++ + using namespace std; + + // Maximum number of bytes allowed to be read from stdin +@@ -55,10 +61,10 @@ + cout << "\n"; + } + +-static long gstdin(FCGX_Request * request, char ** content) ++static uintptr_t gstdin(FCGX_Request * request, char ** content) + { + char * clenstr = FCGX_GetParam("CONTENT_LENGTH", request->envp); +- unsigned long clen = STDIN_MAX; ++ uintptr_t clen = STDIN_MAX; + + if (clenstr) + { +@@ -99,7 +105,7 @@ + int main (void) + { + int count = 0; +- long pid = getpid(); ++ long pid = GETPID(); + + streambuf * cin_streambuf = cin.rdbuf(); + streambuf * cout_streambuf = cout.rdbuf(); +@@ -133,7 +139,7 @@ + // many http clients (browsers) don't support it (so + // the connection deadlocks until a timeout expires!). + char * content; +- unsigned long clen = gstdin(&request, &content); ++ uintptr_t clen = gstdin(&request, &content); + + cout << "Content-type: text/html\r\n" + "\r\n" +diff -Nrub fcgi-2.4.0/examples/echo-x.c fcgi-2.4.0-x64/examples/echo-x.c +--- fcgi-2.4.0/examples/echo-x.c 2001-06-20 00:06:17.000000000 +0900 ++++ fcgi-2.4.0-x64/examples/echo-x.c 2009-07-02 13:59:25.541233500 +0900 +@@ -28,6 +28,12 @@ + extern char **environ; + #endif + ++#ifdef _WIN32 ++#define GETPID _getpid ++#else ++#define GETPID getpid ++#endif ++ + #include "fcgiapp.h" + + static void PrintEnv(FCGX_Stream *out, char *label, char **envp) +@@ -54,7 +60,7 @@ + "\r\n" + "FastCGI echo (fcgiapp version)" + "

FastCGI echo (fcgiapp version)

\n" +- "Request number %d, Process ID: %d

\n", ++count, getpid()); ++ "Request number %d, Process ID: %d

\n", ++count, GETPID()); + + if (contentLength != NULL) + len = strtol(contentLength, NULL, 10); +diff -Nrub fcgi-2.4.0/examples/echo.c fcgi-2.4.0-x64/examples/echo.c +--- fcgi-2.4.0/examples/echo.c 1999-07-28 09:29:37.000000000 +0900 ++++ fcgi-2.4.0-x64/examples/echo.c 2009-07-02 13:59:25.545139500 +0900 +@@ -28,6 +28,12 @@ + extern char **environ; + #endif + ++#ifdef _WIN32 ++#define GETPID _getpid ++#else ++#define GETPID getpid ++#endif ++ + #include "fcgi_stdio.h" + + +@@ -53,7 +59,7 @@ + "\r\n" + "FastCGI echo" + "

FastCGI echo

\n" +- "Request number %d, Process ID: %d

\n", ++count, getpid()); ++ "Request number %d, Process ID: %d

\n", ++count, GETPID()); + + if (contentLength != NULL) { + len = strtol(contentLength, NULL, 10); +diff -Nrub fcgi-2.4.0/examples/threaded.c fcgi-2.4.0-x64/examples/threaded.c +--- fcgi-2.4.0/examples/threaded.c 2001-11-20 12:23:21.000000000 +0900 ++++ fcgi-2.4.0-x64/examples/threaded.c 2009-07-02 13:59:25.549045500 +0900 +@@ -24,7 +24,8 @@ + + static void *doit(void *a) + { +- int rc, i, thread_id = (int)a; ++ int rc, i; ++ intptr_t thread_id = (intptr_t)a; + pid_t pid = getpid(); + FCGX_Request request; + char *server_name; +@@ -71,7 +72,7 @@ + + int main(void) + { +- int i; ++ intptr_t i; + pthread_t id[THREAD_COUNT]; + + FCGX_Init(); +diff -Nrub fcgi-2.4.0/libfcgi/fcgi_stdio.c fcgi-2.4.0-x64/libfcgi/fcgi_stdio.c +--- fcgi-2.4.0/libfcgi/fcgi_stdio.c 2001-09-01 10:09:30.000000000 +0900 ++++ fcgi-2.4.0-x64/libfcgi/fcgi_stdio.c 2009-07-02 13:59:25.552951500 +0900 +@@ -19,6 +19,8 @@ + #include /* for va_arg */ + #include /* for malloc */ + #include /* for strerror */ ++#include ++#include + + #include "fcgi_config.h" + +@@ -56,6 +58,8 @@ + + #define popen _popen + #define pclose _pclose ++#define fdopen _fdopen ++#define fileno _fileno + + #endif /* _WIN32 */ + +@@ -662,8 +666,10 @@ + if((size * nmemb) == 0) { + return 0; + } +- n = FCGX_GetStr((char *) ptr, size * nmemb, fp->fcgx_stream); +- return (n/size); ++ ASSERT(size * nmemb < (size_t)INT_MAX); ++ n = FCGX_GetStr((char *) ptr, (int)(size * nmemb), fp->fcgx_stream); ++ ASSERT(n >= 0); ++ return ((size_t)n/size); + } + return (size_t)EOF; + } +@@ -677,8 +683,10 @@ + if((size * nmemb) == 0) { + return 0; + } +- n = FCGX_PutStr((char *) ptr, size * nmemb, fp->fcgx_stream); +- return (n/size); ++ ASSERT(size * nmemb < (size_t)INT_MAX); ++ n = (size_t)FCGX_PutStr((char *) ptr, (int)(size * nmemb), fp->fcgx_stream); ++ ASSERT(n >= 0); ++ return ((size_t)n/size); + } + return (size_t)EOF; + } +diff -Nrub fcgi-2.4.0/libfcgi/fcgiapp.c fcgi-2.4.0-x64/libfcgi/fcgiapp.c +--- fcgi-2.4.0/libfcgi/fcgiapp.c 2001-12-13 07:54:10.000000000 +0900 ++++ fcgi-2.4.0-x64/libfcgi/fcgiapp.c 2009-07-02 13:59:25.558810500 +0900 +@@ -24,6 +24,10 @@ + #include + #include + #include ++#ifndef _WIN32 ++#include ++#include ++#endif + + #include "fcgi_config.h" + +@@ -63,6 +67,12 @@ + #define LONG_DOUBLE long double + #endif + ++#ifdef _WIN64 ++#define ABS(a) _abs64(a) ++#else ++#define ABS(a) abs(a) ++#endif ++ + /* + * Globals + */ +@@ -85,7 +95,7 @@ + + static char *StringCopy(char *str) + { +- int strLen = strlen(str); ++ size_t strLen = strlen(str); + char *newString = (char *)Malloc(strLen + 1); + memcpy(newString, str, strLen); + newString[strLen] = '\000'; +@@ -142,7 +152,7 @@ + */ + int FCGX_GetStr(char *str, int n, FCGX_Stream *stream) + { +- int m, bytesMoved; ++ intptr_t m, bytesMoved; + + if (stream->isClosed || ! stream->isReader || n <= 0) { + return 0; +@@ -166,15 +176,21 @@ + memcpy(str, stream->rdNext, m); + bytesMoved += m; + stream->rdNext += m; +- if(bytesMoved == n) +- return bytesMoved; ++ if(bytesMoved == n) { ++ ASSERT(INT_MIN <= bytesMoved && bytesMoved <= INT_MAX); ++ return (int)bytesMoved; ++ } + str += m; + } +- if(stream->isClosed || !stream->isReader) +- return bytesMoved; ++ if(stream->isClosed || !stream->isReader) { ++ ASSERT(INT_MIN <= bytesMoved && bytesMoved <= INT_MAX); ++ return (int)bytesMoved; ++ } + stream->fillBuffProc(stream); +- if (stream->isClosed) +- return bytesMoved; ++ if (stream->isClosed) { ++ ASSERT(INT_MIN <= bytesMoved && bytesMoved <= INT_MAX); ++ return (int)bytesMoved; ++ } + + stream->stopUnget = stream->rdNext; + } +@@ -309,7 +325,7 @@ + */ + int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream) + { +- int m, bytesMoved; ++ intptr_t m, bytesMoved; + + /* + * Fast path: room for n bytes in the buffer +@@ -330,8 +346,10 @@ + memcpy(stream->wrNext, str, m); + bytesMoved += m; + stream->wrNext += m; +- if(bytesMoved == n) +- return bytesMoved; ++ if(bytesMoved == n) { ++ ASSERT(INT_MIN <= bytesMoved && bytesMoved <= INT_MAX); ++ return (int)bytesMoved; ++ } + str += m; + } + if(stream->isClosed || stream->isReader) +@@ -355,7 +373,9 @@ + */ + int FCGX_PutS(const char *str, FCGX_Stream *stream) + { +- return FCGX_PutStr(str, strlen(str), stream); ++ size_t sz = strlen(str); ++ ASSERT(sz <= INT_MAX); ++ return FCGX_PutStr(str, (int)sz, stream); + } + + /* +@@ -406,15 +426,17 @@ + /* + * Max size of a format specifier is 1 + 5 + 7 + 7 + 2 + 1 + slop + */ +-static void CopyAndAdvance(char **destPtr, char **srcPtr, int n); ++static void CopyAndAdvance(char **destPtr, char **srcPtr, intptr_t n); + + int FCGX_VFPrintF(FCGX_Stream *stream, const char *format, va_list arg) + { + char *f, *fStop, *percentPtr, *p, *fmtBuffPtr, *buffPtr; +- int op, performedOp, sizeModifier, buffCount = 0, buffLen, specifierLength; +- int fastPath, n, auxBuffLen = 0, buffReqd, minWidth, precision, exp; ++ intptr_t op, performedOp, sizeModifier, buffCount = 0, buffLen; ++ intptr_t fastPath, n, auxBuffLen = 0, minWidth, precision; ++ int exp; ++ intptr_t specifierLength, buffReqd; + char *auxBuffPtr = NULL; +- int streamCount = 0; ++ intptr_t streamCount = 0; + char fmtBuff[FMT_BUFFLEN]; + char buff[PRINTF_BUFFLEN]; + +@@ -439,9 +461,11 @@ + percentPtr = (char *)memchr(f, '%', fStop - f); + if(percentPtr == NULL) percentPtr = fStop; + if(percentPtr != f) { +- if(FCGX_PutStr(f, percentPtr - f, stream) < 0) ++ intptr_t ptrDiff = percentPtr - f; ++ ASSERT(0 <= ptrDiff && ptrDiff < INT_MAX); ++ if(FCGX_PutStr(f, (int)ptrDiff, stream) < 0) + goto ErrorReturn; +- streamCount += percentPtr - f; ++ streamCount += (int)ptrDiff; + f = percentPtr; + if(f == fStop) break; + } +@@ -506,14 +530,14 @@ + if(n == 0) { + if(*p == '*') { + minWidth = va_arg(arg, int); +- if(abs(minWidth) > 999999) ++ if(ABS(minWidth) > 999999) + goto ErrorReturn; + /* + * The following use of strlen rather than the + * value returned from sprintf is because SUNOS4 + * returns a char * instead of an int count. + */ +- sprintf(fmtBuffPtr, "%d", minWidth); ++ sprintf(fmtBuffPtr, "%lld", (long long)minWidth); + fmtBuffPtr += strlen(fmtBuffPtr); + p++; + } else { +@@ -542,7 +566,7 @@ + * value returned from sprintf is because SUNOS4 + * returns a char * instead of an int count. + */ +- sprintf(fmtBuffPtr, "%d", precision); ++ sprintf(fmtBuffPtr, "%lld", (long long)precision); + fmtBuffPtr += strlen(fmtBuffPtr); + p++; + } else { +@@ -764,11 +788,13 @@ + switch(sizeModifier) { + case ' ': + intPtrArg = va_arg(arg, int *); +- *intPtrArg = streamCount; ++ ASSERT(INT_MIN <= streamCount && streamCount <= INT_MAX); ++ *intPtrArg = (int)streamCount; + break; + case 'l': + longPtrArg = va_arg(arg, long *); +- *longPtrArg = streamCount; ++ ASSERT(LONG_MIN <= streamCount && streamCount <= LONG_MAX); ++ *longPtrArg = (long)streamCount; + break; + case 'h': + shortPtrArg = (short *) va_arg(arg, short *); +@@ -835,7 +861,8 @@ + } /* for (;;) */ + ASSERT(buffCount < buffLen); + if(buffCount > 0) { +- if(FCGX_PutStr(buffPtr, buffCount, stream) < 0) ++ ASSERT(0 <= buffCount && buffCount <= INT_MAX); ++ if(FCGX_PutStr(buffPtr, (int)buffCount, stream) < 0) + goto ErrorReturn; + streamCount += buffCount; + } else if(buffCount < 0) { +@@ -848,18 +875,19 @@ + streamCount = -1; + NormalReturn: + if(auxBuffPtr != NULL) free(auxBuffPtr); +- return streamCount; ++ ASSERT(INT_MIN <= streamCount && streamCount <= INT_MAX); ++ return (int)streamCount; + } + + /* + * Copy n characters from *srcPtr to *destPtr, then increment + * both *srcPtr and *destPtr by n. + */ +-static void CopyAndAdvance(char **destPtr, char **srcPtr, int n) ++static void CopyAndAdvance(char **destPtr, char **srcPtr, intptr_t n) + { + char *dest = *destPtr; + char *src = *srcPtr; +- int i; ++ intptr_t i; + for (i = 0; i < n; i++) + *dest++ = *src++; + *destPtr = dest; +@@ -1075,7 +1103,7 @@ + */ + static void PutParam(ParamsPtr paramsPtr, char *nameValue) + { +- int size; ++ intptr_t size; + + *paramsPtr->cur++ = nameValue; + size = paramsPtr->cur - paramsPtr->vec; +@@ -1102,7 +1130,7 @@ + */ + char *FCGX_GetParam(const char *name, FCGX_ParamArray envp) + { +- int len; ++ intptr_t len; + char **p; + + if (name == NULL || envp == NULL) return NULL; +@@ -1269,8 +1297,8 @@ + * + *---------------------------------------------------------------------- + */ +-static int AlignInt8(unsigned n) { +- return (n + 7) & (UINT_MAX - 7); ++static intptr_t AlignInt8(intptr_t n) { ++ return (n + 7) & ~7; + } + + /* +@@ -1284,9 +1312,7 @@ + *---------------------------------------------------------------------- + */ + static unsigned char *AlignPtr8(unsigned char *p) { +- unsigned long u = (unsigned long) p; +- u = ((u + 7) & (ULONG_MAX - 7)) - u; +- return p + u; ++ return (unsigned char *)((((uintptr_t)p) + 7) & ~7); + } + + +@@ -1359,9 +1385,9 @@ + + + +-static int write_it_all(int fd, char *buf, int len) ++static intptr_t write_it_all(int fd, char *buf, intptr_t len) + { +- int wrote; ++ intptr_t wrote; + + while (len) { + wrote = OS_Write(fd, buf, len); +@@ -1386,7 +1412,7 @@ + static void EmptyBuffProc(struct FCGX_Stream *stream, int doClose) + { + FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; +- int cLen, eLen; ++ intptr_t cLen, eLen; + /* + * If the buffer contains stream data, fill in the header. + * Pad the record to a multiple of 8 bytes in length. Padding +@@ -1403,9 +1429,11 @@ + */ + memset(stream->wrNext, 0, eLen - cLen); + stream->wrNext += eLen - cLen; ++ ASSERT(0 <= cLen && cLen <= INT_MAX); ++ ASSERT(0 <= eLen - cLen && eLen - cLen <= INT_MAX); + *((FCGI_Header *) data->buff) + = MakeHeader(data->type, +- data->reqDataPtr->requestId, cLen, eLen - cLen); ++ data->reqDataPtr->requestId, (int)cLen, (int)(eLen - cLen)); + } else { + stream->wrNext = data->buff; + } +@@ -1459,7 +1487,7 @@ + char response[64]; /* 64 = 8 + 3*(1+1+14+1)* + padding */ + char *responseP = &response[FCGI_HEADER_LEN]; + char *name, value = '\0'; +- int len, paddedLen; ++ intptr_t len, paddedLen; + if(type == FCGI_GET_VALUES) { + ReadParams(paramsPtr, stream); + if((FCGX_GetError(stream) != 0) || (data->contentLen != 0)) { +@@ -1480,21 +1508,24 @@ + } + if(name != NULL) { + len = strlen(name); +- sprintf(responseP, "%c%c%s%c", len, 1, name, value); ++ sprintf(responseP, "%c%c%s%c", (int)len, 1, name, value); + responseP += len + 3; + } + } + len = responseP - &response[FCGI_HEADER_LEN]; + paddedLen = AlignInt8(len); ++ ASSERT(0 <= len && len <= INT_MAX); ++ ASSERT(0 <= paddedLen - len && paddedLen - len <= INT_MAX); + *((FCGI_Header *) response) + = MakeHeader(FCGI_GET_VALUES_RESULT, FCGI_NULL_REQUEST_ID, +- len, paddedLen - len); ++ (int)len, (int)(paddedLen - len)); + FreeParams(¶msPtr); + } else { + paddedLen = len = sizeof(FCGI_UnknownTypeBody); ++ ASSERT(0 <= len && len <= INT_MAX); + ((FCGI_UnknownTypeRecord *) response)->header + = MakeHeader(FCGI_UNKNOWN_TYPE, FCGI_NULL_REQUEST_ID, +- len, 0); ++ (int)len, 0); + ((FCGI_UnknownTypeRecord *) response)->body + = MakeUnknownTypeBody(type); + } +@@ -1627,6 +1658,7 @@ + FCGI_Header header; + int headerLen = 0; + int status, count; ++ intptr_t ptrDiff; + + for (;;) { + /* +@@ -1647,7 +1679,9 @@ + * more content bytes, deliver all that are present in data->buff. + */ + if(data->contentLen > 0) { +- count = min(data->contentLen, data->buffStop - stream->rdNext); ++ ptrDiff = data->buffStop - stream->rdNext; ++ ASSERT(0 <= ptrDiff && ptrDiff <= INT_MAX); ++ count = min(data->contentLen, (int)ptrDiff); + data->contentLen -= count; + if(!data->skip) { + stream->wrNext = stream->stop = stream->rdNext + count; +@@ -1666,7 +1700,9 @@ + * the client) was padded, skip over the padding bytes. + */ + if(data->paddingLen > 0) { +- count = min(data->paddingLen, data->buffStop - stream->rdNext); ++ ptrDiff = data->buffStop - stream->rdNext; ++ ASSERT(0 <= ptrDiff && ptrDiff <= INT_MAX); ++ count = min(data->paddingLen, (int)ptrDiff); + data->paddingLen -= count; + stream->rdNext += count; + if(data->paddingLen > 0) { +@@ -1685,8 +1721,10 @@ + /* + * Fill header with bytes from the input buffer. + */ ++ ptrDiff = data->buffStop - stream->rdNext; ++ ASSERT(0 <= ptrDiff && ptrDiff <= INT_MAX); + count = min((int)sizeof(header) - headerLen, +- data->buffStop - stream->rdNext); ++ (int)ptrDiff); + memcpy(((char *)(&header)) + headerLen, stream->rdNext, count); + headerLen += count; + stream->rdNext += count; +@@ -1754,6 +1792,7 @@ + static FCGX_Stream *NewStream( + FCGX_Request *reqDataPtr, int bufflen, int isReader, int streamType) + { ++ intptr_t bufflen_p; + /* + * XXX: It would be a lot cleaner to have a NewStream that only + * knows about the type FCGX_Stream, with all other +@@ -1765,7 +1804,9 @@ + FCGX_Stream *stream = (FCGX_Stream *)Malloc(sizeof(FCGX_Stream)); + FCGX_Stream_Data *data = (FCGX_Stream_Data *)Malloc(sizeof(FCGX_Stream_Data)); + data->reqDataPtr = reqDataPtr; +- bufflen = AlignInt8(min(max(bufflen, 32), FCGI_MAX_LENGTH + 1)); ++ bufflen_p = AlignInt8(min(max(bufflen, 32), FCGI_MAX_LENGTH + 1)); ++ ASSERT(0 <= bufflen_p && bufflen_p <= INT_MAX); ++ bufflen = (int)bufflen_p; + data->bufflen = bufflen; + data->mBuff = (unsigned char *)Malloc(bufflen); + data->buff = AlignPtr8(data->mBuff); +@@ -2306,4 +2347,3 @@ + FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; + data->reqDataPtr->appStatus = status; + } +- +diff -Nrub fcgi-2.4.0/libfcgi/fcgio.cpp fcgi-2.4.0-x64/libfcgi/fcgio.cpp +--- fcgi-2.4.0/libfcgi/fcgio.cpp 2002-02-25 05:12:22.000000000 +0900 ++++ fcgi-2.4.0-x64/libfcgi/fcgio.cpp 2009-07-02 13:59:25.562716500 +0900 +@@ -22,8 +22,14 @@ + #define DLLAPI __declspec(dllexport) + #endif + ++#ifndef _WIN32 ++#include + #include ++#endif ++ ++#include + #include "fcgio.h" ++#include "fcgimisc.h" + + using std::streambuf; + using std::istream; +@@ -63,12 +69,13 @@ + { + if (this->bufsize) + { +- int plen = pptr() - pbase(); ++ intptr_t plen = pptr() - pbase(); + + if (plen) + { +- if (FCGX_PutStr(pbase(), plen, this->fcgx) != plen) return EOF; +- pbump(-plen); ++ ASSERT(0 <= plen && plen <= INT_MAX); ++ if (FCGX_PutStr(pbase(), (int)plen, this->fcgx) != plen) return EOF; ++ pbump((int)(-plen)); + } + } + +@@ -103,7 +110,8 @@ + { + if (in_avail() == 0) + { +- int glen = FCGX_GetStr(eback(), this->bufsize, this->fcgx); ++ ASSERT(0 <= this->bufsize && this->bufsize <= INT_MAX); ++ int glen = FCGX_GetStr(eback(), (int)(this->bufsize), this->fcgx); + if (glen <= 0) return EOF; + + setg(eback(), eback(), eback() + glen); +diff -Nrub fcgi-2.4.0/libfcgi/os_win32.c fcgi-2.4.0-x64/libfcgi/os_win32.c +--- fcgi-2.4.0/libfcgi/os_win32.c 2002-03-06 03:15:15.000000000 +0900 ++++ fcgi-2.4.0-x64/libfcgi/os_win32.c 2009-07-02 13:59:25.566622500 +0900 +@@ -128,6 +128,16 @@ + + static BOOLEAN libInitialized = FALSE; + ++HANDLE strToHandle(char *str) ++{ ++#ifdef _WIN64 ++ return (HANDLE)_atoi64(str); ++#else ++ return (HANDLE)atoi(str); ++#endif ++} ++ ++ + /* + *-------------------------------------------------------------- + * +@@ -226,7 +236,7 @@ + static void StdinThread(void * startup) + { + int doIo = TRUE; +- unsigned long fd; ++ ULONG_PTR fd; + unsigned long bytesRead; + POVERLAPPED_REQUEST pOv; + +@@ -353,7 +363,7 @@ + val = getenv(SHUTDOWN_EVENT_NAME); + if (val != NULL) + { +- HANDLE shutdownEvent = (HANDLE) atoi(val); ++ HANDLE shutdownEvent = strToHandle(val); + + if (_beginthread(ShutdownRequestThread, 0, shutdownEvent) == -1) + { +@@ -367,7 +377,7 @@ + val = getenv(MUTEX_VARNAME); + if (val != NULL) + { +- acceptMutex = (HANDLE) atoi(val); ++ acceptMutex = strToHandle(val); + } + } + +@@ -456,8 +466,10 @@ + */ + } + ++ ASSERT(INT_MIN <= (intptr_t)stdioHandles[STDIN_FILENO] && ++ (intptr_t)stdioHandles[STDIN_FILENO] <= INT_MAX); + if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, +- (int)stdioHandles[STDIN_FILENO], ++ (int)(intptr_t)stdioHandles[STDIN_FILENO], + STDIN_FILENO)) == -1) { + return -1; + } else { +@@ -490,7 +502,7 @@ + if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL && + atoi(cLenPtr) > 0) { + hStdinThread = (HANDLE) _beginthread(StdinThread, 0, NULL); +- if (hStdinThread == (HANDLE) -1) { ++ if (hStdinThread == (HANDLE)(LONG_PTR) -1) { + printf("

OS_LibInit Failed to create STDIN thread! ERROR: %d

\r\n\r\n", + GetLastError()); + return -1; +@@ -511,8 +523,10 @@ + exit(99); + } + ++ ASSERT(INT_MIN <= (intptr_t)stdioHandles[STDOUT_FILENO] && ++ (intptr_t)stdioHandles[STDOUT_FILENO] <= INT_MAX); + if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, +- (int)stdioHandles[STDOUT_FILENO], ++ (int)(intptr_t)stdioHandles[STDOUT_FILENO], + STDOUT_FILENO)) == -1) { + return -1; + } else { +@@ -528,8 +542,10 @@ + DebugBreak(); + exit(99); + } ++ ASSERT(INT_MIN <= (intptr_t)stdioHandles[STDERR_FILENO] && ++ (intptr_t)stdioHandles[STDERR_FILENO] <= INT_MAX); + if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, +- (int)stdioHandles[STDERR_FILENO], ++ (int)(intptr_t)stdioHandles[STDERR_FILENO], + STDERR_FILENO)) == -1) { + return -1; + } else { +@@ -731,7 +747,9 @@ + return -5; + } + +- pseudoFd = Win32NewDescriptor(listenType, listenSock, -1); ++ ASSERT(INT_MIN <= (intptr_t)listenSock && ++ (intptr_t)listenSock <= INT_MAX); ++ pseudoFd = Win32NewDescriptor(listenType, (int)(intptr_t)listenSock, -1); + + if (pseudoFd == -1) + { +@@ -772,7 +790,9 @@ + return -9; + } + +- pseudoFd = Win32NewDescriptor(listenType, (int) hListenPipe, -1); ++ ASSERT(INT_MIN <= (intptr_t)hListenPipe && ++ (intptr_t)hListenPipe <= INT_MAX); ++ pseudoFd = Win32NewDescriptor(listenType, (int)(intptr_t)hListenPipe, -1); + + if (pseudoFd == -1) + { +@@ -818,7 +838,7 @@ + if (*bindPath != ':') + { + char * p = strchr(bindPath, ':'); +- int len = p - bindPath + 1; ++ intptr_t len = p - bindPath + 1; + + host = malloc(len); + strncpy(host, bindPath, len); +@@ -855,7 +875,8 @@ + return -1; + } + +- pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, sock, -1); ++ ASSERT(INT_MIN <= (intptr_t)sock && (intptr_t)sock <= INT_MAX); ++ pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, (int)(intptr_t)sock, -1); + if (pseudoFd == -1) + { + closesocket(sock); +@@ -890,7 +911,8 @@ + return -1; + } + +- pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int) hPipe, -1); ++ ASSERT(INT_MIN <= (intptr_t)hPipe && (intptr_t)hPipe <= INT_MAX); ++ pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int)(intptr_t)hPipe, -1); + + if (pseudoFd == -1) + { +@@ -945,7 +967,8 @@ + case FD_PIPE_SYNC: + case FD_PIPE_ASYNC: + +- if (ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, NULL)) ++ ASSERT(0 <= len && len <= ULONG_MAX); ++ if (ReadFile(fdTable[fd].fid.fileHandle, buf, (DWORD)len, &bytesRead, NULL)) + { + ret = bytesRead; + } +@@ -959,7 +982,8 @@ + case FD_SOCKET_SYNC: + case FD_SOCKET_ASYNC: + +- ret = recv(fdTable[fd].fid.sock, buf, len, 0); ++ ASSERT(0 <= len && len <= INT_MAX); ++ ret = recv(fdTable[fd].fid.sock, buf, (int)len, 0); + if (ret == SOCKET_ERROR) + { + fdTable[fd].Errno = WSAGetLastError(); +@@ -1008,7 +1032,8 @@ + case FD_PIPE_SYNC: + case FD_PIPE_ASYNC: + +- if (WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, NULL)) ++ ASSERT(0 <= len && len <= ULONG_MAX); ++ if (WriteFile(fdTable[fd].fid.fileHandle, buf, (DWORD)len, &bytesWritten, NULL)) + { + ret = bytesWritten; + } +@@ -1022,7 +1047,8 @@ + case FD_SOCKET_SYNC: + case FD_SOCKET_ASYNC: + +- ret = send(fdTable[fd].fid.sock, buf, len, 0); ++ ASSERT(0 <= len && len <= INT_MAX); ++ ret = send(fdTable[fd].fid.sock, buf, (int)len, 0); + if (ret == SOCKET_ERROR) + { + fdTable[fd].Errno = WSAGetLastError(); +@@ -1385,7 +1411,7 @@ + { + struct timeval tv; + fd_set rfds; +- int sock = fdTable[fd].fid.sock; ++ SOCKET sock = fdTable[fd].fid.sock; + int rv; + char trash[1024]; + +@@ -1393,10 +1419,12 @@ + + do + { ++#pragma warning( disable : 4127 ) + FD_SET(sock, &rfds); ++#pragma warning( default : 4127 ) + tv.tv_sec = 2; + tv.tv_usec = 0; +- rv = select(sock + 1, &rfds, NULL, NULL, &tv); ++ rv = select(0, &rfds, NULL, NULL, &tv); + } + while (rv > 0 && recv(sock, trash, sizeof(trash), 0) > 0); + } +@@ -1463,12 +1491,12 @@ + */ + int OS_DoIo(struct timeval *tmo) + { +- unsigned long fd; ++ ULONG_PTR fd; + unsigned long bytes; + POVERLAPPED_REQUEST pOv; + struct timeb tb; +- int ms; +- int ms_last; ++ time_t ms; ++ time_t ms_last; + int err; + + /* XXX +@@ -1486,8 +1514,9 @@ + while (ms >= 0) { + if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100) + ms = 100; ++ ASSERT(0 <= ms && ms < 0xFFFFFFFF); + if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd, +- (LPOVERLAPPED *)&pOv, ms) && !pOv) { ++ (LPOVERLAPPED *)&pOv, (DWORD)ms) && !pOv) { + err = WSAGetLastError(); + return 0; /* timeout */ + } +@@ -1542,7 +1571,7 @@ + LPWSABUF dc3, + LPWSABUF dc4, + GROUP *dc5, +- DWORD data) ++ DWORD_PTR data) + { + struct sockaddr_in *sockaddr = (struct sockaddr_in *) lpCallerId->buf; + +@@ -1613,7 +1642,8 @@ + } + } + +- ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1); ++ ASSERT(INT_MIN <= (intptr_t)hListen && (intptr_t)hListen <= INT_MAX); ++ ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)(intptr_t)hListen, -1); + if (ipcFd == -1) + { + DisconnectNamedPipe(hListen); +@@ -1640,7 +1670,7 @@ + FD_ZERO(&readfds); + + #pragma warning( disable : 4127 ) +- FD_SET((unsigned int) hListen, &readfds); ++ FD_SET((SOCKET)hListen, &readfds); + #pragma warning( default : 4127 ) + + if (select(0, &readfds, NULL, NULL, &timeout) == 0) +@@ -1672,11 +1702,11 @@ + + closesocket(hSock); + #else +- hSock = WSAAccept((unsigned int) hListen, ++ hSock = WSAAccept((SOCKET)hListen, + &sockaddr, + &sockaddrLen, + isAddrOKCallback, +- (DWORD) webServerAddrs); ++ (DWORD_PTR) webServerAddrs); + + if (hSock != INVALID_SOCKET) + { +@@ -1697,7 +1727,8 @@ + return -1; + } + +- ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1); ++ ASSERT(INT_MIN <= (intptr_t)hSock && (intptr_t)hSock <= INT_MAX); ++ ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, (int)(intptr_t)hSock, -1); + if (ipcFd == -1) + { + closesocket(hSock); diff --git a/library/server/libfcgi/Clib/libfcgi/fcgi_config.h b/library/server/libfcgi/Clib/libfcgi/fcgi_config.h new file mode 100644 index 00000000..0e3f74e0 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/fcgi_config.h @@ -0,0 +1,112 @@ +/* fcgi_config.h. Generated automatically by configure. */ +/* fcgi_config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if you have the header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define if there's a fileno() prototype in stdio.h */ +#define HAVE_FILENO_PROTO 1 + +/* Define if the fpos_t typedef is in stdio.h */ +#define HAVE_FPOS 1 + +/* Define if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if cin/cout/cerr has a streambuf assignment operator */ +/* #undef HAVE_IOSTREAM_WITHASSIGN_STREAMBUF */ + +/* Define if you have the `nsl' library (-lnsl). */ +#define HAVE_LIBNSL 1 + +/* Define if you have the `socket' library (-lsocket). */ +#define HAVE_LIBSOCKET 1 + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define if you have POSIX threads libraries and header files. */ +/* #undef HAVE_PTHREAD */ + +/* Define if sockaddr_un in sys/un.h contains a sun_len component */ +/* #undef HAVE_SOCKADDR_UN_SUN_LEN */ + +/* Define if the socklen_t typedef is in sys/socket.h */ +/* #undef HAVE_SOCKLEN */ + +/* Define if you have the header file. */ +/* #undef HAVE_STDINT_H */ + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if char_type is defined in the context of streambuf */ +#define HAVE_STREAMBUF_CHAR_TYPE 1 + +/* Define if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if va_arg(arg, long double) crashes the compiler */ +/* #undef HAVE_VA_ARG_LONG_DOUBLE_BUG */ + +/* Name of package */ +#define PACKAGE "fcgi" + +/* Define to the necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if cross-process locking is required by accept() */ +#define USE_LOCKING 1 + +/* Version number of package */ +#define VERSION "2.4.0" + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define as `__inline' if that's what the C compiler calls it, or to nothing + if it is not supported. */ +/* #undef inline */ + +/* Define to `int' if does not define. */ +/* #undef ssize_t */ diff --git a/library/server/libfcgi/Clib/libfcgi/include/Makefile.am b/library/server/libfcgi/Clib/libfcgi/include/Makefile.am new file mode 100644 index 00000000..417a5613 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/include/Makefile.am @@ -0,0 +1,8 @@ +# $Id: Makefile.am,v 1.2 2001/09/24 18:03:05 skimo Exp $ +include_HEADERS = \ + fastcgi.h \ + fcgi_stdio.h \ + fcgiapp.h \ + fcgimisc.h \ + fcgio.h \ + fcgios.h diff --git a/library/server/libfcgi/Clib/libfcgi/include/Makefile.in b/library/server/libfcgi/Clib/libfcgi/include/Makefile.in new file mode 100644 index 00000000..51ad6249 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/include/Makefile.in @@ -0,0 +1,279 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# $Id: Makefile.am,v 1.2 2001/09/24 18:03:05 skimo Exp $ + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AS = @AS@ +AWK = @AWK@ +CC = @CC@ +CPP = @CPP@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +ECHO = @ECHO@ +ECHO_CPP = @ECHO_CPP@ +EXEEXT = @EXEEXT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LIBFCGIXX = @LIBFCGIXX@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +STRIP = @STRIP@ +SYSTEM = @SYSTEM@ +THREADED = @THREADED@ +VERSION = @VERSION@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +include_HEADERS = \ + fastcgi.h \ + fcgi_stdio.h \ + fcgiapp.h \ + fcgimisc.h \ + fcgio.h \ + fcgios.h + +subdir = include +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/fcgi_config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = +HEADERS = $(include_HEADERS) + +DIST_COMMON = $(include_HEADERS) Makefile.am Makefile.in +all: all-am + +.SUFFIXES: + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign include/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: +install-includeHEADERS: $(include_HEADERS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(includedir) + @list='$(include_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(INSTALL_HEADER) $$d$$p $(DESTDIR)$(includedir)/$$f"; \ + $(INSTALL_HEADER) $$d$$p $(DESTDIR)$(includedir)/$$f; \ + done + +uninstall-includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(include_HEADERS)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f $(DESTDIR)$(includedir)/$$f"; \ + rm -f $(DESTDIR)$(includedir)/$$f; \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(includedir) + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: install-includeHEADERS + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +uninstall-am: uninstall-includeHEADERS uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-libtool distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-includeHEADERS install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool tags uninstall uninstall-am \ + uninstall-includeHEADERS uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/library/server/libfcgi/Clib/libfcgi/include/fastcgi.h b/library/server/libfcgi/Clib/libfcgi/include/fastcgi.h new file mode 100644 index 00000000..d5b54686 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/include/fastcgi.h @@ -0,0 +1,136 @@ +/* + * fastcgi.h -- + * + * Defines for the FastCGI protocol. + * + * + * Copyright (c) 1995-1996 Open Market, Inc. + * + * See the file "LICENSE.TERMS" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * $Id: fastcgi.h,v 1.1.1.1 1997/09/16 15:36:32 stanleyg Exp $ + */ + +#ifndef _FASTCGI_H +#define _FASTCGI_H + +/* + * Listening socket file number + */ +#define FCGI_LISTENSOCK_FILENO 0 + +typedef struct { + unsigned char version; + unsigned char type; + unsigned char requestIdB1; + unsigned char requestIdB0; + unsigned char contentLengthB1; + unsigned char contentLengthB0; + unsigned char paddingLength; + unsigned char reserved; +} FCGI_Header; + +#define FCGI_MAX_LENGTH 0xffff + +/* + * Number of bytes in a FCGI_Header. Future versions of the protocol + * will not reduce this number. + */ +#define FCGI_HEADER_LEN 8 + +/* + * Value for version component of FCGI_Header + */ +#define FCGI_VERSION_1 1 + +/* + * Values for type component of FCGI_Header + */ +#define FCGI_BEGIN_REQUEST 1 +#define FCGI_ABORT_REQUEST 2 +#define FCGI_END_REQUEST 3 +#define FCGI_PARAMS 4 +#define FCGI_STDIN 5 +#define FCGI_STDOUT 6 +#define FCGI_STDERR 7 +#define FCGI_DATA 8 +#define FCGI_GET_VALUES 9 +#define FCGI_GET_VALUES_RESULT 10 +#define FCGI_UNKNOWN_TYPE 11 +#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) + +/* + * Value for requestId component of FCGI_Header + */ +#define FCGI_NULL_REQUEST_ID 0 + + +typedef struct { + unsigned char roleB1; + unsigned char roleB0; + unsigned char flags; + unsigned char reserved[5]; +} FCGI_BeginRequestBody; + +typedef struct { + FCGI_Header header; + FCGI_BeginRequestBody body; +} FCGI_BeginRequestRecord; + +/* + * Mask for flags component of FCGI_BeginRequestBody + */ +#define FCGI_KEEP_CONN 1 + +/* + * Values for role component of FCGI_BeginRequestBody + */ +#define FCGI_RESPONDER 1 +#define FCGI_AUTHORIZER 2 +#define FCGI_FILTER 3 + + +typedef struct { + unsigned char appStatusB3; + unsigned char appStatusB2; + unsigned char appStatusB1; + unsigned char appStatusB0; + unsigned char protocolStatus; + unsigned char reserved[3]; +} FCGI_EndRequestBody; + +typedef struct { + FCGI_Header header; + FCGI_EndRequestBody body; +} FCGI_EndRequestRecord; + +/* + * Values for protocolStatus component of FCGI_EndRequestBody + */ +#define FCGI_REQUEST_COMPLETE 0 +#define FCGI_CANT_MPX_CONN 1 +#define FCGI_OVERLOADED 2 +#define FCGI_UNKNOWN_ROLE 3 + + +/* + * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records + */ +#define FCGI_MAX_CONNS "FCGI_MAX_CONNS" +#define FCGI_MAX_REQS "FCGI_MAX_REQS" +#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS" + + +typedef struct { + unsigned char type; + unsigned char reserved[7]; +} FCGI_UnknownTypeBody; + +typedef struct { + FCGI_Header header; + FCGI_UnknownTypeBody body; +} FCGI_UnknownTypeRecord; + +#endif /* _FASTCGI_H */ + diff --git a/library/server/libfcgi/Clib/libfcgi/include/fcgi_config.h b/library/server/libfcgi/Clib/libfcgi/include/fcgi_config.h new file mode 100644 index 00000000..c59cda80 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/include/fcgi_config.h @@ -0,0 +1,39 @@ +/* + * Copied to fcgi_config.h when building on WinNT without cygwin, + * i.e. configure is not run. See fcgi_config.h.in for details. + */ + +#define HAVE_FPOS 1 +#define HAVE_LIMITS_H 1 +#define HAVE_STREAMBUF_CHAR_TYPE 1 +#define HAVE_STRERROR 1 +#undef HAVE_ARPA_INET_H +#undef HAVE_DLFCN_H +#undef HAVE_FILENO_PROTO +#undef HAVE_INTTYPES_H +#undef HAVE_IOSTREAM_WITHASSIGN_STREAMBUF +#undef HAVE_LIBNSL +#undef HAVE_LIBSOCKET +#undef HAVE_MEMORY_H +#undef HAVE_NETDB_H +#undef HAVE_NETINET_IN_H +#undef HAVE_PTHREAD +#undef HAVE_SOCKADDR_UN_SUN_LEN +#undef HAVE_SOCKLEN +#undef HAVE_STDINT_H +#undef HAVE_STDLIB_H +#undef HAVE_STRING_H +#undef HAVE_STRINGS_H +#undef HAVE_SYS_PARAM_H +#undef HAVE_SYS_SOCKET_H +#undef HAVE_SYS_STAT_H +#undef HAVE_SYS_TIME_H +#undef HAVE_SYS_TYPES_H +#undef HAVE_UNISTD_H +#undef HAVE_VA_ARG_LONG_DOUBLE_BUG +#undef PTHREAD_CREATE_JOINABLE +#undef STDC_HEADERS +#undef USE_LOCKING +#undef const +#undef inline +#undef ssize_t diff --git a/library/server/libfcgi/Clib/libfcgi/include/fcgi_config_x86.h b/library/server/libfcgi/Clib/libfcgi/include/fcgi_config_x86.h new file mode 100644 index 00000000..c59cda80 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/include/fcgi_config_x86.h @@ -0,0 +1,39 @@ +/* + * Copied to fcgi_config.h when building on WinNT without cygwin, + * i.e. configure is not run. See fcgi_config.h.in for details. + */ + +#define HAVE_FPOS 1 +#define HAVE_LIMITS_H 1 +#define HAVE_STREAMBUF_CHAR_TYPE 1 +#define HAVE_STRERROR 1 +#undef HAVE_ARPA_INET_H +#undef HAVE_DLFCN_H +#undef HAVE_FILENO_PROTO +#undef HAVE_INTTYPES_H +#undef HAVE_IOSTREAM_WITHASSIGN_STREAMBUF +#undef HAVE_LIBNSL +#undef HAVE_LIBSOCKET +#undef HAVE_MEMORY_H +#undef HAVE_NETDB_H +#undef HAVE_NETINET_IN_H +#undef HAVE_PTHREAD +#undef HAVE_SOCKADDR_UN_SUN_LEN +#undef HAVE_SOCKLEN +#undef HAVE_STDINT_H +#undef HAVE_STDLIB_H +#undef HAVE_STRING_H +#undef HAVE_STRINGS_H +#undef HAVE_SYS_PARAM_H +#undef HAVE_SYS_SOCKET_H +#undef HAVE_SYS_STAT_H +#undef HAVE_SYS_TIME_H +#undef HAVE_SYS_TYPES_H +#undef HAVE_UNISTD_H +#undef HAVE_VA_ARG_LONG_DOUBLE_BUG +#undef PTHREAD_CREATE_JOINABLE +#undef STDC_HEADERS +#undef USE_LOCKING +#undef const +#undef inline +#undef ssize_t diff --git a/library/server/libfcgi/Clib/libfcgi/include/fcgi_stdio.h b/library/server/libfcgi/Clib/libfcgi/include/fcgi_stdio.h new file mode 100644 index 00000000..7deae531 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/include/fcgi_stdio.h @@ -0,0 +1,246 @@ +/* + * fcgi_stdio.h -- + * + * FastCGI-stdio compatibility package + * + * + * Copyright (c) 1996 Open Market, Inc. + * + * See the file "LICENSE.TERMS" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * $Id: fcgi_stdio.h,v 1.5 2001/06/22 13:21:15 robs Exp $ + */ + +#ifndef _FCGI_STDIO +#define _FCGI_STDIO 1 + +#include +#include +#include "fcgiapp.h" + +#if defined (c_plusplus) || defined (__cplusplus) +extern "C" { +#endif + +#ifndef DLLAPI +#ifdef _WIN32 +#define DLLAPI __declspec(dllimport) +#else +#define DLLAPI +#endif +#endif + +/* + * Wrapper type for FILE + */ + +typedef struct { + FILE *stdio_stream; + FCGX_Stream *fcgx_stream; +} FCGI_FILE; + +/* + * The four new functions and two new macros + */ + +DLLAPI int FCGI_Accept(void); +DLLAPI char** FCGI_Environ(void); +DLLAPI void FCGI_Finish(void); +DLLAPI int FCGI_StartFilterData(void); +DLLAPI void FCGI_SetExitStatus(int status); + +#define FCGI_ToFILE(fcgi_file) (fcgi_file->stdio_stream) +#define FCGI_ToFcgiStream(fcgi_file) (fcgi_file->fcgx_stream) + +/* + * Wrapper stdin, stdout, and stderr variables, set up by FCGI_Accept() + */ + +DLLAPI extern FCGI_FILE _fcgi_sF[]; +#define FCGI_stdin (&_fcgi_sF[0]) +#define FCGI_stdout (&_fcgi_sF[1]) +#define FCGI_stderr (&_fcgi_sF[2]) + +/* + * Wrapper function prototypes, grouped according to sections + * of Harbison & Steele, "C: A Reference Manual," fourth edition, + * Prentice-Hall, 1995. + */ + +DLLAPI void FCGI_perror(const char *str); + +DLLAPI FCGI_FILE *FCGI_fopen(const char *path, const char *mode); +DLLAPI int FCGI_fclose(FCGI_FILE *fp); +DLLAPI int FCGI_fflush(FCGI_FILE *fp); +DLLAPI FCGI_FILE *FCGI_freopen(const char *path, const char *mode, FCGI_FILE *fp); + +DLLAPI int FCGI_setvbuf(FCGI_FILE *fp, char *buf, int bufmode, size_t size); +DLLAPI void FCGI_setbuf(FCGI_FILE *fp, char *buf); + +DLLAPI int FCGI_fseek(FCGI_FILE *fp, long offset, int whence); +DLLAPI int FCGI_ftell(FCGI_FILE *fp); +DLLAPI void FCGI_rewind(FCGI_FILE *fp); +#ifdef HAVE_FPOS +DLLAPI int FCGI_fgetpos(FCGI_FILE *fp, fpos_t *pos); +DLLAPI int FCGI_fsetpos(FCGI_FILE *fp, const fpos_t *pos); +#endif +DLLAPI int FCGI_fgetc(FCGI_FILE *fp); +DLLAPI int FCGI_getchar(void); +DLLAPI int FCGI_ungetc(int c, FCGI_FILE *fp); + +DLLAPI char *FCGI_fgets(char *str, int size, FCGI_FILE *fp); +DLLAPI char *FCGI_gets(char *str); + +/* + * Not yet implemented + * + * int FCGI_fscanf(FCGI_FILE *fp, const char *format, ...); + * int FCGI_scanf(const char *format, ...); + * + */ + +DLLAPI int FCGI_fputc(int c, FCGI_FILE *fp); +DLLAPI int FCGI_putchar(int c); + +DLLAPI int FCGI_fputs(const char *str, FCGI_FILE *fp); +DLLAPI int FCGI_puts(const char *str); + +DLLAPI int FCGI_fprintf(FCGI_FILE *fp, const char *format, ...); +DLLAPI int FCGI_printf(const char *format, ...); + +DLLAPI int FCGI_vfprintf(FCGI_FILE *fp, const char *format, va_list ap); +DLLAPI int FCGI_vprintf(const char *format, va_list ap); + +DLLAPI size_t FCGI_fread(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp); +DLLAPI size_t FCGI_fwrite(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp); + +DLLAPI int FCGI_feof(FCGI_FILE *fp); +DLLAPI int FCGI_ferror(FCGI_FILE *fp); +DLLAPI void FCGI_clearerr(FCGI_FILE *fp); + +DLLAPI FCGI_FILE *FCGI_tmpfile(void); + +DLLAPI int FCGI_fileno(FCGI_FILE *fp); +DLLAPI FCGI_FILE *FCGI_fdopen(int fd, const char *mode); +DLLAPI FCGI_FILE *FCGI_popen(const char *cmd, const char *type); +DLLAPI int FCGI_pclose(FCGI_FILE *); + +/* + * The remaining definitions are for application programs, + * not for fcgi_stdio.c + */ + +#ifndef NO_FCGI_DEFINES + +/* + * Replace standard types, variables, and functions with FastCGI wrappers. + * Use undef in case a macro is already defined. + */ + +#undef FILE +#define FILE FCGI_FILE + +#undef stdin +#define stdin FCGI_stdin +#undef stdout +#define stdout FCGI_stdout +#undef stderr +#define stderr FCGI_stderr + +#undef perror +#define perror FCGI_perror + +#undef fopen +#define fopen FCGI_fopen +#undef fclose +#define fclose FCGI_fclose +#undef fflush +#define fflush FCGI_fflush +#undef freopen +#define freopen FCGI_freopen + +#undef setvbuf +#define setvbuf FCGI_setvbuf +#undef setbuf +#define setbuf FCGI_setbuf + +#undef fseek +#define fseek FCGI_fseek +#undef ftell +#define ftell FCGI_ftell +#undef rewind +#define rewind FCGI_rewind +#undef fgetpos +#define fgetpos FCGI_fgetpos +#undef fsetpos +#define fsetpos FCGI_fsetpos + +#undef fgetc +#define fgetc FCGI_fgetc +#undef getc +#define getc FCGI_fgetc +#undef getchar +#define getchar FCGI_getchar +#undef ungetc +#define ungetc FCGI_ungetc + +#undef fgets +#define fgets FCGI_fgets +#undef gets +#define gets FCGI_gets + +#undef fputc +#define fputc FCGI_fputc +#undef putc +#define putc FCGI_fputc +#undef putchar +#define putchar FCGI_putchar + +#undef fputs +#define fputs FCGI_fputs +#undef puts +#define puts FCGI_puts + +#undef fprintf +#define fprintf FCGI_fprintf +#undef printf +#define printf FCGI_printf + +#undef vfprintf +#define vfprintf FCGI_vfprintf +#undef vprintf +#define vprintf FCGI_vprintf + +#undef fread +#define fread FCGI_fread +#undef fwrite +#define fwrite FCGI_fwrite + +#undef feof +#define feof FCGI_feof +#undef ferror +#define ferror FCGI_ferror +#undef clearerr +#define clearerr FCGI_clearerr + +#undef tmpfile +#define tmpfile FCGI_tmpfile + +#undef fileno +#define fileno FCGI_fileno +#undef fdopen +#define fdopen FCGI_fdopen +#undef popen +#define popen FCGI_popen +#undef pclose +#define pclose FCGI_pclose + +#endif /* NO_FCGI_DEFINES */ + +#if defined (__cplusplus) || defined (c_plusplus) +} /* terminate extern "C" { */ +#endif + +#endif /* _FCGI_STDIO */ + diff --git a/library/server/libfcgi/Clib/libfcgi/include/fcgiapp.h b/library/server/libfcgi/Clib/libfcgi/include/fcgiapp.h new file mode 100644 index 00000000..d7236f6f --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/include/fcgiapp.h @@ -0,0 +1,622 @@ +/* + * fcgiapp.h -- + * + * Definitions for FastCGI application server programs + * + * + * Copyright (c) 1996 Open Market, Inc. + * + * See the file "LICENSE.TERMS" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * $Id: fcgiapp.h,v 1.12 2001/11/21 21:10:11 robs Exp $ + */ + +#ifndef _FCGIAPP_H +#define _FCGIAPP_H + +/* Hack to see if we are building TCL - TCL needs varargs not stdarg */ +#ifndef TCL_LIBRARY +#include +#else +#include +#endif + +#ifndef DLLAPI +#ifdef _WIN32 +#define DLLAPI __declspec(dllimport) +#else +#define DLLAPI +#endif +#endif + +#if defined (c_plusplus) || defined (__cplusplus) +extern "C" { +#endif + +/* + * Error codes. Assigned to avoid conflict with EOF and errno(2). + */ +#define FCGX_UNSUPPORTED_VERSION -2 +#define FCGX_PROTOCOL_ERROR -3 +#define FCGX_PARAMS_ERROR -4 +#define FCGX_CALL_SEQ_ERROR -5 + +/* + * This structure defines the state of a FastCGI stream. + * Streams are modeled after the FILE type defined in stdio.h. + * (We wouldn't need our own if platform vendors provided a + * standard way to subclass theirs.) + * The state of a stream is private and should only be accessed + * by the procedures defined below. + */ +typedef struct FCGX_Stream { + unsigned char *rdNext; /* reader: first valid byte + * writer: equals stop */ + unsigned char *wrNext; /* writer: first free byte + * reader: equals stop */ + unsigned char *stop; /* reader: last valid byte + 1 + * writer: last free byte + 1 */ + unsigned char *stopUnget; /* reader: first byte of current buffer + * fragment, for ungetc + * writer: undefined */ + int isReader; + int isClosed; + int wasFCloseCalled; + int FCGI_errno; /* error status */ + void (*fillBuffProc) (struct FCGX_Stream *stream); + void (*emptyBuffProc) (struct FCGX_Stream *stream, int doClose); + void *data; +} FCGX_Stream; + +/* + * An environment (as defined by environ(7)): A NULL-terminated array + * of strings, each string having the form name=value. + */ +typedef char **FCGX_ParamArray; + +/* + * FCGX_Request Flags + * + * Setting FCGI_FAIL_ACCEPT_ON_INTR prevents FCGX_Accept() from + * restarting upon being interrupted. + */ +#define FCGI_FAIL_ACCEPT_ON_INTR 1 + +/* + * FCGX_Request -- State associated with a request. + * + * Its exposed for API simplicity, I expect parts of it to change! + */ +typedef struct FCGX_Request { + int requestId; /* valid if isBeginProcessed */ + int role; + FCGX_Stream *in; + FCGX_Stream *out; + FCGX_Stream *err; + char **envp; + + /* Don't use anything below here */ + + struct Params *paramsPtr; + int ipcFd; /* < 0 means no connection */ + int isBeginProcessed; /* FCGI_BEGIN_REQUEST seen */ + int keepConnection; /* don't close ipcFd at end of request */ + int appStatus; + int nWriters; /* number of open writers (0..2) */ + int flags; + int listen_sock; +} FCGX_Request; + + +/* + *====================================================================== + * Control + *====================================================================== + */ + +/* + *---------------------------------------------------------------------- + * + * FCGX_IsCGI -- + * + * Returns TRUE iff this process appears to be a CGI process + * rather than a FastCGI process. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_IsCGI(void); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Init -- + * + * Initialize the FCGX library. Call in multi-threaded apps + * before calling FCGX_Accept_r(). + * + * Returns 0 upon success. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_Init(void); + +/* + *---------------------------------------------------------------------- + * + * FCGX_OpenSocket -- + * + * Create a FastCGI listen socket. + * + * path is the Unix domain socket (named pipe for WinNT), or a colon + * followed by a port number. e.g. "/tmp/fastcgi/mysocket", ":5000" + * + * backlog is the listen queue depth used in the listen() call. + * + * Returns the socket's file descriptor or -1 on error. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_OpenSocket(const char *path, int backlog); + +/* + *---------------------------------------------------------------------- + * + * FCGX_InitRequest -- + * + * Initialize a FCGX_Request for use with FCGX_Accept_r(). + * + * sock is a file descriptor returned by FCGX_OpenSocket() or 0 (default). + * The only supported flag at this time is FCGI_FAIL_ON_INTR. + * + * Returns 0 upon success. + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_InitRequest(FCGX_Request *request, int sock, int flags); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Accept_r -- + * + * Accept a new request (multi-thread safe). Be sure to call + * FCGX_Init() first. + * + * Results: + * 0 for successful call, -1 for error. + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * Creates input, output, and error streams and + * assigns them to *in, *out, and *err respectively. + * Creates a parameters data structure to be accessed + * via getenv(3) (if assigned to environ) or by FCGX_GetParam + * and assigns it to *envp. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + * DON'T use the FCGX_Request, its structure WILL change. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_Accept_r(FCGX_Request *request); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Finish_r -- + * + * Finish the request (multi-thread safe). + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_Finish_r(FCGX_Request *request); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Free -- + * + * Free the memory and, if close is true, + * IPC FD associated with the request (multi-thread safe). + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_Free(FCGX_Request * request, int close); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Accept -- + * + * Accept a new request (NOT multi-thread safe). + * + * Results: + * 0 for successful call, -1 for error. + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * Creates input, output, and error streams and + * assigns them to *in, *out, and *err respectively. + * Creates a parameters data structure to be accessed + * via getenv(3) (if assigned to environ) or by FCGX_GetParam + * and assigns it to *envp. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_Accept( + FCGX_Stream **in, + FCGX_Stream **out, + FCGX_Stream **err, + FCGX_ParamArray *envp); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Finish -- + * + * Finish the current request (NOT multi-thread safe). + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_Finish(void); + +/* + *---------------------------------------------------------------------- + * + * FCGX_StartFilterData -- + * + * stream is an input stream for a FCGI_FILTER request. + * stream is positioned at EOF on FCGI_STDIN. + * Repositions stream to the start of FCGI_DATA. + * If the preconditions are not met (e.g. FCGI_STDIN has not + * been read to EOF) sets the stream error code to + * FCGX_CALL_SEQ_ERROR. + * + * Results: + * 0 for a normal return, < 0 for error + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_StartFilterData(FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_SetExitStatus -- + * + * Sets the exit status for stream's request. The exit status + * is the status code the request would have exited with, had + * the request been run as a CGI program. You can call + * SetExitStatus several times during a request; the last call + * before the request ends determines the value. + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_SetExitStatus(int status, FCGX_Stream *stream); + +/* + *====================================================================== + * Parameters + *====================================================================== + */ + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetParam -- obtain value of FCGI parameter in environment + * + * + * Results: + * Value bound to name, NULL if name not present in the + * environment envp. Caller must not mutate the result + * or retain it past the end of this request. + * + *---------------------------------------------------------------------- + */ +DLLAPI char *FCGX_GetParam(const char *name, FCGX_ParamArray envp); + +/* + *====================================================================== + * Readers + *====================================================================== + */ + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetChar -- + * + * Reads a byte from the input stream and returns it. + * + * Results: + * The byte, or EOF (-1) if the end of input has been reached. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_GetChar(FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_UnGetChar -- + * + * Pushes back the character c onto the input stream. One + * character of pushback is guaranteed once a character + * has been read. No pushback is possible for EOF. + * + * Results: + * Returns c if the pushback succeeded, EOF if not. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_UnGetChar(int c, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetStr -- + * + * Reads up to n consecutive bytes from the input stream + * into the character array str. Performs no interpretation + * of the input bytes. + * + * Results: + * Number of bytes read. If result is smaller than n, + * the end of input has been reached. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_GetStr(char *str, int n, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetLine -- + * + * Reads up to n-1 consecutive bytes from the input stream + * into the character array str. Stops before n-1 bytes + * have been read if '\n' or EOF is read. The terminating '\n' + * is copied to str. After copying the last byte into str, + * stores a '\0' terminator. + * + * Results: + * NULL if EOF is the first thing read from the input stream, + * str otherwise. + * + *---------------------------------------------------------------------- + */ +DLLAPI char *FCGX_GetLine(char *str, int n, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_HasSeenEOF -- + * + * Returns EOF if end-of-file has been detected while reading + * from stream; otherwise returns 0. + * + * Note that FCGX_HasSeenEOF(s) may return 0, yet an immediately + * following FCGX_GetChar(s) may return EOF. This function, like + * the standard C stdio function feof, does not provide the + * ability to peek ahead. + * + * Results: + * EOF if end-of-file has been detected, 0 if not. + * + *---------------------------------------------------------------------- + */ + +DLLAPI int FCGX_HasSeenEOF(FCGX_Stream *stream); + +/* + *====================================================================== + * Writers + *====================================================================== + */ + +/* + *---------------------------------------------------------------------- + * + * FCGX_PutChar -- + * + * Writes a byte to the output stream. + * + * Results: + * The byte, or EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_PutChar(int c, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_PutStr -- + * + * Writes n consecutive bytes from the character array str + * into the output stream. Performs no interpretation + * of the output bytes. + * + * Results: + * Number of bytes written (n) for normal return, + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_PutS -- + * + * Writes a null-terminated character string to the output stream. + * + * Results: + * number of bytes written for normal return, + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_PutS(const char *str, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_FPrintF, FCGX_VFPrintF -- + * + * Performs printf-style output formatting and writes the results + * to the output stream. + * + * Results: + * number of bytes written for normal return, + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_FPrintF(FCGX_Stream *stream, const char *format, ...); + +DLLAPI int FCGX_VFPrintF(FCGX_Stream *stream, const char *format, va_list arg); + +/* + *---------------------------------------------------------------------- + * + * FCGX_FFlush -- + * + * Flushes any buffered output. + * + * Server-push is a legitimate application of FCGX_FFlush. + * Otherwise, FCGX_FFlush is not very useful, since FCGX_Accept + * does it implicitly. Calling FCGX_FFlush in non-push applications + * results in extra writes and therefore reduces performance. + * + * Results: + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_FFlush(FCGX_Stream *stream); + +/* + *====================================================================== + * Both Readers and Writers + *====================================================================== + */ + +/* + *---------------------------------------------------------------------- + * + * FCGX_FClose -- + * + * Closes the stream. For writers, flushes any buffered + * output. + * + * Close is not a very useful operation since FCGX_Accept + * does it implicitly. Closing the out stream before the + * err stream results in an extra write if there's nothing + * in the err stream, and therefore reduces performance. + * + * Results: + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_FClose(FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetError -- + * + * Return the stream error code. 0 means no error, > 0 + * is an errno(2) error, < 0 is an FastCGI error. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_GetError(FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_ClearError -- + * + * Clear the stream error code and end-of-file indication. + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_ClearError(FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_CreateWriter -- + * + * Create a FCGX_Stream (used by cgi-fcgi). This shouldn't + * be needed by a FastCGI applictaion. + * + *---------------------------------------------------------------------- + */ +DLLAPI FCGX_Stream *FCGX_CreateWriter( + int socket, + int requestId, + int bufflen, + int streamType); + +/* + *---------------------------------------------------------------------- + * + * FCGX_FreeStream -- + * + * Free a FCGX_Stream (used by cgi-fcgi). This shouldn't + * be needed by a FastCGI applictaion. + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_FreeStream(FCGX_Stream **stream); + +/* ---------------------------------------------------------------------- + * + * Prevent the lib from accepting any new requests. Signal handler safe. + * + * ---------------------------------------------------------------------- + */ +DLLAPI void FCGX_ShutdownPending(void); + +#if defined (__cplusplus) || defined (c_plusplus) +} /* terminate extern "C" { */ +#endif + +#endif /* _FCGIAPP_H */ diff --git a/library/server/libfcgi/Clib/libfcgi/include/fcgimisc.h b/library/server/libfcgi/Clib/libfcgi/include/fcgimisc.h new file mode 100644 index 00000000..0464455f --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/include/fcgimisc.h @@ -0,0 +1,38 @@ +/* + * fcgimisc.h -- + * + * Miscellaneous definitions + * + * + * Copyright (c) 1996 Open Market, Inc. + * + * See the file "LICENSE.TERMS" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * $Id: fcgimisc.h,v 1.3 2001/06/18 14:25:47 robs Exp $ + */ + +#ifndef _FCGIMISC_H +#define _FCGIMISC_H + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (1) +#endif + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef ASSERT +#define ASSERT(assertion) assert(assertion) +#endif + +#endif /* _FCGIMISC_H */ diff --git a/library/server/libfcgi/Clib/libfcgi/include/fcgio.h b/library/server/libfcgi/Clib/libfcgi/include/fcgio.h new file mode 100644 index 00000000..20d222ab --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/include/fcgio.h @@ -0,0 +1,151 @@ +// +// Provides support for FastCGI via C++ iostreams. +// +// $Id: fcgio.h,v 1.15 2002/02/25 13:16:11 robs Exp $ +// +// This work is based on routines written by George Feinberg. They +// have been mostly re-written and extensively changed by +// Michael Richards. +// +// Rewritten again with bug fixes and numerous enhancements by +// Michael Shell. +// +// And rewritten again by Rob Saccoccio. +// +// Special Thanks to Dietmar Kuehl for his help and the numerous custom +// streambuf examples on his web site. +// +// Copyright (c) 2000 Tux the Linux Penguin +// Copyright (c) 2001 Rob Saccoccio and Chelsea Networks +// +// You are free to use this software without charge or royalty +// as long as this notice is not removed or altered, and recognition +// is given to the author(s) +// +// This code is offered as-is without any warranty either expressed or +// implied; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. If it breaks, you get to keep +// both halves. + +#ifndef FCGIO_H +#define FCGIO_H + +#include + +#include "fcgiapp.h" + +#ifndef DLLAPI +#ifdef _WIN32 +#define DLLAPI __declspec(dllimport) +#else +#define DLLAPI +#endif +#endif + +#if ! HAVE_STREAMBUF_CHAR_TYPE +typedef char char_type; +#endif + +/* + * fcgi_streambuf + */ +class DLLAPI fcgi_streambuf : public std::streambuf +{ +public: + + // Note that if no buf is assigned (the default), iostream methods + // such as peek(), unget() and putback() will fail. If a buf is + // assigned, I/O is a bit less effecient and output streams will + // have to be flushed (or the streambuf destroyed) before the next + // call to "accept". + fcgi_streambuf(FCGX_Stream * fcgx, char * buf, int len); + + fcgi_streambuf(char_type * buf, std::streamsize len); + + fcgi_streambuf(FCGX_Stream * fcgx = 0); + + ~fcgi_streambuf(void); + + int attach(FCGX_Stream * fcgx); + +protected: + + // Consume the put area (if buffered) and c (if c is not EOF). + virtual int overflow(int); + + // Flush the put area (if buffered) and the FCGX buffer to the client. + virtual int sync(); + + // Remove and return the current character. + virtual int uflow(); + + // Fill the get area (if buffered) and return the current character. + virtual int underflow(); + + // Use a buffer. The only reasons that a buffer would be useful is + // to support the use of the unget()/putback() or seek() methods. Using + // a buffer will result in less efficient I/O. Note: the underlying + // FastCGI library (FCGX) maintains its own input and output buffers. + virtual std::streambuf * setbuf(char_type * buf, std::streamsize len); + + virtual std::streamsize xsgetn(char_type * s, std::streamsize n); + virtual std::streamsize xsputn(const char_type * s, std::streamsize n); + +private: + + FCGX_Stream * fcgx; + + // buf is just handy to have around + char_type * buf; + + // this isn't kept by the base class + std::streamsize bufsize; + + void init(FCGX_Stream * fcgx, char_type * buf, std::streamsize bufsize); + + void reset(void); +}; + +/* + * fcgi_istream - deprecated + */ +class DLLAPI fcgi_istream : public std::istream +{ +public: + + // deprecated + fcgi_istream(FCGX_Stream * fcgx = 0); + + // deprecated + ~fcgi_istream(void) {} + + // deprecated + virtual void attach(FCGX_Stream * fcgx); + +private: + + fcgi_streambuf fcgi_strmbuf; +}; + +/* + * fcgi_ostream - deprecated + */ +class DLLAPI fcgi_ostream : public std::ostream +{ +public: + + // deprecated + fcgi_ostream(FCGX_Stream * fcgx = 0); + + // deprecated + ~fcgi_ostream(void) {} + + // deprecated + virtual void attach(FCGX_Stream *fcgx); + +private: + + fcgi_streambuf fcgi_strmbuf; +}; + +#endif /* FCGIO_H */ diff --git a/library/server/libfcgi/Clib/libfcgi/include/fcgios.h b/library/server/libfcgi/Clib/libfcgi/include/fcgios.h new file mode 100644 index 00000000..de7c3c7c --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/include/fcgios.h @@ -0,0 +1,130 @@ +/* + * fcgios.h -- + * + * Description of file. + * + * + * Copyright (c) 1996 Open Market, Inc. + * All rights reserved. + * + * This file contains proprietary and confidential information and + * remains the unpublished property of Open Market, Inc. Use, + * disclosure, or reproduction is prohibited except as permitted by + * express written license agreement with Open Market, Inc. + * + * Bill Snapper + * snapper@openmarket.com + */ +#ifndef _FCGIOS_H +#define _FCGIOS_H + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + +#include "fcgi_config.h" + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#if defined (c_plusplus) || defined (__cplusplus) +extern "C" { +#endif + +#ifdef _WIN32 +#define OS_Errno GetLastError() +#define OS_SetErrno(err) SetLastError(err) +#ifndef O_NONBLOCK +#define O_NONBLOCK 0x0004 /* no delay */ +#endif +#else /* !_WIN32 */ +#define OS_Errno errno +#define OS_SetErrno(err) errno = (err) +#endif /* !_WIN32 */ + +#ifndef DLLAPI +#ifdef _WIN32 +#define DLLAPI __declspec(dllimport) +#else +#define DLLAPI +#endif +#endif + + +/* This is the initializer for a "struct timeval" used in a select() call + * right after a new request is accept()ed to determine readablity. Its + * a drop-dead timer. Its only used for AF_UNIX sockets (not TCP sockets). + * Its a workaround for a kernel bug in Linux 2.0.x and SCO Unixware. + * Making this as small as possible, yet remain reliable would be best. + * 2 seconds is very conservative. 0,0 is not reliable. The shorter the + * timeout, the faster request processing will recover. The longer the + * timeout, the more likely this application being "busy" will cause other + * requests to abort and cause more dead sockets that need this timeout. */ +#define READABLE_UNIX_FD_DROP_DEAD_TIMEVAL 2,0 + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +#ifndef X_OK +#define X_OK 0x01 +#endif + +#ifndef _CLIENTDATA +# if defined(__STDC__) || defined(__cplusplus) + typedef void *ClientData; +# else + typedef int *ClientData; +# endif /* __STDC__ */ +#define _CLIENTDATA +#endif + +typedef void (*OS_AsyncProc) (ClientData clientData, int len); + +DLLAPI int OS_LibInit(int stdioFds[3]); +DLLAPI void OS_LibShutdown(void); +DLLAPI int OS_CreateLocalIpcFd(const char *bindPath, int backlog); +DLLAPI int OS_FcgiConnect(char *bindPath); +DLLAPI int OS_Read(int fd, char * buf, size_t len); +DLLAPI int OS_Write(int fd, char * buf, size_t len); +DLLAPI int OS_SpawnChild(char *execPath, int listenFd); +DLLAPI int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, + ClientData clientData); +DLLAPI int OS_AsyncRead(int fd, int offset, void *buf, int len, + OS_AsyncProc procPtr, ClientData clientData); +DLLAPI int OS_AsyncWrite(int fd, int offset, void *buf, int len, + OS_AsyncProc procPtr, ClientData clientData); +DLLAPI int OS_Close(int fd); +DLLAPI int OS_CloseRead(int fd); +DLLAPI int OS_DoIo(struct timeval *tmo); +DLLAPI int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs); +DLLAPI int OS_IpcClose(int ipcFd); +DLLAPI int OS_IsFcgi(int sock); +DLLAPI void OS_SetFlags(int fd, int flags); + +DLLAPI void OS_ShutdownPending(void); + +#if defined (__cplusplus) || defined (c_plusplus) +} /* terminate extern "C" { */ +#endif + +#endif /* _FCGIOS_H */ diff --git a/library/server/libfcgi/Clib/libfcgi/libfcgi/Makefile.am b/library/server/libfcgi/Clib/libfcgi/libfcgi/Makefile.am new file mode 100644 index 00000000..0f146156 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/libfcgi/Makefile.am @@ -0,0 +1,27 @@ +# $Id: Makefile.am,v 1.9 2001/12/22 03:16:20 robs Exp $ + +INCLUDEDIR = ../include +CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir)/include + +INCLUDE_FILES = $(INCLUDEDIR)/fastcgi.h \ + $(INCLUDEDIR)/fcgiapp.h \ + $(INCLUDEDIR)/fcgimisc.h \ + $(INCLUDEDIR)/fcgi_stdio.h \ + $(INCLUDEDIR)/fcgios.h + +lib_LTLIBRARIES = libfcgi.la @LIBFCGIXX@ +EXTRA_LTLIBRARIES = libfcgi++.la + +libfcgi_la_SOURCES = $(INCLUDE_FILES) \ + fcgiapp.c \ + fcgi_stdio.c \ + os_@SYSTEM@.c +libfcgi_la_CC = @PTHREAD_CC@ +libfcgi_la_CFLAGS = @PTHREAD_CFLAGS@ + +libfcgi___la_SOURCES = $(INCLUDE_FILES) \ + $(INCLUDEDIR)/fcgio.h \ + fcgio.cpp +libfcgi___la_CFLAGS = @PTHREAD_CFLAGS@ +libfcgi___la_LDFLAGS = -lfcgi -rpath @libdir@ + diff --git a/library/server/libfcgi/Clib/libfcgi/libfcgi/Makefile.in b/library/server/libfcgi/Clib/libfcgi/libfcgi/Makefile.in new file mode 100644 index 00000000..420ca0bd --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/libfcgi/Makefile.in @@ -0,0 +1,454 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# $Id: Makefile.am,v 1.9 2001/12/22 03:16:20 robs Exp $ + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AS = @AS@ +AWK = @AWK@ +CC = @CC@ +CPP = @CPP@ +CXX = @CXX@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +ECHO = @ECHO@ +ECHO_CPP = @ECHO_CPP@ +EXEEXT = @EXEEXT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LIBFCGIXX = @LIBFCGIXX@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +STRIP = @STRIP@ +SYSTEM = @SYSTEM@ +THREADED = @THREADED@ +VERSION = @VERSION@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +INCLUDEDIR = ../include +CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir)/include + +INCLUDE_FILES = $(INCLUDEDIR)/fastcgi.h \ + $(INCLUDEDIR)/fcgiapp.h \ + $(INCLUDEDIR)/fcgimisc.h \ + $(INCLUDEDIR)/fcgi_stdio.h \ + $(INCLUDEDIR)/fcgios.h + + +lib_LTLIBRARIES = libfcgi.la @LIBFCGIXX@ +EXTRA_LTLIBRARIES = libfcgi++.la + +libfcgi_la_SOURCES = $(INCLUDE_FILES) \ + fcgiapp.c \ + fcgi_stdio.c \ + os_@SYSTEM@.c + +libfcgi_la_CC = @PTHREAD_CC@ +libfcgi_la_CFLAGS = @PTHREAD_CFLAGS@ + +libfcgi___la_SOURCES = $(INCLUDE_FILES) \ + $(INCLUDEDIR)/fcgio.h \ + fcgio.cpp + +libfcgi___la_CFLAGS = @PTHREAD_CFLAGS@ +libfcgi___la_LDFLAGS = -lfcgi -rpath @libdir@ +subdir = libfcgi +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/fcgi_config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(lib_LTLIBRARIES) + +libfcgi___la_LIBADD = +am_libfcgi___la_OBJECTS = fcgio.lo +libfcgi___la_OBJECTS = $(am_libfcgi___la_OBJECTS) +libfcgi_la_LDFLAGS = +libfcgi_la_LIBADD = +am_libfcgi_la_OBJECTS = libfcgi_la-fcgiapp.lo libfcgi_la-fcgi_stdio.lo \ + libfcgi_la-os_@SYSTEM@.lo +libfcgi_la_OBJECTS = $(am_libfcgi_la_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/fcgio.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/libfcgi_la-fcgi_stdio.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/libfcgi_la-fcgiapp.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/libfcgi_la-os_@SYSTEM@.Plo +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \ + $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CXXFLAGS = @CXXFLAGS@ +DIST_SOURCES = $(libfcgi___la_SOURCES) $(libfcgi_la_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libfcgi___la_SOURCES) $(libfcgi_la_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .cpp .lo .o .obj + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign libfcgi/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(libdir) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(LIBTOOL) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$p"; \ + $(LIBTOOL) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$p; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + echo " $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p"; \ + $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) +libfcgi++.la: $(libfcgi___la_OBJECTS) $(libfcgi___la_DEPENDENCIES) + $(CXXLINK) $(libfcgi___la_LDFLAGS) $(libfcgi___la_OBJECTS) $(libfcgi___la_LIBADD) $(LIBS) +libfcgi_la-fcgiapp.lo: fcgiapp.c +libfcgi_la-fcgi_stdio.lo: fcgi_stdio.c +libfcgi_la-os_@SYSTEM@.lo: os_@SYSTEM@.c +libfcgi.la: $(libfcgi_la_OBJECTS) $(libfcgi_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libfcgi_la_LDFLAGS) $(libfcgi_la_OBJECTS) $(libfcgi_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/fcgio.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/libfcgi_la-fcgi_stdio.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/libfcgi_la-fcgiapp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/libfcgi_la-os_@SYSTEM@.Plo@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.c.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$< + +.c.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `cygpath -w $<` + +.c.lo: +@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LTCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +libfcgi_la-fcgiapp.o: fcgiapp.c +@AMDEP_TRUE@ source='fcgiapp.c' object='libfcgi_la-fcgiapp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/libfcgi_la-fcgiapp.Po' tmpdepfile='$(DEPDIR)/libfcgi_la-fcgiapp.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfcgi_la_CFLAGS) $(CFLAGS) -c -o libfcgi_la-fcgiapp.o `test -f fcgiapp.c || echo '$(srcdir)/'`fcgiapp.c + +libfcgi_la-fcgiapp.obj: fcgiapp.c +@AMDEP_TRUE@ source='fcgiapp.c' object='libfcgi_la-fcgiapp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/libfcgi_la-fcgiapp.Po' tmpdepfile='$(DEPDIR)/libfcgi_la-fcgiapp.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfcgi_la_CFLAGS) $(CFLAGS) -c -o libfcgi_la-fcgiapp.obj `cygpath -w fcgiapp.c` + +libfcgi_la-fcgiapp.lo: fcgiapp.c +@AMDEP_TRUE@ source='fcgiapp.c' object='libfcgi_la-fcgiapp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/libfcgi_la-fcgiapp.Plo' tmpdepfile='$(DEPDIR)/libfcgi_la-fcgiapp.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfcgi_la_CFLAGS) $(CFLAGS) -c -o libfcgi_la-fcgiapp.lo `test -f fcgiapp.c || echo '$(srcdir)/'`fcgiapp.c + +libfcgi_la-fcgi_stdio.o: fcgi_stdio.c +@AMDEP_TRUE@ source='fcgi_stdio.c' object='libfcgi_la-fcgi_stdio.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/libfcgi_la-fcgi_stdio.Po' tmpdepfile='$(DEPDIR)/libfcgi_la-fcgi_stdio.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfcgi_la_CFLAGS) $(CFLAGS) -c -o libfcgi_la-fcgi_stdio.o `test -f fcgi_stdio.c || echo '$(srcdir)/'`fcgi_stdio.c + +libfcgi_la-fcgi_stdio.obj: fcgi_stdio.c +@AMDEP_TRUE@ source='fcgi_stdio.c' object='libfcgi_la-fcgi_stdio.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/libfcgi_la-fcgi_stdio.Po' tmpdepfile='$(DEPDIR)/libfcgi_la-fcgi_stdio.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfcgi_la_CFLAGS) $(CFLAGS) -c -o libfcgi_la-fcgi_stdio.obj `cygpath -w fcgi_stdio.c` + +libfcgi_la-fcgi_stdio.lo: fcgi_stdio.c +@AMDEP_TRUE@ source='fcgi_stdio.c' object='libfcgi_la-fcgi_stdio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/libfcgi_la-fcgi_stdio.Plo' tmpdepfile='$(DEPDIR)/libfcgi_la-fcgi_stdio.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfcgi_la_CFLAGS) $(CFLAGS) -c -o libfcgi_la-fcgi_stdio.lo `test -f fcgi_stdio.c || echo '$(srcdir)/'`fcgi_stdio.c + +libfcgi_la-os_@SYSTEM@.o: os_@SYSTEM@.c +@AMDEP_TRUE@ source='os_@SYSTEM@.c' object='libfcgi_la-os_@SYSTEM@.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/libfcgi_la-os_@SYSTEM@.Po' tmpdepfile='$(DEPDIR)/libfcgi_la-os_@SYSTEM@.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfcgi_la_CFLAGS) $(CFLAGS) -c -o libfcgi_la-os_@SYSTEM@.o `test -f os_@SYSTEM@.c || echo '$(srcdir)/'`os_@SYSTEM@.c + +libfcgi_la-os_@SYSTEM@.obj: os_@SYSTEM@.c +@AMDEP_TRUE@ source='os_@SYSTEM@.c' object='libfcgi_la-os_@SYSTEM@.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/libfcgi_la-os_@SYSTEM@.Po' tmpdepfile='$(DEPDIR)/libfcgi_la-os_@SYSTEM@.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfcgi_la_CFLAGS) $(CFLAGS) -c -o libfcgi_la-os_@SYSTEM@.obj `cygpath -w os_@SYSTEM@.c` + +libfcgi_la-os_@SYSTEM@.lo: os_@SYSTEM@.c +@AMDEP_TRUE@ source='os_@SYSTEM@.c' object='libfcgi_la-os_@SYSTEM@.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/libfcgi_la-os_@SYSTEM@.Plo' tmpdepfile='$(DEPDIR)/libfcgi_la-os_@SYSTEM@.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfcgi_la_CFLAGS) $(CFLAGS) -c -o libfcgi_la-os_@SYSTEM@.lo `test -f os_@SYSTEM@.c || echo '$(srcdir)/'`os_@SYSTEM@.c +CCDEPMODE = @CCDEPMODE@ + +.cpp.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< + +.cpp.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CXXCOMPILE) -c -o $@ `cygpath -w $<` + +.cpp.lo: +@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LTCXXCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< +CXXDEPMODE = @CXXDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(libdir) + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-libLTLIBRARIES + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool distclean distclean-compile \ + distclean-depend distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am \ + install-libLTLIBRARIES install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool tags uninstall \ + uninstall-am uninstall-info-am uninstall-libLTLIBRARIES + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/library/server/libfcgi/Clib/libfcgi/libfcgi/fcgi_stdio.c b/library/server/libfcgi/Clib/libfcgi/libfcgi/fcgi_stdio.c new file mode 100644 index 00000000..b081d3ee --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/libfcgi/fcgi_stdio.c @@ -0,0 +1,825 @@ +/* + * fcgi_stdio.c -- + * + * FastCGI-stdio compatibility package + * + * + * Copyright (c) 1996 Open Market, Inc. + * + * See the file "LICENSE.TERMS" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + */ + +#ifndef lint +static const char rcsid[] = "$Id: fcgi_stdio.c,v 1.14 2001/09/01 01:09:30 robs Exp $"; +#endif /* not lint */ + +#include /* for errno */ +#include /* for va_arg */ +#include /* for malloc */ +#include /* for strerror */ +#include +#include + +#include "fcgi_config.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef _WIN32 +#define DLLAPI __declspec(dllexport) +#endif + +#include "fcgiapp.h" +#include "fcgios.h" +#include "fcgimisc.h" + +#define NO_FCGI_DEFINES +#include "fcgi_stdio.h" +#undef NO_FCGI_DEFINES + +#ifndef _WIN32 + +extern char **environ; + +#ifdef HAVE_FILENO_PROTO +#include +#else +extern int fileno(FILE *stream); +#endif + +extern FILE *fdopen(int fildes, const char *type); +extern FILE *popen(const char *command, const char *type); +extern int pclose(FILE *stream); + +#else /* _WIN32 */ + +#define popen _popen +#define pclose _pclose +#define fdopen _fdopen +#define fileno _fileno + +#endif /* _WIN32 */ + +FCGI_FILE _fcgi_sF[3]; + + +/* + *---------------------------------------------------------------------- + * + * FCGI_Accept -- + * + * Accepts a new request from the HTTP server and creates + * a conventional execution environment for the request. + * + * If the application was invoked as a FastCGI server, + * the first call to FCGI_Accept indicates that the application + * has completed its initialization and is ready to accept + * a request. Subsequent calls to FCGI_Accept indicate that + * the application has completed its processing of the + * current request and is ready to accept a new request. + * + * If the application was invoked as a CGI program, the first + * call to FCGI_Accept is essentially a no-op and the second + * call returns EOF (-1). + * + * Results: + * 0 for successful call, -1 for error (application should exit). + * + * Side effects: + * If the application was invoked as a FastCGI server, + * and this is not the first call to this procedure, + * FCGI_Accept first performs the equivalent of FCGI_Finish. + * + * On every call, FCGI_Accept accepts the new request and + * reads the FCGI_PARAMS stream into an environment array, + * i.e. a NULL-terminated array of strings of the form + * ``name=value''. It assigns a pointer to this array + * to the global variable environ, used by the standard + * library function getenv. It creates new FCGI_FILE *s + * representing input from the HTTP server, output to the HTTP + * server, and error output to the HTTP server, and assigns these + * new files to stdin, stdout, and stderr respectively. + * + * DO NOT mutate or retain pointers to environ or any values + * contained in it (e.g. to the result of calling getenv(3)), + * since these are freed by the next call to FCGI_Finish or + * FCGI_Accept. In particular do not use setenv(3) or putenv(3) + * in conjunction with FCGI_Accept. + * + *---------------------------------------------------------------------- + */ +static int acceptCalled = FALSE; +static int isCGI = FALSE; + +int FCGI_Accept(void) +{ + if(!acceptCalled) { + /* + * First call to FCGI_Accept. Is application running + * as FastCGI or as CGI? + */ + isCGI = FCGX_IsCGI(); + acceptCalled = TRUE; + atexit(&FCGI_Finish); + } else if(isCGI) { + /* + * Not first call to FCGI_Accept and running as CGI means + * application is done. + */ + return(EOF); + } + if(isCGI) { + FCGI_stdin->stdio_stream = stdin; + FCGI_stdin->fcgx_stream = NULL; + FCGI_stdout->stdio_stream = stdout; + FCGI_stdout->fcgx_stream = NULL; + FCGI_stderr->stdio_stream = stderr; + FCGI_stderr->fcgx_stream = NULL; + } else { + FCGX_Stream *in, *out, *error; + FCGX_ParamArray envp; + int acceptResult = FCGX_Accept(&in, &out, &error, &envp); + if(acceptResult < 0) { + return acceptResult; + } + FCGI_stdin->stdio_stream = NULL; + FCGI_stdin->fcgx_stream = in; + FCGI_stdout->stdio_stream = NULL; + FCGI_stdout->fcgx_stream = out; + FCGI_stderr->stdio_stream = NULL; + FCGI_stderr->fcgx_stream = error; + environ = envp; + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_Environ -- + * + * Return the (char**) environ variable + * since there are issue related to UNICODE ... + * + *---------------------------------------------------------------------- + */ + +char** FCGI_Environ(void) +{ + return (char**) environ; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_Finish -- + * + * Finishes the current request from the HTTP server. + * + * Side effects: + * + * Flushes any buffered output to the HTTP server. Then frees + * all storage allocated by the previous call, including all + * storage reachable from the value of environ set by the previous + * call to FCGI_Accept. + * + * DO NOT use stdin, stdout, stderr, or environ between calling + * FCGI_Finish and calling FCGI_Accept. + * + * DO NOT mutate or retain pointers to environ or any values + * contained in it (e.g. to the result of calling getenv(3)), + * since these are freed by the next call to FCGI_Finish or + * FCGI_Accept. In particular do not use setenv(3) or putenv(3) + * in conjunction with FCGI_Accept. + * + *---------------------------------------------------------------------- + */ +void FCGI_Finish(void) +{ + if(!acceptCalled || isCGI) { + return; + } + FCGX_Finish(); + FCGI_stdin->fcgx_stream = NULL; + FCGI_stdout->fcgx_stream = NULL; + FCGI_stderr->fcgx_stream = NULL; + environ = NULL; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_StartFilterData -- + * + * + * The current request is for the filter role, and stdin is + * positioned at EOF of FCGI_STDIN. The call repositions + * stdin to the start of FCGI_DATA. + * If the preconditions are not met (e.g. FCGI_STDIN has not + * been read to EOF), the call sets the stream error code to + * FCGX_CALL_SEQ_ERROR. + * + * Results: + * 0 for a normal return, < 0 for error + * + *---------------------------------------------------------------------- + */ +int FCGI_StartFilterData(void) +{ + if(FCGI_stdin->stdio_stream) { + return -1; + } else { + return FCGX_StartFilterData(FCGI_stdin->fcgx_stream); + } +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_SetExitStatus -- + * + * Sets the exit status for the current request. The exit status + * is the status code the request would have exited with, had + * the request been run as a CGI program. You can call + * FCGI_SetExitStatus several times during a request; the last call + * before the request ends (by calling FCGI_Accept) determines the + * value. + * + *---------------------------------------------------------------------- + */ +void FCGI_SetExitStatus(int status) +{ + if(FCGI_stdin->fcgx_stream) { + FCGX_SetExitStatus(status, FCGI_stdin->fcgx_stream); + } +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_perror -- + * + * Wrapper for function defined in H&S Section 11.2 + * + *---------------------------------------------------------------------- + */ +void FCGI_perror(const char *str) +{ + FCGI_fputs(str, FCGI_stderr); + FCGI_fputs(": ", FCGI_stderr); + FCGI_fputs(strerror(OS_Errno), FCGI_stderr); + return; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_OpenFromFILE -- + * + * Constructs a new FCGI_FILE * from the FILE *stream. + * + * Results: + * NULL if stream == NULL or storage could not be allocated, + * otherwise the new FCGI_FILE *. + * + *---------------------------------------------------------------------- + */ +static FCGI_FILE *FCGI_OpenFromFILE(FILE *stream) +{ + FCGI_FILE *fp; + + if (stream == NULL) + return NULL; + + fp = (FCGI_FILE *) malloc(sizeof(FCGI_FILE)); + if (fp != NULL) + { + fp->stdio_stream = stream; + fp->fcgx_stream = NULL; + } + + return fp; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_fopen, FCGI_fclose, FCGI_fflush, FCGI_freopen -- + * + * Wrappers for functions defined in H&S Section 15.2 + * + *---------------------------------------------------------------------- + */ +FCGI_FILE *FCGI_fopen(const char *path, const char *mode) +{ + FILE * file = fopen(path, mode); + FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); + + if (file && !fcgi_file) + fclose(file); + + return fcgi_file; +} + +int FCGI_fclose(FCGI_FILE *fp) +{ + int n = EOF; + if(fp->stdio_stream) { + n = fclose(fp->stdio_stream); + fp->stdio_stream = NULL; + } else if(fp->fcgx_stream) { + n = FCGX_FClose(fp->fcgx_stream); + fp->fcgx_stream = NULL; + } + if((fp != FCGI_stdin) && (fp != FCGI_stdout) && (fp != FCGI_stderr)) { + free(fp); + } + return n; +} + +int FCGI_fflush(FCGI_FILE *fp) +{ + if(fp == NULL) + return fflush(NULL); + if(fp->stdio_stream) + return fflush(fp->stdio_stream); + else if(fp->fcgx_stream) + return FCGX_FFlush(fp->fcgx_stream); + return EOF; +} + +FCGI_FILE *FCGI_freopen(const char *path, const char *mode, + FCGI_FILE *fp) +{ + if(fp->stdio_stream) { + if(freopen(path, mode, fp->stdio_stream) == NULL) + return NULL; + else + return fp; + } else if(fp->fcgx_stream) { + (void) FCGX_FClose(fp->fcgx_stream); + fp->stdio_stream = fopen(path, mode); + if(fp->stdio_stream == NULL) + return NULL; + else { + fp->fcgx_stream = NULL; + return fp; + } + } + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_setvbuf, FCGI_setbuf -- + * + * Wrappers for functions defined in H&S Section 15.3 + * + *---------------------------------------------------------------------- + */ +int FCGI_setvbuf(FCGI_FILE *fp, char *buf, int bufmode, size_t size) +{ + if(fp->stdio_stream) + return setvbuf(fp->stdio_stream, buf, bufmode, size); + else { + return -1; + } +} + +void FCGI_setbuf(FCGI_FILE *fp, char *buf) +{ + if(fp->stdio_stream) + setbuf(fp->stdio_stream, buf); +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_fseek, FCGI_ftell, FCGI_rewind, FCGI_fgetpos, FCGI_fsetpos -- + * + * Wrappers for functions defined in H&S Section 15.5 + * + *---------------------------------------------------------------------- + */ +int FCGI_fseek(FCGI_FILE *fp, long offset, int whence) +{ + if(fp->stdio_stream) + return fseek(fp->stdio_stream, offset, whence); + else { + OS_SetErrno(ESPIPE); + return -1; + } +} + +int FCGI_ftell(FCGI_FILE *fp) +{ + if(fp->stdio_stream) + return ftell(fp->stdio_stream); + else { + OS_SetErrno(ESPIPE); + return -1; + } +} + +void FCGI_rewind(FCGI_FILE *fp) +{ + if(fp->stdio_stream) + rewind(fp->stdio_stream); + else + OS_SetErrno(ESPIPE); +} + +#ifdef HAVE_FPOS +int FCGI_fgetpos(FCGI_FILE *fp, fpos_t *pos) +{ + if(fp->stdio_stream) + return fgetpos(fp->stdio_stream, pos); + else { + OS_SetErrno(ESPIPE); + return -1; + } +} + +int FCGI_fsetpos(FCGI_FILE *fp, const fpos_t *pos) +{ + if(fp->stdio_stream) + return fsetpos(fp->stdio_stream, pos); + else { + OS_SetErrno(ESPIPE); + return -1; + } +} +#endif + +/* + *---------------------------------------------------------------------- + * + * FCGI_fgetc, FCGI_getchar, FCGI_ungetc -- + * + * Wrappers for functions defined in H&S Section 15.6 + * + * XXX: getc and getchar are generally defined as macros + * for performance reasons + * + *---------------------------------------------------------------------- + */ +int FCGI_fgetc(FCGI_FILE *fp) +{ + if(fp->stdio_stream) + return fgetc(fp->stdio_stream); + else if(fp->fcgx_stream) + return FCGX_GetChar(fp->fcgx_stream); + return EOF; +} + +int FCGI_getchar(void) +{ + return FCGI_fgetc(FCGI_stdin); +} + +int FCGI_ungetc(int c, FCGI_FILE *fp) +{ + if(fp->stdio_stream) + return ungetc(c, fp->stdio_stream); + else if(fp->fcgx_stream) + return FCGX_UnGetChar(c, fp->fcgx_stream); + return EOF; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_fgets, FCGI_gets -- + * + * Wrappers for functions defined in H&S Section 15.7 + * + *---------------------------------------------------------------------- + */ +char *FCGI_fgets(char *str, int size, FCGI_FILE *fp) +{ + if(fp->stdio_stream) + return fgets(str, size, fp->stdio_stream); + else if(fp->fcgx_stream) + return FCGX_GetLine(str, size, fp->fcgx_stream); + return NULL; +} + +/* + * The gets() function reads characters from the standard input stream + * into the array pointed to by str until a newline character is read + * or an end-of-file condition is encountered. The newline character + * is discarded and the string is terminated with a null character. + */ +char *FCGI_gets(char *str) +{ + char *s; + int c; + + for (s = str; ((c = FCGI_getchar()) != '\n');) { + if(c == EOF) { + if(s == str) + return NULL; + else + break; + } else + *s++ = (char) c; + } + *s = 0; + return str; +} + +/* + *---------------------------------------------------------------------- + * + * Wrappers for functions defined in H&S Section 15.8 + * + * XXX: missing: fscanf, scanf + * + *---------------------------------------------------------------------- + */ + +/* + *---------------------------------------------------------------------- + * + * FCGI_fputc, FCGI_putchar -- + * + * Wrappers for functions defined in H&S Section 15.9 + * + * XXX: putc and putchar are generally defined as macros + * for performance reasons + * + *---------------------------------------------------------------------- + */ +int FCGI_fputc(int c, FCGI_FILE *fp) +{ + if(fp->stdio_stream) + return fputc(c, fp->stdio_stream); + else if(fp->fcgx_stream) + return FCGX_PutChar(c, fp->fcgx_stream); + else return EOF; +} + +int FCGI_putchar(int c) +{ + return FCGI_fputc(c, FCGI_stdout); +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_fputs, FCGI_puts + * + * Wrappers for functions defined in H&S Section 15.10 + * + *---------------------------------------------------------------------- + */ +int FCGI_fputs(const char *str, FCGI_FILE *fp) +{ + if(fp->stdio_stream) + return fputs(str, fp->stdio_stream); + else if(fp->fcgx_stream) + return FCGX_PutS(str, fp->fcgx_stream); + return EOF; +} + +int FCGI_puts(const char *str) +{ + int n; + if(FCGI_stdout->stdio_stream) { + n = fputs(str, FCGI_stdout->stdio_stream); + if(n < 0) + return n; + else + return fputc('\n', FCGI_stdout->stdio_stream); + } else if(FCGI_stdout->fcgx_stream) { + n = FCGX_PutS(str, FCGI_stdout->fcgx_stream); + if(n < 0) + return n; + else + return FCGX_PutChar('\n', FCGI_stdout->fcgx_stream); + } + return EOF; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_fprintf, FCGI_printf -- + * + * Wrappers for functions defined in H&S Section 15.11 + * + *---------------------------------------------------------------------- + */ +int FCGI_fprintf(FCGI_FILE *fp, const char *format, ...) +{ + va_list ap; + int n = 0; + va_start(ap, format); + if(fp->stdio_stream) + n = vfprintf(fp->stdio_stream, format, ap); + else if(fp->fcgx_stream) + n = FCGX_VFPrintF(fp->fcgx_stream, format, ap); + va_end(ap); + return n; +} + +int FCGI_printf(const char *format, ...) +{ + va_list ap; + int n; + va_start(ap, format); + n = FCGI_vfprintf(FCGI_stdout, format, ap); + va_end(ap); + return n; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_vfprintf, FCGI_vprintf -- + * + * Wrappers for functions defined in H&S Section 15.12 + * + *---------------------------------------------------------------------- + */ +int FCGI_vfprintf(FCGI_FILE *fp, const char *format, va_list ap) +{ + if(fp->stdio_stream) + return vfprintf(fp->stdio_stream, format, ap); + else if(fp->fcgx_stream) + return FCGX_VFPrintF(fp->fcgx_stream, format, ap); + return EOF; +} + +int FCGI_vprintf(const char *format, va_list ap) +{ + if(FCGI_stdout->stdio_stream) + return vfprintf(FCGI_stdout->stdio_stream, format, ap); + else if(FCGI_stdout->fcgx_stream) + return FCGX_VFPrintF(FCGI_stdout->fcgx_stream, format, ap); + return EOF; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_fread, FCGI_fwrite -- + * + * Wrappers for functions defined in H&S Section 15.13 + * + *---------------------------------------------------------------------- + */ +size_t FCGI_fread(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp) +{ + int n; + if(fp->stdio_stream) + return fread(ptr, size, nmemb, fp->stdio_stream); + else if(fp->fcgx_stream) { + if((size * nmemb) == 0) { + return 0; + } + ASSERT(size * nmemb < (size_t)INT_MAX); + n = FCGX_GetStr((char *) ptr, (int)(size * nmemb), fp->fcgx_stream); + ASSERT(n >= 0); + return ((size_t)n/size); + } + return (size_t)EOF; +} + +size_t FCGI_fwrite(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp) +{ + int n; + if(fp->stdio_stream) + return fwrite(ptr, size, nmemb, fp->stdio_stream); + else if(fp->fcgx_stream) { + if((size * nmemb) == 0) { + return 0; + } + ASSERT(size * nmemb < (size_t)INT_MAX); + n = (size_t)FCGX_PutStr((char *) ptr, (int)(size * nmemb), fp->fcgx_stream); + ASSERT(n >= 0); + return ((size_t)n/size); + } + return (size_t)EOF; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_feof, FCGI_ferror, FCGI_clearerr -- + * + * Wrappers for functions defined in H&S Section 15.14 + * + *---------------------------------------------------------------------- + */ +int FCGI_feof(FCGI_FILE *fp) +{ + if(fp->stdio_stream) { + return feof(fp->stdio_stream); + } else if (fp->fcgx_stream){ + return FCGX_HasSeenEOF(fp->fcgx_stream); + } + return -1; + +} + +int FCGI_ferror(FCGI_FILE *fp) +{ + if(fp->stdio_stream) { + return ferror(fp->stdio_stream); + } else if(fp->fcgx_stream) { + return FCGX_GetError(fp->fcgx_stream); + } + return -1; +} + +void FCGI_clearerr(FCGI_FILE *fp) +{ + if(fp->stdio_stream) { + clearerr(fp->stdio_stream); + } else if(fp->fcgx_stream) { + FCGX_ClearError(fp->fcgx_stream); + } + return; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_tmpfile -- + * + * Wrappers for function defined in H&S Section 15.16 + * + *---------------------------------------------------------------------- + */ +FCGI_FILE *FCGI_tmpfile(void) +{ + FILE * file = tmpfile(); + FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); + + if (file && !fcgi_file) + fclose(file); + + return fcgi_file; +} + +/* + *---------------------------------------------------------------------- + * + * FCGI_fileno, FCGI_fdopen, FCGI_popen, FCGI_pclose -- + * + * Wrappers for POSIX, X/OPEN functions not in ISO C + * + *---------------------------------------------------------------------- + */ +int FCGI_fileno(FCGI_FILE *fp) +{ + if(fp->stdio_stream) + return fileno(fp->stdio_stream); + else + return -1; +} + +FCGI_FILE *FCGI_fdopen(int fd, const char *mode) +{ + FILE * file = fdopen(fd, mode); + FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); + + if (file && !fcgi_file) + fclose(file); + + return fcgi_file; +} + +FCGI_FILE *FCGI_popen(const char *cmd, const char *type) +{ + FILE * file = popen(cmd, type); + FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); + + if (file && !fcgi_file) + pclose(file); + + return fcgi_file; +} + +int FCGI_pclose(FCGI_FILE *fp) +{ + int n = EOF; + if (fp->stdio_stream) { + n = pclose(fp->stdio_stream); + fp->stdio_stream = NULL; + } else if(fp->fcgx_stream) { + /* + * The caller is deeply confused; don't free the storage. + */ + return EOF; + } + if((fp != FCGI_stdin) && (fp != FCGI_stdout) && (fp != FCGI_stderr)) { + free(fp); + } + return n; +} diff --git a/library/server/libfcgi/Clib/libfcgi/libfcgi/fcgiapp.c b/library/server/libfcgi/Clib/libfcgi/libfcgi/fcgiapp.c new file mode 100644 index 00000000..11ed8a64 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/libfcgi/fcgiapp.c @@ -0,0 +1,2349 @@ +/* + * fcgiapp.c -- + * + * FastCGI application library: request-at-a-time + * + * + * Copyright (c) 1996 Open Market, Inc. + * + * See the file "LICENSE.TERMS" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + */ +#ifndef lint +static const char rcsid[] = "$Id: fcgiapp.c,v 1.34 2001/12/12 22:54:10 robs Exp $"; +#endif /* not lint */ + +#include +#include +#include /* for fcntl */ +#include +#include /* for memchr() */ +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#endif + +#include "fcgi_config.h" + +#ifdef HAVE_SYS_SOCKET_H +#include /* for getpeername */ +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifdef _WIN32 +#define DLLAPI __declspec(dllexport) +#endif + +#include "fcgimisc.h" +#include "fastcgi.h" +#include "fcgios.h" +#include "fcgiapp.h" + +/* + * This is a workaround for one version of the HP C compiler + * (c89 on HP-UX 9.04, also Stratus FTX), which will dump core + * if given 'long double' for varargs. + */ +#ifdef HAVE_VA_ARG_LONG_DOUBLE_BUG +#define LONG_DOUBLE double +#else +#define LONG_DOUBLE long double +#endif + +#ifdef _WIN64 +#define ABS(a) _abs64(a) +#else +#define ABS(a) abs(a) +#endif + +/* + * Globals + */ +static int libInitialized = 0; +static int isFastCGI = -1; +static char *webServerAddressList = NULL; +static FCGX_Request the_request; + +void FCGX_ShutdownPending(void) +{ + OS_ShutdownPending(); +} + +static void *Malloc(size_t size) +{ + void *result = malloc(size); + ASSERT(size == 0 || result != NULL); + return result; +} + +static char *StringCopy(char *str) +{ + size_t strLen = strlen(str); + char *newString = (char *)Malloc(strLen + 1); + memcpy(newString, str, strLen); + newString[strLen] = '\000'; + return newString; +} + + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetChar -- + * + * Reads a byte from the input stream and returns it. + * + * Results: + * The byte, or EOF (-1) if the end of input has been reached. + * + *---------------------------------------------------------------------- + */ +int FCGX_GetChar(FCGX_Stream *stream) +{ + if (stream->isClosed || ! stream->isReader) + return EOF; + + if (stream->rdNext != stream->stop) + return *stream->rdNext++; + + stream->fillBuffProc(stream); + if (stream->isClosed) + return EOF; + + stream->stopUnget = stream->rdNext; + if (stream->rdNext != stream->stop) + return *stream->rdNext++; + + ASSERT(stream->isClosed); /* bug in fillBufProc if not */ + return EOF; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetStr -- + * + * Reads up to n consecutive bytes from the input stream + * into the character array str. Performs no interpretation + * of the input bytes. + * + * Results: + * Number of bytes read. If result is smaller than n, + * the end of input has been reached. + * + *---------------------------------------------------------------------- + */ +int FCGX_GetStr(char *str, int n, FCGX_Stream *stream) +{ + intptr_t m, bytesMoved; + + if (stream->isClosed || ! stream->isReader || n <= 0) { + return 0; + } + /* + * Fast path: n bytes are already available + */ + if(n <= (stream->stop - stream->rdNext)) { + memcpy(str, stream->rdNext, n); + stream->rdNext += n; + return n; + } + /* + * General case: stream is closed or buffer fill procedure + * needs to be called + */ + bytesMoved = 0; + for (;;) { + if(stream->rdNext != stream->stop) { + m = min(n - bytesMoved, stream->stop - stream->rdNext); + memcpy(str, stream->rdNext, m); + bytesMoved += m; + stream->rdNext += m; + if(bytesMoved == n) { + ASSERT(INT_MIN <= bytesMoved && bytesMoved <= INT_MAX); + return (int)bytesMoved; + } + str += m; + } + if(stream->isClosed || !stream->isReader) { + ASSERT(INT_MIN <= bytesMoved && bytesMoved <= INT_MAX); + return (int)bytesMoved; + } + stream->fillBuffProc(stream); + if (stream->isClosed) { + ASSERT(INT_MIN <= bytesMoved && bytesMoved <= INT_MAX); + return (int)bytesMoved; + } + + stream->stopUnget = stream->rdNext; + } +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetLine -- + * + * Reads up to n-1 consecutive bytes from the input stream + * into the character array str. Stops before n-1 bytes + * have been read if '\n' or EOF is read. The terminating '\n' + * is copied to str. After copying the last byte into str, + * stores a '\0' terminator. + * + * Results: + * NULL if EOF is the first thing read from the input stream, + * str otherwise. + * + *---------------------------------------------------------------------- + */ +char *FCGX_GetLine(char *str, int n, FCGX_Stream *stream) +{ + int c; + char *p = str; + + n--; + while (n > 0) { + c = FCGX_GetChar(stream); + if(c == EOF) { + if(p == str) + return NULL; + else + break; + } + *p++ = (char) c; + n--; + if(c == '\n') + break; + } + *p = '\0'; + return str; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_UnGetChar -- + * + * Pushes back the character c onto the input stream. One + * character of pushback is guaranteed once a character + * has been read. No pushback is possible for EOF. + * + * Results: + * Returns c if the pushback succeeded, EOF if not. + * + *---------------------------------------------------------------------- + */ +int FCGX_UnGetChar(int c, FCGX_Stream *stream) { + if(c == EOF + || stream->isClosed + || !stream->isReader + || stream->rdNext == stream->stopUnget) + return EOF; + --(stream->rdNext); + *stream->rdNext = (unsigned char) c; + return c; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_HasSeenEOF -- + * + * Returns EOF if end-of-file has been detected while reading + * from stream; otherwise returns 0. + * + * Note that FCGX_HasSeenEOF(s) may return 0, yet an immediately + * following FCGX_GetChar(s) may return EOF. This function, like + * the standard C stdio function feof, does not provide the + * ability to peek ahead. + * + * Results: + * EOF if end-of-file has been detected, 0 if not. + * + *---------------------------------------------------------------------- + */ +int FCGX_HasSeenEOF(FCGX_Stream *stream) { + return (stream->isClosed) ? EOF : 0; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_PutChar -- + * + * Writes a byte to the output stream. + * + * Results: + * The byte, or EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +int FCGX_PutChar(int c, FCGX_Stream *stream) +{ + if(stream->wrNext != stream->stop) + return (*stream->wrNext++ = (unsigned char) c); + if(stream->isClosed || stream->isReader) + return EOF; + stream->emptyBuffProc(stream, FALSE); + if(stream->wrNext != stream->stop) + return (*stream->wrNext++ = (unsigned char) c); + ASSERT(stream->isClosed); /* bug in emptyBuffProc if not */ + return EOF; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_PutStr -- + * + * Writes n consecutive bytes from the character array str + * into the output stream. Performs no interpretation + * of the output bytes. + * + * Results: + * Number of bytes written (n) for normal return, + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream) +{ + intptr_t m, bytesMoved; + + /* + * Fast path: room for n bytes in the buffer + */ + if(n <= (stream->stop - stream->wrNext)) { + memcpy(stream->wrNext, str, n); + stream->wrNext += n; + return n; + } + /* + * General case: stream is closed or buffer empty procedure + * needs to be called + */ + bytesMoved = 0; + for (;;) { + if(stream->wrNext != stream->stop) { + m = min(n - bytesMoved, stream->stop - stream->wrNext); + memcpy(stream->wrNext, str, m); + bytesMoved += m; + stream->wrNext += m; + if(bytesMoved == n) { + ASSERT(INT_MIN <= bytesMoved && bytesMoved <= INT_MAX); + return (int)bytesMoved; + } + str += m; + } + if(stream->isClosed || stream->isReader) + return -1; + stream->emptyBuffProc(stream, FALSE); + } +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_PutS -- + * + * Writes a character string to the output stream. + * + * Results: + * number of bytes written for normal return, + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +int FCGX_PutS(const char *str, FCGX_Stream *stream) +{ + size_t sz = strlen(str); + ASSERT(sz <= INT_MAX); + return FCGX_PutStr(str, (int)sz, stream); +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_FPrintF -- + * + * Performs output formatting and writes the results + * to the output stream. + * + * Results: + * number of bytes written for normal return, + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +int FCGX_FPrintF(FCGX_Stream *stream, const char *format, ...) +{ + int result; + va_list ap; + va_start(ap, format); + result = FCGX_VFPrintF(stream, format, ap); + va_end(ap); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_VFPrintF -- + * + * Performs output formatting and writes the results + * to the output stream. + * + * Results: + * number of bytes written for normal return, + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ + +#define PRINTF_BUFFLEN 100 + /* + * More than sufficient space for all unmodified conversions + * except %s and %f. + */ +#define FMT_BUFFLEN 25 + /* + * Max size of a format specifier is 1 + 5 + 7 + 7 + 2 + 1 + slop + */ +static void CopyAndAdvance(char **destPtr, char **srcPtr, intptr_t n); + +int FCGX_VFPrintF(FCGX_Stream *stream, const char *format, va_list arg) +{ + char *f, *fStop, *percentPtr, *p, *fmtBuffPtr, *buffPtr; + intptr_t op, performedOp, sizeModifier, buffCount = 0, buffLen; + intptr_t fastPath, n, auxBuffLen = 0, minWidth, precision; + int exp; + intptr_t specifierLength, buffReqd; + char *auxBuffPtr = NULL; + intptr_t streamCount = 0; + char fmtBuff[FMT_BUFFLEN]; + char buff[PRINTF_BUFFLEN]; + + int intArg; + short shortArg; + long longArg; + unsigned unsignedArg; + unsigned long uLongArg; + unsigned short uShortArg; + char *charPtrArg = NULL; + void *voidPtrArg; + int *intPtrArg; + long *longPtrArg; + short *shortPtrArg; + double doubleArg = 0.0; + LONG_DOUBLE lDoubleArg = 0.0L; + + fmtBuff[0] = '%'; + f = (char *) format; + fStop = f + strlen(f); + while (f != fStop) { + percentPtr = (char *)memchr(f, '%', fStop - f); + if(percentPtr == NULL) percentPtr = fStop; + if(percentPtr != f) { + intptr_t ptrDiff = percentPtr - f; + ASSERT(0 <= ptrDiff && ptrDiff < INT_MAX); + if(FCGX_PutStr(f, (int)ptrDiff, stream) < 0) + goto ErrorReturn; + streamCount += (int)ptrDiff; + f = percentPtr; + if(f == fStop) break; + } + fastPath = TRUE; + /* + * The following loop always executes either once or twice. + */ + for (;;) { + if(fastPath) { + /* + * Fast path: Scan optimistically, hoping that no flags, + * minimum field width, or precision are specified. + * Use the preallocated buffer, which is large enough + * for all fast path cases. If the conversion specifier + * is really more complex, run the loop a second time + * using the slow path. + * Note that fast path execution of %s bypasses the buffer + * and %f is not attempted on the fast path due to + * its large buffering requirements. + */ + op = *(percentPtr + 1); + switch(op) { + case 'l': + case 'L': + case 'h': + sizeModifier = op; + op = *(percentPtr + 2); + fmtBuff[1] = (char) sizeModifier; + fmtBuff[2] = (char) op; + fmtBuff[3] = '\0'; + specifierLength = 3; + break; + default: + sizeModifier = ' '; + fmtBuff[1] = (char) op; + fmtBuff[2] = '\0'; + specifierLength = 2; + break; + } + buffPtr = buff; + buffLen = PRINTF_BUFFLEN; + } else { + /* + * Slow path: Scan the conversion specifier and construct + * a new format string, compute an upper bound on the + * amount of buffering that sprintf will require, + * and allocate a larger buffer if necessary. + */ + p = percentPtr + 1; + fmtBuffPtr = &fmtBuff[1]; + /* + * Scan flags + */ + n = strspn(p, "-0+ #"); + if(n > 5) + goto ErrorReturn; + CopyAndAdvance(&fmtBuffPtr, &p, n); + /* + * Scan minimum field width + */ + n = strspn(p, "0123456789"); + if(n == 0) { + if(*p == '*') { + minWidth = va_arg(arg, int); + if(ABS(minWidth) > 999999) + goto ErrorReturn; + /* + * The following use of strlen rather than the + * value returned from sprintf is because SUNOS4 + * returns a char * instead of an int count. + */ + sprintf(fmtBuffPtr, "%lld", (long long)minWidth); + fmtBuffPtr += strlen(fmtBuffPtr); + p++; + } else { + minWidth = 0; + } + } else if(n <= 6) { + minWidth = strtol(p, NULL, 10); + CopyAndAdvance(&fmtBuffPtr, &p, n); + } else { + goto ErrorReturn; + } + /* + * Scan precision + */ + if(*p == '.') { + CopyAndAdvance(&fmtBuffPtr, &p, 1); + n = strspn(p, "0123456789"); + if(n == 0) { + if(*p == '*') { + precision = va_arg(arg, int); + if(precision < 0) precision = 0; + if(precision > 999999) + goto ErrorReturn; + /* + * The following use of strlen rather than the + * value returned from sprintf is because SUNOS4 + * returns a char * instead of an int count. + */ + sprintf(fmtBuffPtr, "%lld", (long long)precision); + fmtBuffPtr += strlen(fmtBuffPtr); + p++; + } else { + precision = 0; + } + } else if(n <= 6) { + precision = strtol(p, NULL, 10); + CopyAndAdvance(&fmtBuffPtr, &p, n); + } else { + goto ErrorReturn; + } + } else { + precision = -1; + } + /* + * Scan size modifier and conversion operation + */ + switch(*p) { + case 'l': + case 'L': + case 'h': + sizeModifier = *p; + CopyAndAdvance(&fmtBuffPtr, &p, 1); + break; + default: + sizeModifier = ' '; + break; + } + op = *p; + CopyAndAdvance(&fmtBuffPtr, &p, 1); + ASSERT(fmtBuffPtr - fmtBuff < FMT_BUFFLEN); + *fmtBuffPtr = '\0'; + specifierLength = p - percentPtr; + /* + * Bound the required buffer size. For s and f + * conversions this requires examining the argument. + */ + switch(op) { + case 'd': + case 'i': + case 'u': + case 'o': + case 'x': + case 'X': + case 'c': + case 'p': + buffReqd = max(precision, 46); + break; + case 's': + charPtrArg = va_arg(arg, char *); + if (!charPtrArg) charPtrArg = "(null)"; + if(precision == -1) { + buffReqd = strlen(charPtrArg); + } else { + p = (char *)memchr(charPtrArg, '\0', precision); + buffReqd = + (p == NULL) ? precision : p - charPtrArg; + } + break; + case 'f': + switch(sizeModifier) { + case ' ': + doubleArg = va_arg(arg, double); + frexp(doubleArg, &exp); + break; + case 'L': + lDoubleArg = va_arg(arg, LONG_DOUBLE); + /* XXX Need to check for the presence of + * frexpl() and use it if available */ + frexp((double) lDoubleArg, &exp); + break; + default: + goto ErrorReturn; + } + if(precision == -1) precision = 6; + buffReqd = precision + 3 + ((exp > 0) ? exp/3 : 0); + break; + case 'e': + case 'E': + case 'g': + case 'G': + if(precision == -1) precision = 6; + buffReqd = precision + 8; + break; + case 'n': + case '%': + default: + goto ErrorReturn; + break; + } + buffReqd = max(buffReqd + 10, minWidth); + /* + * Allocate the buffer + */ + if(buffReqd <= PRINTF_BUFFLEN) { + buffPtr = buff; + buffLen = PRINTF_BUFFLEN; + } else { + if(auxBuffPtr == NULL || buffReqd > auxBuffLen) { + if(auxBuffPtr != NULL) free(auxBuffPtr); + auxBuffPtr = (char *)Malloc(buffReqd); + auxBuffLen = buffReqd; + if(auxBuffPtr == NULL) + goto ErrorReturn; + } + buffPtr = auxBuffPtr; + buffLen = auxBuffLen; + } + } + /* + * This giant switch statement requires the following variables + * to be set up: op, sizeModifier, arg, buffPtr, fmtBuff. + * When fastPath == FALSE and op == 's' or 'f', the argument + * has been read into charPtrArg, doubleArg, or lDoubleArg. + * The statement produces the boolean performedOp, TRUE iff + * the op/sizeModifier were executed and argument consumed; + * if performedOp, the characters written into buffPtr[] + * and the character count buffCount (== EOF meaning error). + * + * The switch cases are arranged in the same order as in the + * description of fprintf in section 15.11 of Harbison and Steele. + */ + performedOp = TRUE; + switch(op) { + case 'd': + case 'i': + switch(sizeModifier) { + case ' ': + intArg = va_arg(arg, int); + sprintf(buffPtr, fmtBuff, intArg); + buffCount = strlen(buffPtr); + break; + case 'l': + longArg = va_arg(arg, long); + sprintf(buffPtr, fmtBuff, longArg); + buffCount = strlen(buffPtr); + break; + case 'h': + shortArg = (short) va_arg(arg, int); + sprintf(buffPtr, fmtBuff, shortArg); + buffCount = strlen(buffPtr); + break; + default: + goto ErrorReturn; + } + break; + case 'u': + case 'o': + case 'x': + case 'X': + switch(sizeModifier) { + case ' ': + unsignedArg = va_arg(arg, unsigned); + sprintf(buffPtr, fmtBuff, unsignedArg); + buffCount = strlen(buffPtr); + break; + case 'l': + uLongArg = va_arg(arg, unsigned long); + sprintf(buffPtr, fmtBuff, uLongArg); + buffCount = strlen(buffPtr); + break; + case 'h': + uShortArg = (unsigned short) va_arg(arg, int); + sprintf(buffPtr, fmtBuff, uShortArg); + buffCount = strlen(buffPtr); + break; + default: + goto ErrorReturn; + } + break; + case 'c': + switch(sizeModifier) { + case ' ': + intArg = va_arg(arg, int); + sprintf(buffPtr, fmtBuff, intArg); + buffCount = strlen(buffPtr); + break; + case 'l': + /* + * XXX: Allowed by ISO C Amendment 1, but + * many platforms don't yet support wint_t + */ + goto ErrorReturn; + default: + goto ErrorReturn; + } + break; + case 's': + switch(sizeModifier) { + case ' ': + if(fastPath) { + buffPtr = va_arg(arg, char *); + buffCount = strlen(buffPtr); + buffLen = buffCount + 1; + } else { + sprintf(buffPtr, fmtBuff, charPtrArg); + buffCount = strlen(buffPtr); + } + break; + case 'l': + /* + * XXX: Don't know how to convert a sequence + * of wide characters into a byte stream, or + * even how to predict the buffering required. + */ + goto ErrorReturn; + default: + goto ErrorReturn; + } + break; + case 'p': + if(sizeModifier != ' ') + goto ErrorReturn; + voidPtrArg = va_arg(arg, void *); + sprintf(buffPtr, fmtBuff, voidPtrArg); + buffCount = strlen(buffPtr); + break; + case 'n': + switch(sizeModifier) { + case ' ': + intPtrArg = va_arg(arg, int *); + ASSERT(INT_MIN <= streamCount && streamCount <= INT_MAX); + *intPtrArg = (int)streamCount; + break; + case 'l': + longPtrArg = va_arg(arg, long *); + ASSERT(LONG_MIN <= streamCount && streamCount <= LONG_MAX); + *longPtrArg = (long)streamCount; + break; + case 'h': + shortPtrArg = (short *) va_arg(arg, short *); + *shortPtrArg = (short) streamCount; + break; + default: + goto ErrorReturn; + } + buffCount = 0; + break; + case 'f': + if(fastPath) { + performedOp = FALSE; + break; + } + switch(sizeModifier) { + case ' ': + sprintf(buffPtr, fmtBuff, doubleArg); + buffCount = strlen(buffPtr); + break; + case 'L': + sprintf(buffPtr, fmtBuff, lDoubleArg); + buffCount = strlen(buffPtr); + break; + default: + goto ErrorReturn; + } + break; + case 'e': + case 'E': + case 'g': + case 'G': + switch(sizeModifier) { + case ' ': + doubleArg = va_arg(arg, double); + sprintf(buffPtr, fmtBuff, doubleArg); + buffCount = strlen(buffPtr); + break; + case 'L': + lDoubleArg = va_arg(arg, LONG_DOUBLE); + sprintf(buffPtr, fmtBuff, lDoubleArg); + buffCount = strlen(buffPtr); + break; + default: + goto ErrorReturn; + } + break; + case '%': + if(sizeModifier != ' ') + goto ErrorReturn; + buff[0] = '%'; + buffCount = 1; + break; + case '\0': + goto ErrorReturn; + default: + performedOp = FALSE; + break; + } /* switch(op) */ + if(performedOp) break; + if(!fastPath) + goto ErrorReturn; + fastPath = FALSE; + } /* for (;;) */ + ASSERT(buffCount < buffLen); + if(buffCount > 0) { + ASSERT(0 <= buffCount && buffCount <= INT_MAX); + if(FCGX_PutStr(buffPtr, (int)buffCount, stream) < 0) + goto ErrorReturn; + streamCount += buffCount; + } else if(buffCount < 0) { + goto ErrorReturn; + } + f += specifierLength; + } /* while(f != fStop) */ + goto NormalReturn; + ErrorReturn: + streamCount = -1; + NormalReturn: + if(auxBuffPtr != NULL) free(auxBuffPtr); + ASSERT(INT_MIN <= streamCount && streamCount <= INT_MAX); + return (int)streamCount; +} + +/* + * Copy n characters from *srcPtr to *destPtr, then increment + * both *srcPtr and *destPtr by n. + */ +static void CopyAndAdvance(char **destPtr, char **srcPtr, intptr_t n) +{ + char *dest = *destPtr; + char *src = *srcPtr; + intptr_t i; + for (i = 0; i < n; i++) + *dest++ = *src++; + *destPtr = dest; + *srcPtr = src; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_FFlush -- + * + * Flushes any buffered output. + * + * Server-push is a legitimate application of FCGX_FFlush. + * Otherwise, FCGX_FFlush is not very useful, since FCGX_Accept + * does it implicitly. FCGX_FFlush may reduce performance + * by increasing the total number of operating system calls + * the application makes. + * + * Results: + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +int FCGX_FFlush(FCGX_Stream *stream) +{ + if(stream->isClosed || stream->isReader) + return 0; + stream->emptyBuffProc(stream, FALSE); + return (stream->isClosed) ? -1 : 0; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_FClose -- + * + * Performs FCGX_FFlush and closes the stream. + * + * This is not a very useful operation, since FCGX_Accept + * does it implicitly. Closing the out stream before the + * err stream results in an extra write if there's nothing + * in the err stream, and therefore reduces performance. + * + * Results: + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +int FCGX_FClose(FCGX_Stream *stream) +{ + if (stream == NULL) return 0; + + if(!stream->wasFCloseCalled) { + if(!stream->isReader) { + stream->emptyBuffProc(stream, TRUE); + } + stream->wasFCloseCalled = TRUE; + stream->isClosed = TRUE; + if(stream->isReader) { + stream->wrNext = stream->stop = stream->rdNext; + } else { + stream->rdNext = stream->stop = stream->wrNext; + } + } + return (stream->FCGI_errno == 0) ? 0 : EOF; +} + +/* + *---------------------------------------------------------------------- + * + * SetError -- + * + * An error has occurred; save the error code in the stream + * for diagnostic purposes and set the stream state so that + * reads return EOF and writes have no effect. + * + *---------------------------------------------------------------------- + */ +static void SetError(FCGX_Stream *stream, int FCGI_errno) +{ + /* + * Preserve only the first error. + */ + if(stream->FCGI_errno == 0) { + stream->FCGI_errno = FCGI_errno; + } + + stream->isClosed = TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetError -- + * + * Return the stream error code. 0 means no error, > 0 + * is an errno(2) error, < 0 is an FCGX_errno error. + * + *---------------------------------------------------------------------- + */ +int FCGX_GetError(FCGX_Stream *stream) { + return stream->FCGI_errno; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_ClearError -- + * + * Clear the stream error code and end-of-file indication. + * + *---------------------------------------------------------------------- + */ +void FCGX_ClearError(FCGX_Stream *stream) { + stream->FCGI_errno = 0; + /* + * stream->isClosed = FALSE; + * XXX: should clear isClosed but work is needed to make it safe + * to do so. For example, if an application calls FClose, gets + * an I/O error on the write, calls ClearError and retries + * the FClose, FClose (really EmptyBuffProc) will write a second + * EOF record. If an application calls PutChar instead of FClose + * after the ClearError, the application will write more data. + * The stream's state must discriminate between various states + * of the stream that are now all lumped under isClosed. + */ +} + +/* + *====================================================================== + * Parameters + *====================================================================== + */ + +/* + * A vector of pointers representing the parameters received + * by a FastCGI application server, with the vector's length + * and last valid element so adding new parameters is efficient. + */ + +typedef struct Params { + FCGX_ParamArray vec; /* vector of strings */ + int length; /* number of string vec can hold */ + char **cur; /* current item in vec; *cur == NULL */ +} Params; +typedef Params *ParamsPtr; + +/* + *---------------------------------------------------------------------- + * + * NewParams -- + * + * Creates a new Params structure. + * + * Results: + * Pointer to the new structure. + * + *---------------------------------------------------------------------- + */ +static ParamsPtr NewParams(int length) +{ + ParamsPtr result; + result = (Params *)Malloc(sizeof(Params)); + result->vec = (char **)Malloc(length * sizeof(char *)); + result->length = length; + result->cur = result->vec; + *result->cur = NULL; + return result; +} + +/* + *---------------------------------------------------------------------- + * + * FreeParams -- + * + * Frees a Params structure and all the parameters it contains. + * + * Side effects: + * env becomes invalid. + * + *---------------------------------------------------------------------- + */ +static void FreeParams(ParamsPtr *paramsPtrPtr) +{ + ParamsPtr paramsPtr = *paramsPtrPtr; + char **p; + if(paramsPtr == NULL) { + return; + } + for (p = paramsPtr->vec; p < paramsPtr->cur; p++) { + free(*p); + } + free(paramsPtr->vec); + free(paramsPtr); + *paramsPtrPtr = NULL; +} + +/* + *---------------------------------------------------------------------- + * + * PutParam -- + * + * Add a name/value pair to a Params structure. + * + * Results: + * None. + * + * Side effects: + * Parameters structure updated. + * + *---------------------------------------------------------------------- + */ +static void PutParam(ParamsPtr paramsPtr, char *nameValue) +{ + intptr_t size; + + *paramsPtr->cur++ = nameValue; + size = paramsPtr->cur - paramsPtr->vec; + if(size >= paramsPtr->length) { + paramsPtr->length *= 2; + paramsPtr->vec = (FCGX_ParamArray)realloc(paramsPtr->vec, paramsPtr->length * sizeof(char *)); + paramsPtr->cur = paramsPtr->vec + size; + } + *paramsPtr->cur = NULL; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetParam -- obtain value of FCGI parameter in environment + * + * + * Results: + * Value bound to name, NULL if name not present in the + * environment envp. Caller must not mutate the result + * or retain it past the end of this request. + * + *---------------------------------------------------------------------- + */ +char *FCGX_GetParam(const char *name, FCGX_ParamArray envp) +{ + intptr_t len; + char **p; + + if (name == NULL || envp == NULL) return NULL; + + len = strlen(name); + + for (p = envp; *p; ++p) { + if((strncmp(name, *p, len) == 0) && ((*p)[len] == '=')) { + return *p+len+1; + } + } + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * Start of FastCGI-specific code + * + *---------------------------------------------------------------------- + */ + +/* + *---------------------------------------------------------------------- + * + * ReadParams -- + * + * Reads FastCGI name-value pairs from stream until EOF. Converts + * each pair to name=value format and adds it to Params structure. + * + *---------------------------------------------------------------------- + */ +static int ReadParams(Params *paramsPtr, FCGX_Stream *stream) +{ + int nameLen, valueLen; + unsigned char lenBuff[3]; + char *nameValue; + + while((nameLen = FCGX_GetChar(stream)) != EOF) { + /* + * Read name length (one or four bytes) and value length + * (one or four bytes) from stream. + */ + if((nameLen & 0x80) != 0) { + if(FCGX_GetStr((char *) &lenBuff[0], 3, stream) != 3) { + SetError(stream, FCGX_PARAMS_ERROR); + return -1; + } + nameLen = ((nameLen & 0x7f) << 24) + (lenBuff[0] << 16) + + (lenBuff[1] << 8) + lenBuff[2]; + } + if((valueLen = FCGX_GetChar(stream)) == EOF) { + SetError(stream, FCGX_PARAMS_ERROR); + return -1; + } + if((valueLen & 0x80) != 0) { + if(FCGX_GetStr((char *) &lenBuff[0], 3, stream) != 3) { + SetError(stream, FCGX_PARAMS_ERROR); + return -1; + } + valueLen = ((valueLen & 0x7f) << 24) + (lenBuff[0] << 16) + + (lenBuff[1] << 8) + lenBuff[2]; + } + /* + * nameLen and valueLen are now valid; read the name and value + * from stream and construct a standard environment entry. + */ + nameValue = (char *)Malloc(nameLen + valueLen + 2); + if(FCGX_GetStr(nameValue, nameLen, stream) != nameLen) { + SetError(stream, FCGX_PARAMS_ERROR); + free(nameValue); + return -1; + } + *(nameValue + nameLen) = '='; + if(FCGX_GetStr(nameValue + nameLen + 1, valueLen, stream) + != valueLen) { + SetError(stream, FCGX_PARAMS_ERROR); + free(nameValue); + return -1; + } + *(nameValue + nameLen + valueLen + 1) = '\0'; + PutParam(paramsPtr, nameValue); + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * MakeHeader -- + * + * Constructs an FCGI_Header struct. + * + *---------------------------------------------------------------------- + */ +static FCGI_Header MakeHeader( + int type, + int requestId, + int contentLength, + int paddingLength) +{ + FCGI_Header header; + ASSERT(contentLength >= 0 && contentLength <= FCGI_MAX_LENGTH); + ASSERT(paddingLength >= 0 && paddingLength <= 0xff); + header.version = FCGI_VERSION_1; + header.type = (unsigned char) type; + header.requestIdB1 = (unsigned char) ((requestId >> 8) & 0xff); + header.requestIdB0 = (unsigned char) ((requestId ) & 0xff); + header.contentLengthB1 = (unsigned char) ((contentLength >> 8) & 0xff); + header.contentLengthB0 = (unsigned char) ((contentLength ) & 0xff); + header.paddingLength = (unsigned char) paddingLength; + header.reserved = 0; + return header; +} + +/* + *---------------------------------------------------------------------- + * + * MakeEndRequestBody -- + * + * Constructs an FCGI_EndRequestBody struct. + * + *---------------------------------------------------------------------- + */ +static FCGI_EndRequestBody MakeEndRequestBody( + int appStatus, + int protocolStatus) +{ + FCGI_EndRequestBody body; + body.appStatusB3 = (unsigned char) ((appStatus >> 24) & 0xff); + body.appStatusB2 = (unsigned char) ((appStatus >> 16) & 0xff); + body.appStatusB1 = (unsigned char) ((appStatus >> 8) & 0xff); + body.appStatusB0 = (unsigned char) ((appStatus ) & 0xff); + body.protocolStatus = (unsigned char) protocolStatus; + memset(body.reserved, 0, sizeof(body.reserved)); + return body; +} + +/* + *---------------------------------------------------------------------- + * + * MakeUnknownTypeBody -- + * + * Constructs an FCGI_MakeUnknownTypeBody struct. + * + *---------------------------------------------------------------------- + */ +static FCGI_UnknownTypeBody MakeUnknownTypeBody( + int type) +{ + FCGI_UnknownTypeBody body; + body.type = (unsigned char) type; + memset(body.reserved, 0, sizeof(body.reserved)); + return body; +} + +/* + *---------------------------------------------------------------------- + * + * AlignInt8 -- + * + * Returns the smallest integer greater than or equal to n + * that's a multiple of 8. + * + *---------------------------------------------------------------------- + */ +static intptr_t AlignInt8(intptr_t n) { + return (n + 7) & ~7; +} + +/* + *---------------------------------------------------------------------- + * + * AlignPtr8 -- + * + * Returns the smallest pointer greater than or equal to p + * that's a multiple of 8. + * + *---------------------------------------------------------------------- + */ +static unsigned char *AlignPtr8(unsigned char *p) { + return (unsigned char *)((((uintptr_t)p) + 7) & ~7); +} + + +/* + * State associated with a stream + */ +typedef struct FCGX_Stream_Data { + unsigned char *buff; /* buffer after alignment */ + int bufflen; /* number of bytes buff can store */ + unsigned char *mBuff; /* buffer as returned by Malloc */ + unsigned char *buffStop; /* reader: last valid byte + 1 of entire buffer. + * stop generally differs from buffStop for + * readers because of record structure. + * writer: buff + bufflen */ + int type; /* reader: FCGI_PARAMS or FCGI_STDIN + * writer: FCGI_STDOUT or FCGI_STDERR */ + int eorStop; /* reader: stop stream at end-of-record */ + int skip; /* reader: don't deliver content bytes */ + int contentLen; /* reader: bytes of unread content */ + int paddingLen; /* reader: bytes of unread padding */ + int isAnythingWritten; /* writer: data has been written to ipcFd */ + int rawWrite; /* writer: write data without stream headers */ + FCGX_Request *reqDataPtr; /* request data not specific to one stream */ +} FCGX_Stream_Data; + +/* + *---------------------------------------------------------------------- + * + * WriteCloseRecords -- + * + * Writes an EOF record for the stream content if necessary. + * If this is the last writer to close, writes an FCGI_END_REQUEST + * record. + * + *---------------------------------------------------------------------- + */ +static void WriteCloseRecords(struct FCGX_Stream *stream) +{ + FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; + /* + * Enter rawWrite mode so final records won't be encapsulated as + * stream data. + */ + data->rawWrite = TRUE; + /* + * Generate EOF for stream content if needed. + */ + if(!(data->type == FCGI_STDERR + && stream->wrNext == data->buff + && !data->isAnythingWritten)) { + FCGI_Header header; + header = MakeHeader(data->type, data->reqDataPtr->requestId, 0, 0); + FCGX_PutStr((char *) &header, sizeof(header), stream); + }; + /* + * Generate FCGI_END_REQUEST record if needed. + */ + if(data->reqDataPtr->nWriters == 1) { + FCGI_EndRequestRecord endRequestRecord; + endRequestRecord.header = MakeHeader(FCGI_END_REQUEST, + data->reqDataPtr->requestId, + sizeof(endRequestRecord.body), 0); + endRequestRecord.body = MakeEndRequestBody( + data->reqDataPtr->appStatus, FCGI_REQUEST_COMPLETE); + FCGX_PutStr((char *) &endRequestRecord, + sizeof(endRequestRecord), stream); + } + data->reqDataPtr->nWriters--; +} + + + +static intptr_t write_it_all(int fd, char *buf, intptr_t len) +{ + intptr_t wrote; + + while (len) { + wrote = OS_Write(fd, buf, len); + if (wrote < 0) + return wrote; + len -= wrote; + buf += wrote; + } + return len; +} + +/* + *---------------------------------------------------------------------- + * + * EmptyBuffProc -- + * + * Encapsulates any buffered stream content in a FastCGI + * record. Writes the data, making the buffer empty. + * + *---------------------------------------------------------------------- + */ +static void EmptyBuffProc(struct FCGX_Stream *stream, int doClose) +{ + FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; + intptr_t cLen, eLen; + /* + * If the buffer contains stream data, fill in the header. + * Pad the record to a multiple of 8 bytes in length. Padding + * can't overflow the buffer because the buffer is a multiple + * of 8 bytes in length. If the buffer contains no stream + * data, reclaim the space reserved for the header. + */ + if(!data->rawWrite) { + cLen = stream->wrNext - data->buff - sizeof(FCGI_Header); + if(cLen > 0) { + eLen = AlignInt8(cLen); + /* + * Giving the padding a well-defined value keeps Purify happy. + */ + memset(stream->wrNext, 0, eLen - cLen); + stream->wrNext += eLen - cLen; + ASSERT(0 <= cLen && cLen <= INT_MAX); + ASSERT(0 <= eLen - cLen && eLen - cLen <= INT_MAX); + *((FCGI_Header *) data->buff) + = MakeHeader(data->type, + data->reqDataPtr->requestId, (int)cLen, (int)(eLen - cLen)); + } else { + stream->wrNext = data->buff; + } + } + if(doClose) { + WriteCloseRecords(stream); + }; + if (stream->wrNext != data->buff) { + data->isAnythingWritten = TRUE; + if (write_it_all(data->reqDataPtr->ipcFd, (char *)data->buff, stream->wrNext - data->buff) < 0) { + SetError(stream, OS_Errno); + return; + } + stream->wrNext = data->buff; + } + /* + * The buffer is empty. + */ + if(!data->rawWrite) { + stream->wrNext += sizeof(FCGI_Header); + } +} + +/* + * Return codes for Process* functions + */ +#define STREAM_RECORD 0 +#define SKIP 1 +#define BEGIN_RECORD 2 +#define MGMT_RECORD 3 + +/* + *---------------------------------------------------------------------- + * + * ProcessManagementRecord -- + * + * Reads and responds to a management record. The only type of + * management record this library understands is FCGI_GET_VALUES. + * The only variables that this library's FCGI_GET_VALUES + * understands are FCGI_MAX_CONNS, FCGI_MAX_REQS, and FCGI_MPXS_CONNS. + * Ignore other FCGI_GET_VALUES variables; respond to other + * management records with a FCGI_UNKNOWN_TYPE record. + * + *---------------------------------------------------------------------- + */ +static int ProcessManagementRecord(int type, FCGX_Stream *stream) +{ + FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; + ParamsPtr paramsPtr = NewParams(3); + char **pPtr; + char response[64]; /* 64 = 8 + 3*(1+1+14+1)* + padding */ + char *responseP = &response[FCGI_HEADER_LEN]; + char *name, value = '\0'; + intptr_t len, paddedLen; + if(type == FCGI_GET_VALUES) { + ReadParams(paramsPtr, stream); + if((FCGX_GetError(stream) != 0) || (data->contentLen != 0)) { + FreeParams(¶msPtr); + return FCGX_PROTOCOL_ERROR; + } + for (pPtr = paramsPtr->vec; pPtr < paramsPtr->cur; pPtr++) { + name = *pPtr; + *(strchr(name, '=')) = '\0'; + if(strcmp(name, FCGI_MAX_CONNS) == 0) { + value = '1'; + } else if(strcmp(name, FCGI_MAX_REQS) == 0) { + value = '1'; + } else if(strcmp(name, FCGI_MPXS_CONNS) == 0) { + value = '0'; + } else { + name = NULL; + } + if(name != NULL) { + len = strlen(name); + sprintf(responseP, "%c%c%s%c", (int)len, 1, name, value); + responseP += len + 3; + } + } + len = responseP - &response[FCGI_HEADER_LEN]; + paddedLen = AlignInt8(len); + ASSERT(0 <= len && len <= INT_MAX); + ASSERT(0 <= paddedLen - len && paddedLen - len <= INT_MAX); + *((FCGI_Header *) response) + = MakeHeader(FCGI_GET_VALUES_RESULT, FCGI_NULL_REQUEST_ID, + (int)len, (int)(paddedLen - len)); + FreeParams(¶msPtr); + } else { + paddedLen = len = sizeof(FCGI_UnknownTypeBody); + ASSERT(0 <= len && len <= INT_MAX); + ((FCGI_UnknownTypeRecord *) response)->header + = MakeHeader(FCGI_UNKNOWN_TYPE, FCGI_NULL_REQUEST_ID, + (int)len, 0); + ((FCGI_UnknownTypeRecord *) response)->body + = MakeUnknownTypeBody(type); + } + if (write_it_all(data->reqDataPtr->ipcFd, response, FCGI_HEADER_LEN + paddedLen) < 0) { + SetError(stream, OS_Errno); + return -1; + } + + return MGMT_RECORD; +} + +/* + *---------------------------------------------------------------------- + * + * ProcessBeginRecord -- + * + * Reads an FCGI_BEGIN_REQUEST record. + * + * Results: + * BEGIN_RECORD for normal return. FCGX_PROTOCOL_ERROR for + * protocol error. SKIP for attempt to multiplex + * connection. -1 for error from write (errno in stream). + * + * Side effects: + * In case of BEGIN_RECORD return, stores requestId, role, + * keepConnection values, and sets isBeginProcessed = TRUE. + * + *---------------------------------------------------------------------- + */ +static int ProcessBeginRecord(int requestId, FCGX_Stream *stream) +{ + FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; + FCGI_BeginRequestBody body; + if(requestId == 0 || data->contentLen != sizeof(body)) { + return FCGX_PROTOCOL_ERROR; + } + if(data->reqDataPtr->isBeginProcessed) { + /* + * The Web server is multiplexing the connection. This library + * doesn't know how to handle multiplexing, so respond with + * FCGI_END_REQUEST{protocolStatus = FCGI_CANT_MPX_CONN} + */ + FCGI_EndRequestRecord endRequestRecord; + endRequestRecord.header = MakeHeader(FCGI_END_REQUEST, + requestId, sizeof(endRequestRecord.body), 0); + endRequestRecord.body + = MakeEndRequestBody(0, FCGI_CANT_MPX_CONN); + if (write_it_all(data->reqDataPtr->ipcFd, (char *)&endRequestRecord, sizeof(endRequestRecord)) < 0) { + SetError(stream, OS_Errno); + return -1; + } + + return SKIP; + } + /* + * Accept this new request. Read the record body. + */ + data->reqDataPtr->requestId = requestId; + if(FCGX_GetStr((char *) &body, sizeof(body), stream) + != sizeof(body)) { + return FCGX_PROTOCOL_ERROR; + } + data->reqDataPtr->keepConnection = (body.flags & FCGI_KEEP_CONN); + data->reqDataPtr->role = (body.roleB1 << 8) + body.roleB0; + data->reqDataPtr->isBeginProcessed = TRUE; + return BEGIN_RECORD; +} + +/* + *---------------------------------------------------------------------- + * + * ProcessHeader -- + * + * Interprets FCGI_Header. Processes FCGI_BEGIN_REQUEST and + * management records here; extracts information from stream + * records (FCGI_PARAMS, FCGI_STDIN) into stream. + * + * Results: + * >= 0 for a normal return, < 0 for error + * + * Side effects: + * XXX: Many (more than there used to be). + * If !stream->isRequestIdSet, ProcessHeader initializes + * stream->requestId from header and sets stream->isRequestIdSet + * to TRUE. ProcessHeader also sets stream->contentLen to header's + * contentLength, and sets stream->paddingLen to the header's + * paddingLength. + * + *---------------------------------------------------------------------- + */ +static int ProcessHeader(FCGI_Header header, FCGX_Stream *stream) +{ + FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; + int requestId; + if(header.version != FCGI_VERSION_1) { + return FCGX_UNSUPPORTED_VERSION; + } + requestId = (header.requestIdB1 << 8) + + header.requestIdB0; + data->contentLen = (header.contentLengthB1 << 8) + + header.contentLengthB0; + data->paddingLen = header.paddingLength; + if(header.type == FCGI_BEGIN_REQUEST) { + return ProcessBeginRecord(requestId, stream); + } + if(requestId == FCGI_NULL_REQUEST_ID) { + return ProcessManagementRecord(header.type, stream); + } + if(requestId != data->reqDataPtr->requestId) { + return SKIP; + } + if(header.type != data->type) { + return FCGX_PROTOCOL_ERROR; + } + return STREAM_RECORD; +} + +/* + *---------------------------------------------------------------------- + * + * FillBuffProc -- + * + * Reads bytes from the ipcFd, supplies bytes to a stream client. + * + *---------------------------------------------------------------------- + */ +static void FillBuffProc(FCGX_Stream *stream) +{ + FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; + FCGI_Header header; + int headerLen = 0; + int status, count; + intptr_t ptrDiff; + + for (;;) { + /* + * If data->buff is empty, do a read. + */ + if(stream->rdNext == data->buffStop) { + count = OS_Read(data->reqDataPtr->ipcFd, (char *)data->buff, + data->bufflen); + if(count <= 0) { + SetError(stream, (count == 0 ? FCGX_PROTOCOL_ERROR : OS_Errno)); + return; + } + stream->rdNext = data->buff; + data->buffStop = data->buff + count; + } + /* + * Now data->buff is not empty. If the current record contains + * more content bytes, deliver all that are present in data->buff. + */ + if(data->contentLen > 0) { + ptrDiff = data->buffStop - stream->rdNext; + ASSERT(0 <= ptrDiff && ptrDiff <= INT_MAX); + count = min(data->contentLen, (int)ptrDiff); + data->contentLen -= count; + if(!data->skip) { + stream->wrNext = stream->stop = stream->rdNext + count; + return; + } else { + stream->rdNext += count; + if(data->contentLen > 0) { + continue; + } else { + data->skip = FALSE; + } + } + } + /* + * If the current record (whose content has been fully consumed by + * the client) was padded, skip over the padding bytes. + */ + if(data->paddingLen > 0) { + ptrDiff = data->buffStop - stream->rdNext; + ASSERT(0 <= ptrDiff && ptrDiff <= INT_MAX); + count = min(data->paddingLen, (int)ptrDiff); + data->paddingLen -= count; + stream->rdNext += count; + if(data->paddingLen > 0) { + continue; + } + } + /* + * All done with the current record, including the padding. + * If we're in a recursive call from ProcessHeader, deliver EOF. + */ + if(data->eorStop) { + stream->stop = stream->rdNext; + stream->isClosed = TRUE; + return; + } + /* + * Fill header with bytes from the input buffer. + */ + ptrDiff = data->buffStop - stream->rdNext; + ASSERT(0 <= ptrDiff && ptrDiff <= INT_MAX); + count = min((int)sizeof(header) - headerLen, + (int)ptrDiff); + memcpy(((char *)(&header)) + headerLen, stream->rdNext, count); + headerLen += count; + stream->rdNext += count; + if(headerLen < sizeof(header)) { + continue; + }; + headerLen = 0; + /* + * Interpret header. eorStop prevents ProcessHeader from reading + * past the end-of-record when using stream to read content. + */ + data->eorStop = TRUE; + stream->stop = stream->rdNext; + status = ProcessHeader(header, stream); + data->eorStop = FALSE; + stream->isClosed = FALSE; + switch(status) { + case STREAM_RECORD: + /* + * If this stream record header marked the end of stream + * data deliver EOF to the stream client, otherwise loop + * and deliver data. + * + * XXX: If this is final stream and + * stream->rdNext != data->buffStop, buffered + * data is next request (server pipelining)? + */ + if(data->contentLen == 0) { + stream->wrNext = stream->stop = stream->rdNext; + stream->isClosed = TRUE; + return; + } + break; + case SKIP: + data->skip = TRUE; + break; + case BEGIN_RECORD: + /* + * If this header marked the beginning of a new + * request, return role information to caller. + */ + return; + break; + case MGMT_RECORD: + break; + default: + ASSERT(status < 0); + SetError(stream, status); + return; + break; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * NewStream -- + * + * Creates a stream to read or write from an open ipcFd. + * The stream performs reads/writes of up to bufflen bytes. + * + *---------------------------------------------------------------------- + */ +static FCGX_Stream *NewStream( + FCGX_Request *reqDataPtr, int bufflen, int isReader, int streamType) +{ + intptr_t bufflen_p; + /* + * XXX: It would be a lot cleaner to have a NewStream that only + * knows about the type FCGX_Stream, with all other + * necessary data passed in. It appears that not just + * data and the two procs are needed for initializing stream, + * but also data->buff and data->buffStop. This has implications + * for procs that want to swap buffers, too. + */ + FCGX_Stream *stream = (FCGX_Stream *)Malloc(sizeof(FCGX_Stream)); + FCGX_Stream_Data *data = (FCGX_Stream_Data *)Malloc(sizeof(FCGX_Stream_Data)); + data->reqDataPtr = reqDataPtr; + bufflen_p = AlignInt8(min(max(bufflen, 32), FCGI_MAX_LENGTH + 1)); + ASSERT(0 <= bufflen_p && bufflen_p <= INT_MAX); + bufflen = (int)bufflen_p; + data->bufflen = bufflen; + data->mBuff = (unsigned char *)Malloc(bufflen); + data->buff = AlignPtr8(data->mBuff); + if(data->buff != data->mBuff) { + data->bufflen -= 8; + } + if(isReader) { + data->buffStop = data->buff; + } else { + data->buffStop = data->buff + data->bufflen; + } + data->type = streamType; + data->eorStop = FALSE; + data->skip = FALSE; + data->contentLen = 0; + data->paddingLen = 0; + data->isAnythingWritten = FALSE; + data->rawWrite = FALSE; + + stream->data = data; + stream->isReader = isReader; + stream->isClosed = FALSE; + stream->wasFCloseCalled = FALSE; + stream->FCGI_errno = 0; + if(isReader) { + stream->fillBuffProc = FillBuffProc; + stream->emptyBuffProc = NULL; + stream->rdNext = data->buff; + stream->stop = stream->rdNext; + stream->stopUnget = data->buff; + stream->wrNext = stream->stop; + } else { + stream->fillBuffProc = NULL; + stream->emptyBuffProc = EmptyBuffProc; + stream->wrNext = data->buff + sizeof(FCGI_Header); + stream->stop = data->buffStop; + stream->stopUnget = NULL; + stream->rdNext = stream->stop; + } + return stream; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_FreeStream -- + * + * Frees all storage allocated when *streamPtr was created, + * and nulls out *streamPtr. + * + *---------------------------------------------------------------------- + */ +void FCGX_FreeStream(FCGX_Stream **streamPtr) +{ + FCGX_Stream *stream = *streamPtr; + FCGX_Stream_Data *data; + if(stream == NULL) { + return; + } + data = (FCGX_Stream_Data *)stream->data; + data->reqDataPtr = NULL; + free(data->mBuff); + free(data); + free(stream); + *streamPtr = NULL; +} + +/* + *---------------------------------------------------------------------- + * + * SetReaderType -- + * + * Re-initializes the stream to read data of the specified type. + * + *---------------------------------------------------------------------- + */ +static FCGX_Stream *SetReaderType(FCGX_Stream *stream, int streamType) +{ + FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; + ASSERT(stream->isReader); + data->type = streamType; + data->eorStop = FALSE; + data->skip = FALSE; + data->contentLen = 0; + data->paddingLen = 0; + stream->wrNext = stream->stop = stream->rdNext; + stream->isClosed = FALSE; + return stream; +} + +/* + *---------------------------------------------------------------------- + * + * NewReader -- + * + * Creates a stream to read streamType records for the given + * request. The stream performs OS reads of up to bufflen bytes. + * + *---------------------------------------------------------------------- + */ +static FCGX_Stream *NewReader(FCGX_Request *reqDataPtr, int bufflen, int streamType) +{ + return NewStream(reqDataPtr, bufflen, TRUE, streamType); +} + +/* + *---------------------------------------------------------------------- + * + * NewWriter -- + * + * Creates a stream to write streamType FastCGI records, using + * the ipcFd and RequestId contained in *reqDataPtr. + * The stream performs OS writes of up to bufflen bytes. + * + *---------------------------------------------------------------------- + */ +static FCGX_Stream *NewWriter(FCGX_Request *reqDataPtr, int bufflen, int streamType) +{ + return NewStream(reqDataPtr, bufflen, FALSE, streamType); +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_CreateWriter -- + * + * Creates a stream to write streamType FastCGI records, using + * the given ipcFd and request Id. This function is provided + * for use by cgi-fcgi. In order to be defensive against misuse, + * this function leaks a little storage; cgi-fcgi doesn't care. + * + *---------------------------------------------------------------------- + */ +FCGX_Stream *FCGX_CreateWriter( + int ipcFd, + int requestId, + int bufflen, + int streamType) +{ + FCGX_Request *reqDataPtr = (FCGX_Request *)Malloc(sizeof(FCGX_Request)); + reqDataPtr->ipcFd = ipcFd; + reqDataPtr->requestId = requestId; + /* + * Suppress writing an FCGI_END_REQUEST record. + */ + reqDataPtr->nWriters = 2; + return NewWriter(reqDataPtr, bufflen, streamType); +} + +/* + *====================================================================== + * Control + *====================================================================== + */ + +/* + *---------------------------------------------------------------------- + * + * FCGX_IsCGI -- + * + * This routine determines if the process is running as a CGI or + * FastCGI process. The distinction is made by determining whether + * FCGI_LISTENSOCK_FILENO is a listener ipcFd or the end of a + * pipe (ie. standard in). + * + * Results: + * TRUE if the process is a CGI process, FALSE if FastCGI. + * + *---------------------------------------------------------------------- + */ +int FCGX_IsCGI(void) +{ + if (isFastCGI != -1) { + return !isFastCGI; + } + + if (!libInitialized) { + int rc = FCGX_Init(); + if (rc) { + /* exit() isn't great, but hey */ + exit((rc < 0) ? rc : -rc); + } + } + + isFastCGI = OS_IsFcgi(FCGI_LISTENSOCK_FILENO); + + return !isFastCGI; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_Finish -- + * + * Finishes the current request from the HTTP server. + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + *---------------------------------------------------------------------- + */ + +void FCGX_Finish(void) +{ + FCGX_Finish_r(&the_request); +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_Finish_r -- + * + * Finishes the current request from the HTTP server. + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + *---------------------------------------------------------------------- + */ +void FCGX_Finish_r(FCGX_Request *reqDataPtr) +{ + int close; + + if (reqDataPtr == NULL) { + return; + } + + close = !reqDataPtr->keepConnection; + + /* This should probably use a 'status' member instead of 'in' */ + if (reqDataPtr->in) { + close |= FCGX_FClose(reqDataPtr->err); + close |= FCGX_FClose(reqDataPtr->out); + + close |= FCGX_GetError(reqDataPtr->in); + } + + FCGX_Free(reqDataPtr, close); +} + +void FCGX_Free(FCGX_Request * request, int close) +{ + if (request == NULL) + return; + + FCGX_FreeStream(&request->in); + FCGX_FreeStream(&request->out); + FCGX_FreeStream(&request->err); + FreeParams(&request->paramsPtr); + + if (close) { + OS_IpcClose(request->ipcFd); + request->ipcFd = -1; + } +} + +int FCGX_OpenSocket(const char *path, int backlog) +{ + int rc = OS_CreateLocalIpcFd(path, backlog); + if (rc == FCGI_LISTENSOCK_FILENO && isFastCGI == 0) { + /* XXX probably need to call OS_LibInit() again for Win */ + isFastCGI = 1; + } + return rc; +} + +int FCGX_InitRequest(FCGX_Request *request, int sock, int flags) +{ + memset(request, 0, sizeof(FCGX_Request)); + + /* @@@ Should check that sock is open and listening */ + request->listen_sock = sock; + + /* @@@ Should validate against "known" flags */ + request->flags = flags; + + request->ipcFd = -1; + + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_Init -- + * + * Initilize the FCGX library. This is called by FCGX_Accept() + * but must be called by the user when using FCGX_Accept_r(). + * + * Results: + * 0 for successful call. + * + *---------------------------------------------------------------------- + */ +int FCGX_Init(void) +{ + char *p; + + if (libInitialized) { + return 0; + } + + FCGX_InitRequest(&the_request, FCGI_LISTENSOCK_FILENO, 0); + + if (OS_LibInit(NULL) == -1) { + return OS_Errno ? OS_Errno : -9997; + } + + p = getenv("FCGI_WEB_SERVER_ADDRS"); + webServerAddressList = p ? StringCopy(p) : NULL; + + libInitialized = 1; + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_Accept -- + * + * Accepts a new request from the HTTP server. + * + * Results: + * 0 for successful call, -1 for error. + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * Creates input, output, and error streams and + * assigns them to *in, *out, and *err respectively. + * Creates a parameters data structure to be accessed + * via getenv(3) (if assigned to environ) or by FCGX_GetParam + * and assigns it to *envp. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + *---------------------------------------------------------------------- + */ + +int FCGX_Accept( + FCGX_Stream **in, + FCGX_Stream **out, + FCGX_Stream **err, + FCGX_ParamArray *envp) +{ + int rc; + + if (! libInitialized) { + rc = FCGX_Init(); + if (rc) { + return rc; + } + } + + rc = FCGX_Accept_r(&the_request); + + *in = the_request.in; + *out = the_request.out; + *err = the_request.err; + *envp = the_request.envp; + + return rc; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_Accept_r -- + * + * Accepts a new request from the HTTP server. + * + * Results: + * 0 for successful call, -1 for error. + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * Creates input, output, and error streams and + * assigns them to *in, *out, and *err respectively. + * Creates a parameters data structure to be accessed + * via getenv(3) (if assigned to environ) or by FCGX_GetParam + * and assigns it to *envp. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + *---------------------------------------------------------------------- + */ +int FCGX_Accept_r(FCGX_Request *reqDataPtr) +{ + if (!libInitialized) { + return -9998; + } + + /* Finish the current request, if any. */ + FCGX_Finish_r(reqDataPtr); + + for (;;) { + /* + * If a connection isn't open, accept a new connection (blocking). + * If an OS error occurs in accepting the connection, + * return -1 to the caller, who should exit. + */ + if (reqDataPtr->ipcFd < 0) { + int fail_on_intr = reqDataPtr->flags & FCGI_FAIL_ACCEPT_ON_INTR; + + reqDataPtr->ipcFd = OS_Accept(reqDataPtr->listen_sock, fail_on_intr, webServerAddressList); + if (reqDataPtr->ipcFd < 0) { + return (errno > 0) ? (0 - errno) : -9999; + } + } + /* + * A connection is open. Read from the connection in order to + * get the request's role and environment. If protocol or other + * errors occur, close the connection and try again. + */ + reqDataPtr->isBeginProcessed = FALSE; + reqDataPtr->in = NewReader(reqDataPtr, 8192, 0); + FillBuffProc(reqDataPtr->in); + if(!reqDataPtr->isBeginProcessed) { + goto TryAgain; + } + { + char *roleStr; + switch(reqDataPtr->role) { + case FCGI_RESPONDER: + roleStr = "FCGI_ROLE=RESPONDER"; + break; + case FCGI_AUTHORIZER: + roleStr = "FCGI_ROLE=AUTHORIZER"; + break; + case FCGI_FILTER: + roleStr = "FCGI_ROLE=FILTER"; + break; + default: + goto TryAgain; + } + reqDataPtr->paramsPtr = NewParams(30); + PutParam(reqDataPtr->paramsPtr, StringCopy(roleStr)); + } + SetReaderType(reqDataPtr->in, FCGI_PARAMS); + if(ReadParams(reqDataPtr->paramsPtr, reqDataPtr->in) >= 0) { + /* + * Finished reading the environment. No errors occurred, so + * leave the connection-retry loop. + */ + break; + } + + /* + * Close the connection and try again. + */ +TryAgain: + FCGX_Free(reqDataPtr, 1); + + } /* for (;;) */ + /* + * Build the remaining data structures representing the new + * request and return successfully to the caller. + */ + SetReaderType(reqDataPtr->in, FCGI_STDIN); + reqDataPtr->out = NewWriter(reqDataPtr, 8192, FCGI_STDOUT); + reqDataPtr->err = NewWriter(reqDataPtr, 512, FCGI_STDERR); + reqDataPtr->nWriters = 2; + reqDataPtr->envp = reqDataPtr->paramsPtr->vec; + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_StartFilterData -- + * + * stream is an input stream for a FCGI_FILTER request. + * stream is positioned at EOF on FCGI_STDIN. + * Repositions stream to the start of FCGI_DATA. + * If the preconditions are not met (e.g. FCGI_STDIN has not + * been read to EOF) sets the stream error code to + * FCGX_CALL_SEQ_ERROR. + * + * Results: + * 0 for a normal return, < 0 for error + * + *---------------------------------------------------------------------- + */ + +int FCGX_StartFilterData(FCGX_Stream *stream) +{ + FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; + if(data->reqDataPtr->role != FCGI_FILTER + || !stream->isReader + || !stream->isClosed + || data->type != FCGI_STDIN) { + SetError(stream, FCGX_CALL_SEQ_ERROR); + return -1; + } + SetReaderType(stream, FCGI_DATA); + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * FCGX_SetExitStatus -- + * + * Sets the exit status for stream's request. The exit status + * is the status code the request would have exited with, had + * the request been run as a CGI program. You can call + * SetExitStatus several times during a request; the last call + * before the request ends determines the value. + * + *---------------------------------------------------------------------- + */ + +void FCGX_SetExitStatus(int status, FCGX_Stream *stream) +{ + FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; + data->reqDataPtr->appStatus = status; +} diff --git a/library/server/libfcgi/Clib/libfcgi/libfcgi/fcgio.cpp b/library/server/libfcgi/Clib/libfcgi/libfcgi/fcgio.cpp new file mode 100644 index 00000000..5684a5f6 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/libfcgi/fcgio.cpp @@ -0,0 +1,203 @@ +// +// $Id: fcgio.cpp,v 1.13 2002/02/24 20:12:22 robs Exp $ +// +// Allows you communicate with FastCGI streams using C++ iostreams +// +// ORIGINAL AUTHOR: George Feinberg +// REWRITTEN BY: Michael Richards 06/20/1999 +// REWRITTEN AGAIN BY: Michael Shell 02/23/2000 +// REWRITTEN AGAIN BY: Rob Saccoccio 11 Nov 2001 +// +// Copyright (c) 2000 Tux the Linux Penguin +// +// You are free to use this software without charge or royalty +// as long as this notice is not removed or altered, and recognition +// is given to the author(s) +// +// This code is offered as-is without any warranty either expressed or +// implied; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. + +#ifdef _WIN32 +#define DLLAPI __declspec(dllexport) +#endif + +#ifndef _WIN32 +#include +#include +#endif + +#include +#include "fcgio.h" +#include "fcgimisc.h" + +using std::streambuf; +using std::istream; +using std::ostream; +using std::streamsize; + +fcgi_streambuf::fcgi_streambuf(FCGX_Stream * fs, char * b, int bs) +{ + init(fs, b, bs); +} + +fcgi_streambuf::fcgi_streambuf(char_type * b, streamsize bs) +{ + init(0, b, bs); +} + +fcgi_streambuf::fcgi_streambuf(FCGX_Stream * fs) +{ + init(fs, 0, 0); +} + +fcgi_streambuf::~fcgi_streambuf(void) +{ + overflow(EOF); + // FCGX_Finish()/FCGX_Accept() will flush and close +} + +void fcgi_streambuf::init(FCGX_Stream * fs, char_type * b, streamsize bs) +{ + this->fcgx = fs; + this->buf = 0; + this->bufsize = 0; + setbuf(b, bs); +} + +int fcgi_streambuf::overflow(int c) +{ + if (this->bufsize) + { + intptr_t plen = pptr() - pbase(); + + if (plen) + { + ASSERT(0 <= plen && plen <= INT_MAX); + if (FCGX_PutStr(pbase(), (int)plen, this->fcgx) != plen) return EOF; + pbump((int)(-plen)); + } + } + + if (c != EOF) + { + if (FCGX_PutChar(c, this->fcgx) != c) return EOF; + } + + return 0; +} + +// default base class behaviour seems to be inconsistent +int fcgi_streambuf::sync() +{ + if (overflow(EOF)) return EOF; + if (FCGX_FFlush(this->fcgx)) return EOF; + return 0; +} + +// uflow() removes the char, underflow() doesn't +int fcgi_streambuf::uflow() +{ + int rv = underflow(); + if (this->bufsize) gbump(1); + return rv; +} + +// Note that the expected behaviour when there is no buffer varies +int fcgi_streambuf::underflow() +{ + if (this->bufsize) + { + if (in_avail() == 0) + { + ASSERT(0 <= this->bufsize && this->bufsize <= INT_MAX); + int glen = FCGX_GetStr(eback(), (int)(this->bufsize), this->fcgx); + if (glen <= 0) return EOF; + + setg(eback(), eback(), eback() + glen); + } + + return (unsigned char) *gptr(); + } + else + { + return FCGX_GetChar(this->fcgx); + } +} + +void fcgi_streambuf::reset(void) +{ + // it should be ok to set up both the get and put areas + setg(this->buf, this->buf, this->buf); + setp(this->buf, this->buf + this->bufsize); +} + +std::streambuf * fcgi_streambuf::setbuf(char_type * b, streamsize bs) +{ + // XXX support moving data from an old buffer + if (this->bufsize) return 0; + + this->buf = b; + this->bufsize = bs; + + // the base setbuf() *has* to be called + streambuf::setbuf(b, bs); + + reset(); + + return this; +} + +int fcgi_streambuf::attach(FCGX_Stream * fs) +{ + this->fcgx = fs; + + if (this->bufsize) + { + reset(); + } + + return 0; +} + +streamsize fcgi_streambuf::xsgetn(char_type * s, streamsize n) +{ + if (n > INT_MAX) return 0; + return (this->bufsize) + ? streambuf::xsgetn(s, n) + : (streamsize) FCGX_GetStr((char *) s, (int) n, this->fcgx); +} + +streamsize fcgi_streambuf::xsputn(const char_type * s, streamsize n) +{ + if (n > INT_MAX) return 0; + return (this->bufsize) + ? streambuf::xsputn(s, n) + : (streamsize) FCGX_PutStr((char *) s, (int) n, this->fcgx); +} + +// deprecated +fcgi_istream::fcgi_istream(FCGX_Stream * fs) : + istream(&fcgi_strmbuf) +{ + fcgi_strmbuf.attach(fs); +} + +// deprecated +void fcgi_istream::attach(FCGX_Stream * fs) +{ + fcgi_strmbuf.attach(fs); +} + +// deprecated +fcgi_ostream::fcgi_ostream(FCGX_Stream * fs) : + ostream(&fcgi_strmbuf) +{ + fcgi_strmbuf.attach(fs); +} + +// deprecated +void fcgi_ostream::attach(FCGX_Stream * fs) +{ + fcgi_strmbuf.attach(fs); +} diff --git a/library/server/libfcgi/Clib/libfcgi/libfcgi/libfcgi.mak b/library/server/libfcgi/Clib/libfcgi/libfcgi/libfcgi.mak new file mode 100644 index 00000000..85c751b1 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/libfcgi/libfcgi.mak @@ -0,0 +1,311 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on libfcgi.dsp + +!IF "$(CFG)" == "" +CFG=release +!ENDIF + +!IF "$(CFG)" != "release" && "$(CFG)" != "debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libfcgi.mak" CFG="debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "release" + +OUTDIR=.\..\libfcgi\Release +INTDIR=.\..\libfcgi\Release +# Begin Custom Macros +OutDir=.\..\libfcgi\Release +# End Custom Macros + +ALL : "$(OUTDIR)\libfcgi.dll" + + +CLEAN : + -@erase "$(INTDIR)\fcgi_stdio.obj" + -@erase "$(INTDIR)\fcgiapp.obj" + -@erase "$(INTDIR)\fcgio.obj" + -@erase "$(INTDIR)\os_win32.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\libfcgi.dll" + -@erase "$(OUTDIR)\libfcgi.exp" + -@erase "$(OUTDIR)\libfcgi.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\libfcgi.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\libfcgi.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=Ws2_32.lib /nologo /dll /pdb:none /machine:I386 /out:"$(OUTDIR)\libfcgi.dll" /implib:"$(OUTDIR)\libfcgi.lib" +LINK32_OBJS= \ + "$(INTDIR)\fcgi_stdio.obj" \ + "$(INTDIR)\fcgiapp.obj" \ + "$(INTDIR)\fcgio.obj" \ + "$(INTDIR)\os_win32.obj" + +"$(OUTDIR)\libfcgi.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "debug" + +OUTDIR=.\..\libfcgi\Debug +INTDIR=.\..\libfcgi\Debug +# Begin Custom Macros +OutDir=.\..\libfcgi\Debug +# End Custom Macros + +ALL : "$(OUTDIR)\libfcgi.dll" "$(OUTDIR)\libfcgi.bsc" + + +CLEAN : + -@erase "$(INTDIR)\fcgi_stdio.obj" + -@erase "$(INTDIR)\fcgi_stdio.sbr" + -@erase "$(INTDIR)\fcgiapp.obj" + -@erase "$(INTDIR)\fcgiapp.sbr" + -@erase "$(INTDIR)\fcgio.obj" + -@erase "$(INTDIR)\fcgio.sbr" + -@erase "$(INTDIR)\os_win32.obj" + -@erase "$(INTDIR)\os_win32.sbr" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(OUTDIR)\libfcgi.bsc" + -@erase "$(OUTDIR)\libfcgi.dll" + -@erase "$(OUTDIR)\libfcgi.exp" + -@erase "$(OUTDIR)\libfcgi.lib" + -@erase "$(OUTDIR)\libfcgi.map" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W4 /Gm /Gi /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\libfcgi.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\libfcgi.bsc" +BSC32_SBRS= \ + "$(INTDIR)\fcgi_stdio.sbr" \ + "$(INTDIR)\fcgiapp.sbr" \ + "$(INTDIR)\fcgio.sbr" \ + "$(INTDIR)\os_win32.sbr" + +"$(OUTDIR)\libfcgi.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +LINK32_FLAGS=Ws2_32.lib /nologo /dll /profile /map:"$(INTDIR)\libfcgi.map" /debug /machine:I386 /out:"$(OUTDIR)\libfcgi.dll" /implib:"$(OUTDIR)\libfcgi.lib" +LINK32_OBJS= \ + "$(INTDIR)\fcgi_stdio.obj" \ + "$(INTDIR)\fcgiapp.obj" \ + "$(INTDIR)\fcgio.obj" \ + "$(INTDIR)\os_win32.obj" + +"$(OUTDIR)\libfcgi.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + + +..\libfcgi\fcgi_stdio.c : \ + "..\include\fcgi_config.h"\ + "..\include\fcgi_stdio.h"\ + "..\include\fcgiapp.h"\ + "..\include\fcgimisc.h"\ + "..\include\fcgios.h"\ + + +..\libfcgi\fcgiapp.c : \ + "..\include\fastcgi.h"\ + "..\include\fcgi_config.h"\ + "..\include\fcgiapp.h"\ + "..\include\fcgimisc.h"\ + "..\include\fcgios.h"\ + + +..\libfcgi\fcgio.cpp : \ + "..\include\fcgiapp.h"\ + "..\include\fcgio.h"\ + + +..\libfcgi\os_win32.c : \ + "..\include\fcgi_config.h"\ + "..\include\fcgimisc.h"\ + "..\include\fcgios.h"\ + + +!IF "$(CFG)" == "release" || "$(CFG)" == "debug" +SOURCE=..\libfcgi\fcgi_stdio.c + +!IF "$(CFG)" == "release" + + +"$(INTDIR)\fcgi_stdio.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "debug" + + +"$(INTDIR)\fcgi_stdio.obj" "$(INTDIR)\fcgi_stdio.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\libfcgi\fcgiapp.c + +!IF "$(CFG)" == "release" + + +"$(INTDIR)\fcgiapp.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "debug" + + +"$(INTDIR)\fcgiapp.obj" "$(INTDIR)\fcgiapp.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\libfcgi\fcgio.cpp + +!IF "$(CFG)" == "release" + +CPP_SWITCHES=/nologo /MD /W3 /GX /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\libfcgi.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +"$(INTDIR)\fcgio.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) @<< + $(CPP_SWITCHES) $(SOURCE) +<< + + +!ELSEIF "$(CFG)" == "debug" + +CPP_SWITCHES=/nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\libfcgi.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +"$(INTDIR)\fcgio.obj" "$(INTDIR)\fcgio.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) @<< + $(CPP_SWITCHES) $(SOURCE) +<< + + +!ENDIF + +SOURCE=..\libfcgi\os_unix.c +SOURCE=..\libfcgi\os_win32.c + +!IF "$(CFG)" == "release" + + +"$(INTDIR)\os_win32.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "debug" + + +"$(INTDIR)\os_win32.obj" "$(INTDIR)\os_win32.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\libfcgi\strerror.c + +!ENDIF + diff --git a/library/server/libfcgi/Clib/libfcgi/libfcgi/os_unix.c b/library/server/libfcgi/Clib/libfcgi/libfcgi/os_unix.c new file mode 100644 index 00000000..73e6a7f5 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/libfcgi/os_unix.c @@ -0,0 +1,1293 @@ +/* + * os_unix.c -- + * + * Description of file. + * + * + * Copyright (c) 1995 Open Market, Inc. + * All rights reserved. + * + * This file contains proprietary and confidential information and + * remains the unpublished property of Open Market, Inc. Use, + * disclosure, or reproduction is prohibited except as permitted by + * express written license agreement with Open Market, Inc. + * + * Bill Snapper + * snapper@openmarket.com + */ + +#ifndef lint +static const char rcsid[] = "$Id: os_unix.c,v 1.37 2002/03/05 19:14:49 robs Exp $"; +#endif /* not lint */ + +#include "fcgi_config.h" + +#include + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#include +#include +#include +#include /* for fcntl */ +#include +#include /* for memchr() */ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_NETDB_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include /* for getpeername */ +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "fastcgi.h" +#include "fcgimisc.h" +#include "fcgios.h" + +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long) -1) +#endif + +/* + * This structure holds an entry for each oustanding async I/O operation. + */ +typedef struct { + OS_AsyncProc procPtr; /* callout completion procedure */ + ClientData clientData; /* caller private data */ + int fd; + int len; + int offset; + void *buf; + int inUse; +} AioInfo; + +/* + * Entries in the async I/O table are allocated 2 per file descriptor. + * + * Read Entry Index = fd * 2 + * Write Entry Index = (fd * 2) + 1 + */ +#define AIO_RD_IX(fd) (fd * 2) +#define AIO_WR_IX(fd) ((fd * 2) + 1) + +static int asyncIoInUse = FALSE; +static int asyncIoTableSize = 16; +static AioInfo *asyncIoTable = NULL; + +static int libInitialized = FALSE; + +static fd_set readFdSet; +static fd_set writeFdSet; + +static fd_set readFdSetPost; +static int numRdPosted = 0; +static fd_set writeFdSetPost; +static int numWrPosted = 0; +static int volatile maxFd = -1; + +static int shutdownPending = FALSE; +static int shutdownNow = FALSE; + +void OS_ShutdownPending() +{ + shutdownPending = TRUE; +} + +static void OS_Sigusr1Handler(int signo) +{ + OS_ShutdownPending(); +} + +static void OS_SigpipeHandler(int signo) +{ + ; +} + +static void installSignalHandler(int signo, const struct sigaction * act, int force) +{ + struct sigaction sa; + + sigaction(signo, NULL, &sa); + + if (force || sa.sa_handler == SIG_DFL) + { + sigaction(signo, act, NULL); + } +} + +static void OS_InstallSignalHandlers(int force) +{ + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + sa.sa_handler = OS_SigpipeHandler; + installSignalHandler(SIGPIPE, &sa, force); + + sa.sa_handler = OS_Sigusr1Handler; + installSignalHandler(SIGUSR1, &sa, force); +} + +/* + *-------------------------------------------------------------- + * + * OS_LibInit -- + * + * Set up the OS library for use. + * + * NOTE: This function is really only needed for application + * asynchronous I/O. It will most likely change in the + * future to setup the multi-threaded environment. + * + * Results: + * Returns 0 if success, -1 if not. + * + * Side effects: + * Async I/O table allocated and initialized. + * + *-------------------------------------------------------------- + */ +int OS_LibInit(int stdioFds[3]) +{ + if(libInitialized) + return 0; + + asyncIoTable = (AioInfo *)malloc(asyncIoTableSize * sizeof(AioInfo)); + if(asyncIoTable == NULL) { + errno = ENOMEM; + return -1; + } + memset((char *) asyncIoTable, 0, + asyncIoTableSize * sizeof(AioInfo)); + + FD_ZERO(&readFdSet); + FD_ZERO(&writeFdSet); + FD_ZERO(&readFdSetPost); + FD_ZERO(&writeFdSetPost); + + OS_InstallSignalHandlers(FALSE); + + libInitialized = TRUE; + + return 0; +} + +/* + *-------------------------------------------------------------- + * + * OS_LibShutdown -- + * + * Shutdown the OS library. + * + * Results: + * None. + * + * Side effects: + * Memory freed, fds closed. + * + *-------------------------------------------------------------- + */ +void OS_LibShutdown() +{ + if(!libInitialized) + return; + + free(asyncIoTable); + asyncIoTable = NULL; + libInitialized = FALSE; + return; +} + +/* + *---------------------------------------------------------------------- + * + * OS_BuildSockAddrUn -- + * + * Using the pathname bindPath, fill in the sockaddr_un structure + * *servAddrPtr and the length of this structure *servAddrLen. + * + * The format of the sockaddr_un structure changed incompatibly in + * 4.3BSD Reno. Digital UNIX supports both formats, other systems + * support one or the other. + * + * Results: + * 0 for normal return, -1 for failure (bindPath too long). + * + *---------------------------------------------------------------------- + */ + +static int OS_BuildSockAddrUn(const char *bindPath, + struct sockaddr_un *servAddrPtr, + int *servAddrLen) +{ + int bindPathLen = strlen(bindPath); + +#ifdef HAVE_SOCKADDR_UN_SUN_LEN /* 4.3BSD Reno and later: BSDI, DEC */ + if(bindPathLen >= sizeof(servAddrPtr->sun_path)) { + return -1; + } +#else /* 4.3 BSD Tahoe: Solaris, HPUX, DEC, ... */ + if(bindPathLen > sizeof(servAddrPtr->sun_path)) { + return -1; + } +#endif + memset((char *) servAddrPtr, 0, sizeof(*servAddrPtr)); + servAddrPtr->sun_family = AF_UNIX; + memcpy(servAddrPtr->sun_path, bindPath, bindPathLen); +#ifdef HAVE_SOCKADDR_UN_SUN_LEN /* 4.3BSD Reno and later: BSDI, DEC */ + *servAddrLen = sizeof(servAddrPtr->sun_len) + + sizeof(servAddrPtr->sun_family) + + bindPathLen + 1; + servAddrPtr->sun_len = *servAddrLen; +#else /* 4.3 BSD Tahoe: Solaris, HPUX, DEC, ... */ + *servAddrLen = sizeof(servAddrPtr->sun_family) + bindPathLen; +#endif + return 0; +} +union SockAddrUnion { + struct sockaddr_un unixVariant; + struct sockaddr_in inetVariant; +}; + +/* + * OS_CreateLocalIpcFd -- + * + * This procedure is responsible for creating the listener socket + * on Unix for local process communication. It will create a + * domain socket or a TCP/IP socket bound to "localhost" and return + * a file descriptor to it to the caller. + * + * Results: + * Listener socket created. This call returns either a valid + * file descriptor or -1 on error. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +int OS_CreateLocalIpcFd(const char *bindPath, int backlog) +{ + int listenSock, servLen; + union SockAddrUnion sa; + int tcp = FALSE; + unsigned long tcp_ia = 0; + char *tp; + short port = 0; + char host[MAXPATHLEN]; + + strcpy(host, bindPath); + if((tp = strchr(host, ':')) != 0) { + *tp++ = 0; + if((port = atoi(tp)) == 0) { + *--tp = ':'; + } else { + tcp = TRUE; + } + } + if(tcp) { + if (!*host || !strcmp(host,"*")) { + tcp_ia = htonl(INADDR_ANY); + } else { + tcp_ia = inet_addr(host); + if (tcp_ia == INADDR_NONE) { + struct hostent * hep; + hep = gethostbyname(host); + if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { + fprintf(stderr, "Cannot resolve host name %s -- exiting!\n", host); + exit(1); + } + if (hep->h_addr_list[1]) { + fprintf(stderr, "Host %s has multiple addresses ---\n", host); + fprintf(stderr, "you must choose one explicitly!!!\n"); + exit(1); + } + tcp_ia = ((struct in_addr *) (hep->h_addr))->s_addr; + } + } + } + + if(tcp) { + listenSock = socket(AF_INET, SOCK_STREAM, 0); + if(listenSock >= 0) { + int flag = 1; + if(setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, + (char *) &flag, sizeof(flag)) < 0) { + fprintf(stderr, "Can't set SO_REUSEADDR.\n"); + exit(1001); + } + } + } else { + listenSock = socket(AF_UNIX, SOCK_STREAM, 0); + } + if(listenSock < 0) { + return -1; + } + + /* + * Bind the listening socket. + */ + if(tcp) { + memset((char *) &sa.inetVariant, 0, sizeof(sa.inetVariant)); + sa.inetVariant.sin_family = AF_INET; + sa.inetVariant.sin_addr.s_addr = tcp_ia; + sa.inetVariant.sin_port = htons(port); + servLen = sizeof(sa.inetVariant); + } else { + unlink(bindPath); + if(OS_BuildSockAddrUn(bindPath, &sa.unixVariant, &servLen)) { + fprintf(stderr, "Listening socket's path name is too long.\n"); + exit(1000); + } + } + if(bind(listenSock, (struct sockaddr *) &sa.unixVariant, servLen) < 0 + || listen(listenSock, backlog) < 0) { + perror("bind/listen"); + exit(errno); + } + + return listenSock; +} + +/* + *---------------------------------------------------------------------- + * + * OS_FcgiConnect -- + * + * Create the socket and connect to the remote application if + * possible. + * + * This was lifted from the cgi-fcgi application and was abstracted + * out because Windows NT does not have a domain socket and must + * use a named pipe which has a different API altogether. + * + * Results: + * -1 if fail or a valid file descriptor if connection succeeds. + * + * Side effects: + * Remote connection established. + * + *---------------------------------------------------------------------- + */ +int OS_FcgiConnect(char *bindPath) +{ + union SockAddrUnion sa; + int servLen, resultSock; + int connectStatus; + char *tp; + char host[MAXPATHLEN]; + short port = 0; + int tcp = FALSE; + + strcpy(host, bindPath); + if((tp = strchr(host, ':')) != 0) { + *tp++ = 0; + if((port = atoi(tp)) == 0) { + *--tp = ':'; + } else { + tcp = TRUE; + } + } + if(tcp == TRUE) { + struct hostent *hp; + if((hp = gethostbyname((*host ? host : "localhost"))) == NULL) { + fprintf(stderr, "Unknown host: %s\n", bindPath); + exit(1000); + } + sa.inetVariant.sin_family = AF_INET; + memcpy(&sa.inetVariant.sin_addr, hp->h_addr, hp->h_length); + sa.inetVariant.sin_port = htons(port); + servLen = sizeof(sa.inetVariant); + resultSock = socket(AF_INET, SOCK_STREAM, 0); + } else { + if(OS_BuildSockAddrUn(bindPath, &sa.unixVariant, &servLen)) { + fprintf(stderr, "Listening socket's path name is too long.\n"); + exit(1000); + } + resultSock = socket(AF_UNIX, SOCK_STREAM, 0); + } + + ASSERT(resultSock >= 0); + connectStatus = connect(resultSock, (struct sockaddr *) &sa.unixVariant, + servLen); + if(connectStatus >= 0) { + return resultSock; + } else { + /* + * Most likely (errno == ENOENT || errno == ECONNREFUSED) + * and no FCGI application server is running. + */ + close(resultSock); + return -1; + } +} + +/* + *-------------------------------------------------------------- + * + * OS_Read -- + * + * Pass through to the unix read function. + * + * Results: + * Returns number of byes read, 0, or -1 failure: errno + * contains actual error. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ +int OS_Read(int fd, char * buf, size_t len) +{ + if (shutdownNow) return -1; + return(read(fd, buf, len)); +} + +/* + *-------------------------------------------------------------- + * + * OS_Write -- + * + * Pass through to unix write function. + * + * Results: + * Returns number of byes read, 0, or -1 failure: errno + * contains actual error. + * + * Side effects: + * none. + * + *-------------------------------------------------------------- + */ +int OS_Write(int fd, char * buf, size_t len) +{ + if (shutdownNow) return -1; + return(write(fd, buf, len)); +} + +/* + *---------------------------------------------------------------------- + * + * OS_SpawnChild -- + * + * Spawns a new FastCGI listener process. + * + * Results: + * 0 if success, -1 if error. + * + * Side effects: + * Child process spawned. + * + *---------------------------------------------------------------------- + */ +int OS_SpawnChild(char *appPath, int listenFd) +{ + int forkResult; + + forkResult = fork(); + if(forkResult < 0) { + exit(errno); + } + + if(forkResult == 0) { + /* + * Close STDIN unconditionally. It's used by the parent + * process for CGI communication. The FastCGI applciation + * will be replacing this with the FastCGI listenFd IF + * STDIN_FILENO is the same as FCGI_LISTENSOCK_FILENO + * (which it is on Unix). Regardless, STDIN, STDOUT, and + * STDERR will be closed as the FastCGI process uses a + * multiplexed socket in their place. + */ + close(STDIN_FILENO); + + /* + * If the listenFd is already the value of FCGI_LISTENSOCK_FILENO + * we're set. If not, change it so the child knows where to + * get the listen socket from. + */ + if(listenFd != FCGI_LISTENSOCK_FILENO) { + dup2(listenFd, FCGI_LISTENSOCK_FILENO); + close(listenFd); + } + + close(STDOUT_FILENO); + close(STDERR_FILENO); + + /* + * We're a child. Exec the application. + * + * XXX: entire environment passes through + */ + execl(appPath, appPath, NULL); + /* + * XXX: Can't do this as we've already closed STDERR!!! + * + * perror("exec"); + */ + exit(errno); + } + return 0; +} + +/* + *-------------------------------------------------------------- + * + * OS_AsyncReadStdin -- + * + * This initiates an asynchronous read on the standard + * input handle. + * + * The abstraction is necessary because Windows NT does not + * have a clean way of "select"ing a file descriptor for + * I/O. + * + * Results: + * -1 if error, 0 otherwise. + * + * Side effects: + * Asynchronous bit is set in the readfd variable and + * request is enqueued. + * + *-------------------------------------------------------------- + */ +int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, + ClientData clientData) +{ + int index = AIO_RD_IX(STDIN_FILENO); + + asyncIoInUse = TRUE; + ASSERT(asyncIoTable[index].inUse == 0); + asyncIoTable[index].procPtr = procPtr; + asyncIoTable[index].clientData = clientData; + asyncIoTable[index].fd = STDIN_FILENO; + asyncIoTable[index].len = len; + asyncIoTable[index].offset = 0; + asyncIoTable[index].buf = buf; + asyncIoTable[index].inUse = 1; + FD_SET(STDIN_FILENO, &readFdSet); + if(STDIN_FILENO > maxFd) + maxFd = STDIN_FILENO; + return 0; +} + +static void GrowAsyncTable(void) +{ + int oldTableSize = asyncIoTableSize; + + asyncIoTableSize = asyncIoTableSize * 2; + asyncIoTable = (AioInfo *)realloc(asyncIoTable, asyncIoTableSize * sizeof(AioInfo)); + if(asyncIoTable == NULL) { + errno = ENOMEM; + exit(errno); + } + memset((char *) &asyncIoTable[oldTableSize], 0, + oldTableSize * sizeof(AioInfo)); + +} + +/* + *-------------------------------------------------------------- + * + * OS_AsyncRead -- + * + * This initiates an asynchronous read on the file + * handle which may be a socket or named pipe. + * + * We also must save the ProcPtr and ClientData, so later + * when the io completes, we know who to call. + * + * We don't look at any results here (the ReadFile may + * return data if it is cached) but do all completion + * processing in OS_Select when we get the io completion + * port done notifications. Then we call the callback. + * + * Results: + * -1 if error, 0 otherwise. + * + * Side effects: + * Asynchronous I/O operation is queued for completion. + * + *-------------------------------------------------------------- + */ +int OS_AsyncRead(int fd, int offset, void *buf, int len, + OS_AsyncProc procPtr, ClientData clientData) +{ + int index = AIO_RD_IX(fd); + + ASSERT(asyncIoTable != NULL); + asyncIoInUse = TRUE; + + if(fd > maxFd) + maxFd = fd; + + while (index >= asyncIoTableSize) { + GrowAsyncTable(); + } + + ASSERT(asyncIoTable[index].inUse == 0); + asyncIoTable[index].procPtr = procPtr; + asyncIoTable[index].clientData = clientData; + asyncIoTable[index].fd = fd; + asyncIoTable[index].len = len; + asyncIoTable[index].offset = offset; + asyncIoTable[index].buf = buf; + asyncIoTable[index].inUse = 1; + FD_SET(fd, &readFdSet); + return 0; +} + +/* + *-------------------------------------------------------------- + * + * OS_AsyncWrite -- + * + * This initiates an asynchronous write on the "fake" file + * descriptor (which may be a file, socket, or named pipe). + * We also must save the ProcPtr and ClientData, so later + * when the io completes, we know who to call. + * + * We don't look at any results here (the WriteFile generally + * completes immediately) but do all completion processing + * in OS_DoIo when we get the io completion port done + * notifications. Then we call the callback. + * + * Results: + * -1 if error, 0 otherwise. + * + * Side effects: + * Asynchronous I/O operation is queued for completion. + * + *-------------------------------------------------------------- + */ +int OS_AsyncWrite(int fd, int offset, void *buf, int len, + OS_AsyncProc procPtr, ClientData clientData) +{ + int index = AIO_WR_IX(fd); + + asyncIoInUse = TRUE; + + if(fd > maxFd) + maxFd = fd; + + while (index >= asyncIoTableSize) { + GrowAsyncTable(); + } + + ASSERT(asyncIoTable[index].inUse == 0); + asyncIoTable[index].procPtr = procPtr; + asyncIoTable[index].clientData = clientData; + asyncIoTable[index].fd = fd; + asyncIoTable[index].len = len; + asyncIoTable[index].offset = offset; + asyncIoTable[index].buf = buf; + asyncIoTable[index].inUse = 1; + FD_SET(fd, &writeFdSet); + return 0; +} + +/* + *-------------------------------------------------------------- + * + * OS_Close -- + * + * Closes the descriptor. This is a pass through to the + * Unix close. + * + * Results: + * 0 for success, -1 on failure + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ +int OS_Close(int fd) +{ + if (fd == -1) + return 0; + + if (asyncIoInUse) { + int index = AIO_RD_IX(fd); + + FD_CLR(fd, &readFdSet); + FD_CLR(fd, &readFdSetPost); + if (asyncIoTable[index].inUse != 0) { + asyncIoTable[index].inUse = 0; + } + + FD_CLR(fd, &writeFdSet); + FD_CLR(fd, &writeFdSetPost); + index = AIO_WR_IX(fd); + if (asyncIoTable[index].inUse != 0) { + asyncIoTable[index].inUse = 0; + } + + if (maxFd == fd) { + maxFd--; + } + } + + /* + * shutdown() the send side and then read() from client until EOF + * or a timeout expires. This is done to minimize the potential + * that a TCP RST will be sent by our TCP stack in response to + * receipt of additional data from the client. The RST would + * cause the client to discard potentially useful response data. + */ + + if (shutdown(fd, 1) == 0) + { + struct timeval tv; + fd_set rfds; + int rv; + char trash[1024]; + + FD_ZERO(&rfds); + + do + { + FD_SET(fd, &rfds); + tv.tv_sec = 2; + tv.tv_usec = 0; + rv = select(fd + 1, &rfds, NULL, NULL, &tv); + } + while (rv > 0 && read(fd, trash, sizeof(trash)) > 0); + } + + return close(fd); +} + +/* + *-------------------------------------------------------------- + * + * OS_CloseRead -- + * + * Cancel outstanding asynchronous reads and prevent subsequent + * reads from completing. + * + * Results: + * Socket or file is shutdown. Return values mimic Unix shutdown: + * 0 success, -1 failure + * + *-------------------------------------------------------------- + */ +int OS_CloseRead(int fd) +{ + if(asyncIoTable[AIO_RD_IX(fd)].inUse != 0) { + asyncIoTable[AIO_RD_IX(fd)].inUse = 0; + FD_CLR(fd, &readFdSet); + } + + return shutdown(fd, 0); +} + +/* + *-------------------------------------------------------------- + * + * OS_DoIo -- + * + * This function was formerly OS_Select. It's purpose is + * to pull I/O completion events off the queue and dispatch + * them to the appropriate place. + * + * Results: + * Returns 0. + * + * Side effects: + * Handlers are called. + * + *-------------------------------------------------------------- + */ +int OS_DoIo(struct timeval *tmo) +{ + int fd, len, selectStatus; + OS_AsyncProc procPtr; + ClientData clientData; + AioInfo *aioPtr; + fd_set readFdSetCpy; + fd_set writeFdSetCpy; + + asyncIoInUse = TRUE; + FD_ZERO(&readFdSetCpy); + FD_ZERO(&writeFdSetCpy); + + for(fd = 0; fd <= maxFd; fd++) { + if(FD_ISSET(fd, &readFdSet)) { + FD_SET(fd, &readFdSetCpy); + } + if(FD_ISSET(fd, &writeFdSet)) { + FD_SET(fd, &writeFdSetCpy); + } + } + + /* + * If there were no completed events from a prior call, see if there's + * any work to do. + */ + if(numRdPosted == 0 && numWrPosted == 0) { + selectStatus = select((maxFd+1), &readFdSetCpy, &writeFdSetCpy, + NULL, tmo); + if(selectStatus < 0) { + exit(errno); + } + + for(fd = 0; fd <= maxFd; fd++) { + /* + * Build up a list of completed events. We'll work off of + * this list as opposed to looping through the read and write + * fd sets since they can be affected by a callbacl routine. + */ + if(FD_ISSET(fd, &readFdSetCpy)) { + numRdPosted++; + FD_SET(fd, &readFdSetPost); + FD_CLR(fd, &readFdSet); + } + + if(FD_ISSET(fd, &writeFdSetCpy)) { + numWrPosted++; + FD_SET(fd, &writeFdSetPost); + FD_CLR(fd, &writeFdSet); + } + } + } + + if(numRdPosted == 0 && numWrPosted == 0) + return 0; + + for(fd = 0; fd <= maxFd; fd++) { + /* + * Do reads and dispatch callback. + */ + if(FD_ISSET(fd, &readFdSetPost) + && asyncIoTable[AIO_RD_IX(fd)].inUse) { + + numRdPosted--; + FD_CLR(fd, &readFdSetPost); + aioPtr = &asyncIoTable[AIO_RD_IX(fd)]; + + len = read(aioPtr->fd, aioPtr->buf, aioPtr->len); + + procPtr = aioPtr->procPtr; + aioPtr->procPtr = NULL; + clientData = aioPtr->clientData; + aioPtr->inUse = 0; + + (*procPtr)(clientData, len); + } + + /* + * Do writes and dispatch callback. + */ + if(FD_ISSET(fd, &writeFdSetPost) && + asyncIoTable[AIO_WR_IX(fd)].inUse) { + + numWrPosted--; + FD_CLR(fd, &writeFdSetPost); + aioPtr = &asyncIoTable[AIO_WR_IX(fd)]; + + len = write(aioPtr->fd, aioPtr->buf, aioPtr->len); + + procPtr = aioPtr->procPtr; + aioPtr->procPtr = NULL; + clientData = aioPtr->clientData; + aioPtr->inUse = 0; + (*procPtr)(clientData, len); + } + } + return 0; +} + +/* + * Not all systems have strdup(). + * @@@ autoconf should determine whether or not this is needed, but for now.. + */ +static char * str_dup(const char * str) +{ + char * sdup = (char *) malloc(strlen(str) + 1); + + if (sdup) + strcpy(sdup, str); + + return sdup; +} + +/* + *---------------------------------------------------------------------- + * + * ClientAddrOK -- + * + * Checks if a client address is in a list of allowed addresses + * + * Results: + * TRUE if address list is empty or client address is present + * in the list, FALSE otherwise. + * + *---------------------------------------------------------------------- + */ +static int ClientAddrOK(struct sockaddr_in *saPtr, const char *clientList) +{ + int result = FALSE; + char *clientListCopy, *cur, *next; + + if (clientList == NULL || *clientList == '\0') { + return TRUE; + } + + clientListCopy = str_dup(clientList); + + for (cur = clientListCopy; cur != NULL; cur = next) { + next = strchr(cur, ','); + if (next != NULL) { + *next++ = '\0'; + } + if (inet_addr(cur) == saPtr->sin_addr.s_addr) { + result = TRUE; + break; + } + } + + free(clientListCopy); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * AcquireLock -- + * + * On platforms that implement concurrent calls to accept + * on a shared listening ipcFd, returns 0. On other platforms, + * acquires an exclusive lock across all processes sharing a + * listening ipcFd, blocking until the lock has been acquired. + * + * Results: + * 0 for successful call, -1 in case of system error (fatal). + * + * Side effects: + * This process now has the exclusive lock. + * + *---------------------------------------------------------------------- + */ +static int AcquireLock(int sock, int fail_on_intr) +{ +#ifdef USE_LOCKING + do { + struct flock lock; + lock.l_type = F_WRLCK; + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; + + if (fcntl(sock, F_SETLKW, &lock) != -1) + return 0; + } while (errno == EINTR + && ! fail_on_intr + && ! shutdownPending); + + return -1; + +#else + return 0; +#endif +} + +/* + *---------------------------------------------------------------------- + * + * ReleaseLock -- + * + * On platforms that implement concurrent calls to accept + * on a shared listening ipcFd, does nothing. On other platforms, + * releases an exclusive lock acquired by AcquireLock. + * + * Results: + * 0 for successful call, -1 in case of system error (fatal). + * + * Side effects: + * This process no longer holds the lock. + * + *---------------------------------------------------------------------- + */ +static int ReleaseLock(int sock) +{ +#ifdef USE_LOCKING + do { + struct flock lock; + lock.l_type = F_UNLCK; + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; + + if (fcntl(sock, F_SETLK, &lock) != -1) + return 0; + } while (errno == EINTR); + + return -1; + +#else + return 0; +#endif +} + +/********************************************************************** + * Determine if the errno resulting from a failed accept() warrants a + * retry or exit(). Based on Apache's http_main.c accept() handling + * and Stevens' Unix Network Programming Vol 1, 2nd Ed, para. 15.6. + */ +static int is_reasonable_accept_errno (const int error) +{ + switch (error) { +#ifdef EPROTO + /* EPROTO on certain older kernels really means ECONNABORTED, so + * we need to ignore it for them. See discussion in new-httpd + * archives nh.9701 search for EPROTO. Also see nh.9603, search + * for EPROTO: There is potentially a bug in Solaris 2.x x<6, and + * other boxes that implement tcp sockets in userland (i.e. on top of + * STREAMS). On these systems, EPROTO can actually result in a fatal + * loop. See PR#981 for example. It's hard to handle both uses of + * EPROTO. */ + case EPROTO: +#endif +#ifdef ECONNABORTED + case ECONNABORTED: +#endif + /* Linux generates the rest of these, other tcp stacks (i.e. + * bsd) tend to hide them behind getsockopt() interfaces. They + * occur when the net goes sour or the client disconnects after the + * three-way handshake has been done in the kernel but before + * userland has picked up the socket. */ +#ifdef ECONNRESET + case ECONNRESET: +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: +#endif +#ifdef ENETUNREACH + case ENETUNREACH: +#endif + return 1; + + default: + return 0; + } +} + +/********************************************************************** + * This works around a problem on Linux 2.0.x and SCO Unixware (maybe + * others?). When a connect() is made to a Unix Domain socket, but its + * not accept()ed before the web server gets impatient and close()s, an + * accept() results in a valid file descriptor, but no data to read. + * This causes a block on the first read() - which never returns! + * + * Another approach to this is to write() to the socket to provoke a + * SIGPIPE, but this is a pain because of the FastCGI protocol, the fact + * that whatever is written has to be universally ignored by all FastCGI + * web servers, and a SIGPIPE handler has to be installed which returns + * (or SIGPIPE is ignored). + * + * READABLE_UNIX_FD_DROP_DEAD_TIMEVAL = 2,0 by default. + * + * Making it shorter is probably safe, but I'll leave that to you. Making + * it 0,0 doesn't work reliably. The shorter you can reliably make it, + * the faster your application will be able to recover (waiting 2 seconds + * may _cause_ the problem when there is a very high demand). At any rate, + * this is better than perma-blocking. + */ +static int is_af_unix_keeper(const int fd) +{ + struct timeval tval = { READABLE_UNIX_FD_DROP_DEAD_TIMEVAL }; + fd_set read_fds; + + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + + return select(fd + 1, &read_fds, NULL, NULL, &tval) >= 0 && FD_ISSET(fd, &read_fds); +} + +/* + *---------------------------------------------------------------------- + * + * OS_Accept -- + * + * Accepts a new FastCGI connection. This routine knows whether + * we're dealing with TCP based sockets or NT Named Pipes for IPC. + * + * Results: + * -1 if the operation fails, otherwise this is a valid IPC fd. + * + * Side effects: + * New IPC connection is accepted. + * + *---------------------------------------------------------------------- + */ +int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs) +{ + int socket = -1; + union { + struct sockaddr_un un; + struct sockaddr_in in; + } sa; + + for (;;) { + if (AcquireLock(listen_sock, fail_on_intr)) + return -1; + + for (;;) { + do { +#ifdef HAVE_SOCKLEN + socklen_t len = sizeof(sa); +#else + int len = sizeof(sa); +#endif + if (shutdownPending) break; + /* There's a window here */ + + socket = accept(listen_sock, (struct sockaddr *)&sa, &len); + } while (socket < 0 + && errno == EINTR + && ! fail_on_intr + && ! shutdownPending); + + if (socket < 0) { + if (shutdownPending || ! is_reasonable_accept_errno(errno)) { + int errnoSave = errno; + + ReleaseLock(listen_sock); + + if (! shutdownPending) { + errno = errnoSave; + } + + return (-1); + } + errno = 0; + } + else { /* socket >= 0 */ + int set = 1; + + if (sa.in.sin_family != AF_INET) + break; + +#ifdef TCP_NODELAY + /* No replies to outgoing data, so disable Nagle */ + setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *)&set, sizeof(set)); +#endif + + /* Check that the client IP address is approved */ + if (ClientAddrOK(&sa.in, webServerAddrs)) + break; + + close(socket); + } /* socket >= 0 */ + } /* for(;;) */ + + if (ReleaseLock(listen_sock)) + return (-1); + + if (sa.in.sin_family != AF_UNIX || is_af_unix_keeper(socket)) + break; + + close(socket); + } /* while(1) - lock */ + + return (socket); +} + +/* + *---------------------------------------------------------------------- + * + * OS_IpcClose + * + * OS IPC routine to close an IPC connection. + * + * Results: + * + * + * Side effects: + * IPC connection is closed. + * + *---------------------------------------------------------------------- + */ +int OS_IpcClose(int ipcFd) +{ + return OS_Close(ipcFd); +} + +/* + *---------------------------------------------------------------------- + * + * OS_IsFcgi -- + * + * Determines whether this process is a FastCGI process or not. + * + * Results: + * Returns 1 if FastCGI, 0 if not. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +int OS_IsFcgi(int sock) +{ + union { + struct sockaddr_in in; + struct sockaddr_un un; + } sa; +#ifdef HAVE_SOCKLEN + socklen_t len = sizeof(sa); +#else + int len = sizeof(sa); +#endif + + errno = 0; + + if (getpeername(sock, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) { + return TRUE; + } + else { + return FALSE; + } +} + +/* + *---------------------------------------------------------------------- + * + * OS_SetFlags -- + * + * Sets selected flag bits in an open file descriptor. + * + *---------------------------------------------------------------------- + */ +void OS_SetFlags(int fd, int flags) +{ + int val; + if((val = fcntl(fd, F_GETFL, 0)) < 0) { + exit(errno); + } + val |= flags; + if(fcntl(fd, F_SETFL, val) < 0) { + exit(errno); + } +} diff --git a/library/server/libfcgi/Clib/libfcgi/libfcgi/os_win32.c b/library/server/libfcgi/Clib/libfcgi/libfcgi/os_win32.c new file mode 100644 index 00000000..28186017 --- /dev/null +++ b/library/server/libfcgi/Clib/libfcgi/libfcgi/os_win32.c @@ -0,0 +1,1918 @@ +/* + * os_win32.c -- + * + * + * Copyright (c) 1995 Open Market, Inc. + * All rights reserved. + * + * This file contains proprietary and confidential information and + * remains the unpublished property of Open Market, Inc. Use, + * disclosure, or reproduction is prohibited except as permitted by + * express written license agreement with Open Market, Inc. + * + * Bill Snapper + * snapper@openmarket.com + * + * (Special thanks to Karen and Bill. They made my job much easier and + * significantly more enjoyable.) + */ +#ifndef lint +static const char rcsid[] = "$Id: os_win32.c,v 1.33 2002/03/05 18:15:15 robs Exp $"; +#endif /* not lint */ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include + +#define DLLAPI __declspec(dllexport) + +#include "fcgimisc.h" +#include "fcgios.h" + +#define WIN32_OPEN_MAX 128 /* XXX: Small hack */ + +/* + * millisecs to wait for a client connection before checking the + * shutdown flag (then go back to waiting for a connection, etc). + */ +#define ACCEPT_TIMEOUT 1000 + +#define MUTEX_VARNAME "_FCGI_MUTEX_" +#define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_" +#define LOCALHOST "localhost" + +static HANDLE hIoCompPort = INVALID_HANDLE_VALUE; +static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE; +static HANDLE hStdinThread = INVALID_HANDLE_VALUE; + +static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE}; + +// This is a nail for listening to more than one port.. +static HANDLE acceptMutex = INVALID_HANDLE_VALUE; + +static BOOLEAN shutdownPending = FALSE; +static BOOLEAN shutdownNow = FALSE; + +/* + * An enumeration of the file types + * supported by the FD_TABLE structure. + * + * XXX: Not all currently supported. This allows for future + * functionality. + */ +typedef enum { + FD_UNUSED, + FD_FILE_SYNC, + FD_FILE_ASYNC, + FD_SOCKET_SYNC, + FD_SOCKET_ASYNC, + FD_PIPE_SYNC, + FD_PIPE_ASYNC +} FILE_TYPE; + +typedef union { + HANDLE fileHandle; + SOCKET sock; + unsigned int value; +} DESCRIPTOR; + +/* + * Structure used to map file handle and socket handle + * values into values that can be used to create unix-like + * select bitmaps, read/write for both sockets/files. + */ +struct FD_TABLE { + DESCRIPTOR fid; + FILE_TYPE type; + char *path; + DWORD Errno; + unsigned long instance; + int status; + int offset; /* only valid for async file writes */ + LPDWORD offsetHighPtr; /* pointers to offset high and low words */ + LPDWORD offsetLowPtr; /* only valid for async file writes (logs) */ + HANDLE hMapMutex; /* mutex handle for multi-proc offset update */ + LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */ +}; + +/* + * XXX Note there is no dyanmic sizing of this table, so if the + * number of open file descriptors exceeds WIN32_OPEN_MAX the + * app will blow up. + */ +static struct FD_TABLE fdTable[WIN32_OPEN_MAX]; + +static CRITICAL_SECTION fdTableCritical; + +struct OVERLAPPED_REQUEST { + OVERLAPPED overlapped; + unsigned long instance; /* file instance (won't match after a close) */ + OS_AsyncProc procPtr; /* callback routine */ + ClientData clientData; /* callback argument */ + ClientData clientData1; /* additional clientData */ +}; +typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST; + +static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\"; + +static FILE_TYPE listenType = FD_UNUSED; + +// XXX This should be a DESCRIPTOR +static HANDLE hListen = INVALID_HANDLE_VALUE; + +static BOOLEAN libInitialized = FALSE; + +HANDLE strToHandle(char *str) +{ +#ifdef _WIN64 + return (HANDLE)_atoi64(str); +#else + return (HANDLE)atoi(str); +#endif +} + + +/* + *-------------------------------------------------------------- + * + * Win32NewDescriptor -- + * + * Set up for I/O descriptor masquerading. + * + * Results: + * Returns "fake id" which masquerades as a UNIX-style "small + * non-negative integer" file/socket descriptor. + * Win32_* routine below will "do the right thing" based on the + * descriptor's actual type. -1 indicates failure. + * + * Side effects: + * Entry in fdTable is reserved to represent the socket/file. + * + *-------------------------------------------------------------- + */ +static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd) +{ + int index = -1; + + EnterCriticalSection(&fdTableCritical); + + /* + * If desiredFd is set, try to get this entry (this is used for + * mapping stdio handles). Otherwise try to get the fd entry. + * If this is not available, find a the first empty slot. . + */ + if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX) + { + if (fdTable[desiredFd].type == FD_UNUSED) + { + index = desiredFd; + } + } + else if (fd > 0) + { + if (fd < WIN32_OPEN_MAX && fdTable[fd].type == FD_UNUSED) + { + index = fd; + } + else + { + int i; + + for (i = 1; i < WIN32_OPEN_MAX; ++i) + { + if (fdTable[i].type == FD_UNUSED) + { + index = i; + break; + } + } + } + } + + if (index != -1) + { + fdTable[index].fid.value = fd; + fdTable[index].type = type; + fdTable[index].path = NULL; + fdTable[index].Errno = NO_ERROR; + fdTable[index].status = 0; + fdTable[index].offset = -1; + fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL; + fdTable[index].hMapMutex = NULL; + fdTable[index].ovList = NULL; + } + + LeaveCriticalSection(&fdTableCritical); + return index; +} + +/* + *-------------------------------------------------------------- + * + * StdinThread-- + * + * This thread performs I/O on stadard input. It is needed + * because you can't guarantee that all applications will + * create standard input with sufficient access to perform + * asynchronous I/O. Since we don't want to block the app + * reading from stdin we make it look like it's using I/O + * completion ports to perform async I/O. + * + * Results: + * Data is read from stdin and posted to the io completion + * port. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ +static void StdinThread(void * startup) +{ + int doIo = TRUE; + ULONG_PTR fd; + unsigned long bytesRead; + POVERLAPPED_REQUEST pOv; + + // Touch the arg to prevent warning + startup = NULL; + + while(doIo) { + /* + * Block until a request to read from stdin comes in or a + * request to terminate the thread arrives (fd = -1). + */ + if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd, + (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) { + doIo = 0; + break; + } + + ASSERT((fd == STDIN_FILENO) || (fd == -1)); + if(fd == -1) { + doIo = 0; + break; + } + ASSERT(pOv->clientData1 != NULL); + + if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead, + &bytesRead, NULL)) { + PostQueuedCompletionStatus(hIoCompPort, bytesRead, + STDIN_FILENO, (LPOVERLAPPED)pOv); + } else { + doIo = 0; + break; + } + } + + ExitThread(0); +} + +void OS_ShutdownPending(void) +{ + shutdownPending = TRUE; +} + +static void ShutdownRequestThread(void * arg) +{ + HANDLE shutdownEvent = (HANDLE) arg; + + WaitForSingleObject(shutdownEvent, INFINITE); + + shutdownPending = TRUE; + + if (listenType == FD_PIPE_SYNC) + { + // Its a hassle to get ConnectNamedPipe to return early, + // so just wack the whole process - yes, this will toast + // any requests in progress, but at least its a clean + // shutdown (its better than TerminateProcess()) + exit(0); + } + + // FD_SOCKET_SYNC: When in Accept(), select() is used to poll + // the shutdownPending flag - yeah this isn't pretty either + // but its only one process doing it if an Accept mutex is used. + // This at least buys no toasted requests. +} + +/* + *-------------------------------------------------------------- + * + * OS_LibInit -- + * + * Set up the OS library for use. + * + * Results: + * Returns 0 if success, -1 if not. + * + * Side effects: + * Sockets initialized, pseudo file descriptors setup, etc. + * + *-------------------------------------------------------------- + */ +int OS_LibInit(int stdioFds[3]) +{ + WORD wVersion; + WSADATA wsaData; + int err; + int fakeFd; + char *cLenPtr = NULL; + char *val = NULL; + + if(libInitialized) + return 0; + + InitializeCriticalSection(&fdTableCritical); + + /* + * Initialize windows sockets library. + */ + wVersion = MAKEWORD(2,0); + err = WSAStartup( wVersion, &wsaData ); + if (err) { + fprintf(stderr, "Error starting Windows Sockets. Error: %d", + WSAGetLastError()); + exit(111); + } + + /* + * Create the I/O completion port to be used for our I/O queue. + */ + if (hIoCompPort == INVALID_HANDLE_VALUE) { + hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, + 0, 1); + if(hIoCompPort == INVALID_HANDLE_VALUE) { + printf("

OS_LibInit Failed CreateIoCompletionPort! ERROR: %d

\r\n\r\n", + GetLastError()); + return -1; + } + } + + /* + * If a shutdown event is in the env, save it (I don't see any to + * remove it from the environment out from under the application). + * Spawn a thread to wait on the shutdown request. + */ + val = getenv(SHUTDOWN_EVENT_NAME); + if (val != NULL) + { + HANDLE shutdownEvent = strToHandle(val); + + if (_beginthread(ShutdownRequestThread, 0, shutdownEvent) == -1) + { + return -1; + } + } + + if (acceptMutex == INVALID_HANDLE_VALUE) + { + /* If an accept mutex is in the env, use it */ + val = getenv(MUTEX_VARNAME); + if (val != NULL) + { + acceptMutex = strToHandle(val); + } + } + + /* + * Determine if this library is being used to listen for FastCGI + * connections. This is communicated by STDIN containing a + * valid handle to a listener object. In this case, both the + * "stdout" and "stderr" handles will be INVALID (ie. closed) by + * the starting process. + * + * The trick is determining if this is a pipe or a socket... + * + * XXX: Add the async accept test to determine socket or handle to a + * pipe!!! + */ + if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) && + (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) && + (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) ) + { + DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT; + HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE); + + // Move the handle to a "low" number + if (! DuplicateHandle(GetCurrentProcess(), oldStdIn, + GetCurrentProcess(), &hListen, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + return -1; + } + + if (! SetStdHandle(STD_INPUT_HANDLE, hListen)) + { + return -1; + } + + CloseHandle(oldStdIn); + + /* + * Set the pipe handle state so that it operates in wait mode. + * + * NOTE: The listenFd is not mapped to a pseudo file descriptor + * as all work done on it is contained to the OS library. + * + * XXX: Initial assumption is that SetNamedPipeHandleState will + * fail if this is an IP socket... + */ + if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) + { + listenType = FD_PIPE_SYNC; + } + else + { + listenType = FD_SOCKET_SYNC; + } + } + + /* + * If there are no stdioFds passed in, we're done. + */ + if(stdioFds == NULL) { + libInitialized = 1; + return 0; + } + + /* + * Setup standard input asynchronous I/O. There is actually a separate + * thread spawned for this purpose. The reason for this is that some + * web servers use anonymous pipes for the connection between itself + * and a CGI application. Anonymous pipes can't perform asynchronous + * I/O or use I/O completion ports. Therefore in order to present a + * consistent I/O dispatch model to an application we emulate I/O + * completion port behavior by having the standard input thread posting + * messages to the hIoCompPort which look like a complete overlapped + * I/O structure. This keeps the event dispatching simple from the + * application perspective. + */ + stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE); + + if(!SetHandleInformation(stdioHandles[STDIN_FILENO], + HANDLE_FLAG_INHERIT, 0)) { +/* + * XXX: Causes error when run from command line. Check KB + err = GetLastError(); + DebugBreak(); + exit(99); + */ + } + + ASSERT(INT_MIN <= (intptr_t)stdioHandles[STDIN_FILENO] && + (intptr_t)stdioHandles[STDIN_FILENO] <= INT_MAX); + if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, + (int)(intptr_t)stdioHandles[STDIN_FILENO], + STDIN_FILENO)) == -1) { + return -1; + } else { + /* + * Set stdin equal to our pseudo FD and create the I/O completion + * port to be used for async I/O. + */ + stdioFds[STDIN_FILENO] = fakeFd; + } + + /* + * Create the I/O completion port to be used for communicating with + * the thread doing I/O on standard in. This port will carry read + * and possibly thread termination requests to the StdinThread. + */ + if (hStdinCompPort == INVALID_HANDLE_VALUE) { + hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, + 0, 1); + if(hStdinCompPort == INVALID_HANDLE_VALUE) { + printf("

OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d

\r\n\r\n", + GetLastError()); + return -1; + } + } + + /* + * Create the thread that will read stdin if the CONTENT_LENGTH + * is non-zero. + */ + if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL && + atoi(cLenPtr) > 0) { + hStdinThread = (HANDLE) _beginthread(StdinThread, 0, NULL); + if (hStdinThread == (HANDLE)(LONG_PTR) -1) { + printf("

OS_LibInit Failed to create STDIN thread! ERROR: %d

\r\n\r\n", + GetLastError()); + return -1; + } + } + + /* + * STDOUT will be used synchronously. + * + * XXX: May want to convert this so that it could be used for OVERLAPPED + * I/O later. If so, model it after the Stdin I/O as stdout is + * also incapable of async I/O on some servers. + */ + stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE); + if(!SetHandleInformation(stdioHandles[STDOUT_FILENO], + HANDLE_FLAG_INHERIT, FALSE)) { + DebugBreak(); + exit(99); + } + + ASSERT(INT_MIN <= (intptr_t)stdioHandles[STDOUT_FILENO] && + (intptr_t)stdioHandles[STDOUT_FILENO] <= INT_MAX); + if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, + (int)(intptr_t)stdioHandles[STDOUT_FILENO], + STDOUT_FILENO)) == -1) { + return -1; + } else { + /* + * Set stdout equal to our pseudo FD + */ + stdioFds[STDOUT_FILENO] = fakeFd; + } + + stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE); + if(!SetHandleInformation(stdioHandles[STDERR_FILENO], + HANDLE_FLAG_INHERIT, FALSE)) { + DebugBreak(); + exit(99); + } + ASSERT(INT_MIN <= (intptr_t)stdioHandles[STDERR_FILENO] && + (intptr_t)stdioHandles[STDERR_FILENO] <= INT_MAX); + if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, + (int)(intptr_t)stdioHandles[STDERR_FILENO], + STDERR_FILENO)) == -1) { + return -1; + } else { + /* + * Set stderr equal to our pseudo FD + */ + stdioFds[STDERR_FILENO] = fakeFd; + } + + return 0; +} + +/* + *-------------------------------------------------------------- + * + * OS_LibShutdown -- + * + * Shutdown the OS library. + * + * Results: + * None. + * + * Side effects: + * Memory freed, handles closed. + * + *-------------------------------------------------------------- + */ +void OS_LibShutdown() +{ + + if (hIoCompPort != INVALID_HANDLE_VALUE) + { + CloseHandle(hIoCompPort); + hIoCompPort = INVALID_HANDLE_VALUE; + } + + if (hStdinCompPort != INVALID_HANDLE_VALUE) + { + CloseHandle(hStdinCompPort); + hStdinCompPort = INVALID_HANDLE_VALUE; + } + + if (acceptMutex != INVALID_HANDLE_VALUE) + { + ReleaseMutex(acceptMutex); + } + + DisconnectNamedPipe(hListen); + + CancelIo(hListen); + + + WSACleanup(); +} + +/* + *-------------------------------------------------------------- + * + * Win32FreeDescriptor -- + * + * Free I/O descriptor entry in fdTable. + * + * Results: + * Frees I/O descriptor entry in fdTable. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ +static void Win32FreeDescriptor(int fd) +{ + /* Catch it if fd is a bogus value */ + ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); + + EnterCriticalSection(&fdTableCritical); + + if (fdTable[fd].type != FD_UNUSED) + { + switch (fdTable[fd].type) + { + case FD_FILE_SYNC: + case FD_FILE_ASYNC: + + /* Free file path string */ + ASSERT(fdTable[fd].path != NULL); + free(fdTable[fd].path); + fdTable[fd].path = NULL; + break; + + default: + break; + } + + ASSERT(fdTable[fd].path == NULL); + + fdTable[fd].type = FD_UNUSED; + fdTable[fd].path = NULL; + fdTable[fd].Errno = NO_ERROR; + fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL; + + if (fdTable[fd].hMapMutex != NULL) + { + CloseHandle(fdTable[fd].hMapMutex); + fdTable[fd].hMapMutex = NULL; + } + } + + LeaveCriticalSection(&fdTableCritical); + + return; +} + +static short getPort(const char * bindPath) +{ + short port = 0; + char * p = strchr(bindPath, ':'); + + if (p && *++p) + { + char buf[6]; + + strncpy(buf, p, 6); + buf[5] = '\0'; + + port = (short) atoi(buf); + } + + return port; +} + +/* + * OS_CreateLocalIpcFd -- + * + * This procedure is responsible for creating the listener pipe + * on Windows NT for local process communication. It will create a + * named pipe and return a file descriptor to it to the caller. + * + * Results: + * Listener pipe created. This call returns either a valid + * pseudo file descriptor or -1 on error. + * + * Side effects: + * Listener pipe and IPC address are stored in the FCGI info + * structure. + * 'errno' will set on errors (-1 is returned). + * + *---------------------------------------------------------------------- + */ +int OS_CreateLocalIpcFd(const char *bindPath, int backlog) +{ + int pseudoFd = -1; + short port = getPort(bindPath); + + if (acceptMutex == INVALID_HANDLE_VALUE) + { + acceptMutex = CreateMutex(NULL, FALSE, NULL); + if (acceptMutex == NULL) return -2; + if (! SetHandleInformation(acceptMutex, HANDLE_FLAG_INHERIT, TRUE)) return -3; + } + + // There's nothing to be gained (at the moment) by a shutdown Event + + if (port && *bindPath != ':' && strncmp(bindPath, LOCALHOST, strlen(LOCALHOST))) + { + fprintf(stderr, "To start a service on a TCP port can not " + "specify a host name.\n" + "You should either use \"localhost:\" or " + " just use \":.\"\n"); + exit(1); + } + + listenType = (port) ? FD_SOCKET_SYNC : FD_PIPE_ASYNC; + + if (port) + { + SOCKET listenSock; + struct sockaddr_in sockAddr; + int sockLen = sizeof(sockAddr); + + memset(&sockAddr, 0, sizeof(sockAddr)); + sockAddr.sin_family = AF_INET; + sockAddr.sin_addr.s_addr = htonl(INADDR_ANY); + sockAddr.sin_port = htons(port); + + listenSock = socket(AF_INET, SOCK_STREAM, 0); + if (listenSock == INVALID_SOCKET) + { + return -4; + } + + if (bind(listenSock, (struct sockaddr *) &sockAddr, sockLen) ) + { + return -12; + } + + if (listen(listenSock, backlog)) + { + return -5; + } + + ASSERT(INT_MIN <= (intptr_t)listenSock && + (intptr_t)listenSock <= INT_MAX); + pseudoFd = Win32NewDescriptor(listenType, (int)(intptr_t)listenSock, -1); + + if (pseudoFd == -1) + { + closesocket(listenSock); + return -6; + } + + hListen = (HANDLE) listenSock; + } + else + { + HANDLE hListenPipe = INVALID_HANDLE_VALUE; + char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1); + + if (! pipePath) + { + return -7; + } + + strcpy(pipePath, bindPathPrefix); + strcat(pipePath, bindPath); + + hListenPipe = CreateNamedPipe(pipePath, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE, + PIPE_UNLIMITED_INSTANCES, + 4096, 4096, 0, NULL); + + free(pipePath); + + if (hListenPipe == INVALID_HANDLE_VALUE) + { + return -8; + } + + if (! SetHandleInformation(hListenPipe, HANDLE_FLAG_INHERIT, TRUE)) + { + return -9; + } + + ASSERT(INT_MIN <= (intptr_t)hListenPipe && + (intptr_t)hListenPipe <= INT_MAX); + pseudoFd = Win32NewDescriptor(listenType, (int)(intptr_t)hListenPipe, -1); + + if (pseudoFd == -1) + { + CloseHandle(hListenPipe); + return -10; + } + + hListen = (HANDLE) hListenPipe; + } + + return pseudoFd; +} + +/* + *---------------------------------------------------------------------- + * + * OS_FcgiConnect -- + * + * Create the pipe pathname connect to the remote application if + * possible. + * + * Results: + * -1 if fail or a valid handle if connection succeeds. + * + * Side effects: + * Remote connection established. + * + *---------------------------------------------------------------------- + */ +int OS_FcgiConnect(char *bindPath) +{ + short port = getPort(bindPath); + int pseudoFd = -1; + + if (port) + { + struct hostent *hp; + char *host = NULL; + struct sockaddr_in sockAddr; + int sockLen = sizeof(sockAddr); + SOCKET sock; + + if (*bindPath != ':') + { + char * p = strchr(bindPath, ':'); + intptr_t len = p - bindPath + 1; + + host = malloc(len); + strncpy(host, bindPath, len); + host[len] = '\0'; + } + + hp = gethostbyname(host ? host : LOCALHOST); + + if (host) + { + free(host); + } + + if (hp == NULL) + { + fprintf(stderr, "Unknown host: %s\n", bindPath); + return -1; + } + + memset(&sockAddr, 0, sizeof(sockAddr)); + sockAddr.sin_family = AF_INET; + memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length); + sockAddr.sin_port = htons(port); + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) + { + return -1; + } + + if (! connect(sock, (struct sockaddr *) &sockAddr, sockLen)) + { + closesocket(sock); + return -1; + } + + ASSERT(INT_MIN <= (intptr_t)sock && (intptr_t)sock <= INT_MAX); + pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, (int)(intptr_t)sock, -1); + if (pseudoFd == -1) + { + closesocket(sock); + return -1; + } + } + else + { + char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1); + HANDLE hPipe; + + if (! pipePath) + { + return -1; + } + + strcpy(pipePath, bindPathPrefix); + strcat(pipePath, bindPath); + + hPipe = CreateFile(pipePath, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + free(pipePath); + + if( hPipe == INVALID_HANDLE_VALUE) + { + return -1; + } + + ASSERT(INT_MIN <= (intptr_t)hPipe && (intptr_t)hPipe <= INT_MAX); + pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int)(intptr_t)hPipe, -1); + + if (pseudoFd == -1) + { + CloseHandle(hPipe); + return -1; + } + + /* + * Set stdin equal to our pseudo FD and create the I/O completion + * port to be used for async I/O. + */ + if (! CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1)) + { + Win32FreeDescriptor(pseudoFd); + CloseHandle(hPipe); + return -1; + } + } + + return pseudoFd; +} + +/* + *-------------------------------------------------------------- + * + * OS_Read -- + * + * Pass through to the appropriate NT read function. + * + * Results: + * Returns number of byes read. Mimics unix read:. + * n bytes read, 0 or -1 failure: errno contains actual error + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ +int OS_Read(int fd, char * buf, size_t len) +{ + DWORD bytesRead; + int ret = -1; + + ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); + + if (shutdownNow) return -1; + + switch (fdTable[fd].type) + { + case FD_FILE_SYNC: + case FD_FILE_ASYNC: + case FD_PIPE_SYNC: + case FD_PIPE_ASYNC: + + ASSERT(0 <= len && len <= ULONG_MAX); + if (ReadFile(fdTable[fd].fid.fileHandle, buf, (DWORD)len, &bytesRead, NULL)) + { + ret = bytesRead; + } + else + { + fdTable[fd].Errno = GetLastError(); + } + + break; + + case FD_SOCKET_SYNC: + case FD_SOCKET_ASYNC: + + ASSERT(0 <= len && len <= INT_MAX); + ret = recv(fdTable[fd].fid.sock, buf, (int)len, 0); + if (ret == SOCKET_ERROR) + { + fdTable[fd].Errno = WSAGetLastError(); + ret = -1; + } + + break; + + default: + + ASSERT(0); + } + + return ret; +} + +/* + *-------------------------------------------------------------- + * + * OS_Write -- + * + * Perform a synchronous OS write. + * + * Results: + * Returns number of bytes written. Mimics unix write: + * n bytes written, 0 or -1 failure (??? couldn't find man page). + * + * Side effects: + * none. + * + *-------------------------------------------------------------- + */ +int OS_Write(int fd, char * buf, size_t len) +{ + DWORD bytesWritten; + int ret = -1; + + ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX); + + if (shutdownNow) return -1; + + switch (fdTable[fd].type) + { + case FD_FILE_SYNC: + case FD_FILE_ASYNC: + case FD_PIPE_SYNC: + case FD_PIPE_ASYNC: + + ASSERT(0 <= len && len <= ULONG_MAX); + if (WriteFile(fdTable[fd].fid.fileHandle, buf, (DWORD)len, &bytesWritten, NULL)) + { + ret = bytesWritten; + } + else + { + fdTable[fd].Errno = GetLastError(); + } + + break; + + case FD_SOCKET_SYNC: + case FD_SOCKET_ASYNC: + + ASSERT(0 <= len && len <= INT_MAX); + ret = send(fdTable[fd].fid.sock, buf, (int)len, 0); + if (ret == SOCKET_ERROR) + { + fdTable[fd].Errno = WSAGetLastError(); + ret = -1; + } + + break; + + default: + + ASSERT(0); + } + + return ret; +} + +/* + *---------------------------------------------------------------------- + * + * OS_SpawnChild -- + * + * Spawns a new server listener process, and stores the information + * relating to the child in the supplied record. A wait handler is + * registered on the child's completion. This involves creating + * a process on NT and preparing a command line with the required + * state (currently a -childproc flag and the server socket to use + * for accepting connections). + * + * Results: + * 0 if success, -1 if error. + * + * Side effects: + * Child process spawned. + * + *---------------------------------------------------------------------- + */ +int OS_SpawnChild(char *execPath, int listenFd) +{ + STARTUPINFO StartupInfo; + PROCESS_INFORMATION pInfo; + BOOL success; + + memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO)); + StartupInfo.cb = sizeof (STARTUPINFO); + StartupInfo.lpReserved = NULL; + StartupInfo.lpReserved2 = NULL; + StartupInfo.cbReserved2 = 0; + StartupInfo.lpDesktop = NULL; + + /* + * FastCGI on NT will set the listener pipe HANDLE in the stdin of + * the new process. The fact that there is a stdin and NULL handles + * for stdout and stderr tells the FastCGI process that this is a + * FastCGI process and not a CGI process. + */ + StartupInfo.dwFlags = STARTF_USESTDHANDLES; + /* + * XXX: Do I have to dup the handle before spawning the process or is + * it sufficient to use the handle as it's reference counted + * by NT anyway? + */ + StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle; + StartupInfo.hStdOutput = INVALID_HANDLE_VALUE; + StartupInfo.hStdError = INVALID_HANDLE_VALUE; + + /* + * Make the listener socket inheritable. + */ + success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT, + TRUE); + if(!success) { + exit(99); + } + + /* + * XXX: Might want to apply some specific security attributes to the + * processes. + */ + success = CreateProcess(execPath, /* LPCSTR address of module name */ + NULL, /* LPCSTR address of command line */ + NULL, /* Process security attributes */ + NULL, /* Thread security attributes */ + TRUE, /* Inheritable Handes inherited. */ + 0, /* DWORD creation flags */ + NULL, /* Use parent environment block */ + NULL, /* Address of current directory name */ + &StartupInfo, /* Address of STARTUPINFO */ + &pInfo); /* Address of PROCESS_INFORMATION */ + if(success) { + return 0; + } else { + return -1; + } +} + +/* + *-------------------------------------------------------------- + * + * OS_AsyncReadStdin -- + * + * This initiates an asynchronous read on the standard + * input handle. This handle is not guaranteed to be + * capable of performing asynchronous I/O so we send a + * message to the StdinThread to do the synchronous read. + * + * Results: + * -1 if error, 0 otherwise. + * + * Side effects: + * Asynchronous message is queued to the StdinThread and an + * overlapped structure is allocated/initialized. + * + *-------------------------------------------------------------- + */ +int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, + ClientData clientData) +{ + POVERLAPPED_REQUEST pOv; + + ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED); + + pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST)); + ASSERT(pOv); + memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST)); + pOv->clientData1 = (ClientData)buf; + pOv->instance = fdTable[STDIN_FILENO].instance; + pOv->procPtr = procPtr; + pOv->clientData = clientData; + + PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO, + (LPOVERLAPPED)pOv); + return 0; +} + +/* + *-------------------------------------------------------------- + * + * OS_AsyncRead -- + * + * This initiates an asynchronous read on the file + * handle which may be a socket or named pipe. + * + * We also must save the ProcPtr and ClientData, so later + * when the io completes, we know who to call. + * + * We don't look at any results here (the ReadFile may + * return data if it is cached) but do all completion + * processing in OS_Select when we get the io completion + * port done notifications. Then we call the callback. + * + * Results: + * -1 if error, 0 otherwise. + * + * Side effects: + * Asynchronous I/O operation is queued for completion. + * + *-------------------------------------------------------------- + */ +int OS_AsyncRead(int fd, int offset, void *buf, int len, + OS_AsyncProc procPtr, ClientData clientData) +{ + DWORD bytesRead; + POVERLAPPED_REQUEST pOv; + + /* + * Catch any bogus fd values + */ + ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); + /* + * Confirm that this is an async fd + */ + ASSERT(fdTable[fd].type != FD_UNUSED); + ASSERT(fdTable[fd].type != FD_FILE_SYNC); + ASSERT(fdTable[fd].type != FD_PIPE_SYNC); + ASSERT(fdTable[fd].type != FD_SOCKET_SYNC); + + pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST)); + ASSERT(pOv); + memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST)); + /* + * Only file offsets should be non-zero, but make sure. + */ + if (fdTable[fd].type == FD_FILE_ASYNC) + if (fdTable[fd].offset >= 0) + pOv->overlapped.Offset = fdTable[fd].offset; + else + pOv->overlapped.Offset = offset; + pOv->instance = fdTable[fd].instance; + pOv->procPtr = procPtr; + pOv->clientData = clientData; + bytesRead = fd; + /* + * ReadFile returns: TRUE success, FALSE failure + */ + if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, + (LPOVERLAPPED)pOv)) { + fdTable[fd].Errno = GetLastError(); + if(fdTable[fd].Errno == ERROR_NO_DATA || + fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) { + PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv); + return 0; + } + if(fdTable[fd].Errno != ERROR_IO_PENDING) { + PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv); + return -1; + } + fdTable[fd].Errno = 0; + } + return 0; +} + +/* + *-------------------------------------------------------------- + * + * OS_AsyncWrite -- + * + * This initiates an asynchronous write on the "fake" file + * descriptor (which may be a file, socket, or named pipe). + * We also must save the ProcPtr and ClientData, so later + * when the io completes, we know who to call. + * + * We don't look at any results here (the WriteFile generally + * completes immediately) but do all completion processing + * in OS_DoIo when we get the io completion port done + * notifications. Then we call the callback. + * + * Results: + * -1 if error, 0 otherwise. + * + * Side effects: + * Asynchronous I/O operation is queued for completion. + * + *-------------------------------------------------------------- + */ +int OS_AsyncWrite(int fd, int offset, void *buf, int len, + OS_AsyncProc procPtr, ClientData clientData) +{ + DWORD bytesWritten; + POVERLAPPED_REQUEST pOv; + + /* + * Catch any bogus fd values + */ + ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); + /* + * Confirm that this is an async fd + */ + ASSERT(fdTable[fd].type != FD_UNUSED); + ASSERT(fdTable[fd].type != FD_FILE_SYNC); + ASSERT(fdTable[fd].type != FD_PIPE_SYNC); + ASSERT(fdTable[fd].type != FD_SOCKET_SYNC); + + pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST)); + ASSERT(pOv); + memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST)); + /* + * Only file offsets should be non-zero, but make sure. + */ + if (fdTable[fd].type == FD_FILE_ASYNC) + /* + * Only file opened via OS_AsyncWrite with + * O_APPEND will have an offset != -1. + */ + if (fdTable[fd].offset >= 0) + /* + * If the descriptor has a memory mapped file + * handle, take the offsets from there. + */ + if (fdTable[fd].hMapMutex != NULL) { + /* + * Wait infinitely; this *should* not cause problems. + */ + WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE); + + /* + * Retrieve the shared offset values. + */ + pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr); + pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr); + + /* + * Update the shared offset values for the next write + */ + *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */ + *(fdTable[fd].offsetLowPtr) += len; + + ReleaseMutex(fdTable[fd].hMapMutex); + } else + pOv->overlapped.Offset = fdTable[fd].offset; + else + pOv->overlapped.Offset = offset; + pOv->instance = fdTable[fd].instance; + pOv->procPtr = procPtr; + pOv->clientData = clientData; + bytesWritten = fd; + /* + * WriteFile returns: TRUE success, FALSE failure + */ + if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, + (LPOVERLAPPED)pOv)) { + fdTable[fd].Errno = GetLastError(); + if(fdTable[fd].Errno != ERROR_IO_PENDING) { + PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv); + return -1; + } + fdTable[fd].Errno = 0; + } + if (fdTable[fd].offset >= 0) + fdTable[fd].offset += len; + return 0; +} + +/* + *-------------------------------------------------------------- + * + * OS_Close -- + * + * Closes the descriptor with routine appropriate for + * descriptor's type. + * + * Results: + * Socket or file is closed. Return values mimic Unix close: + * 0 success, -1 failure + * + * Side effects: + * Entry in fdTable is marked as free. + * + *-------------------------------------------------------------- + */ +int OS_Close(int fd) +{ + int ret = 0; + + /* + * Catch it if fd is a bogus value + */ + ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); + ASSERT(fdTable[fd].type != FD_UNUSED); + + switch (fdTable[fd].type) { + case FD_PIPE_SYNC: + case FD_PIPE_ASYNC: + case FD_FILE_SYNC: + case FD_FILE_ASYNC: + + break; + + case FD_SOCKET_SYNC: + case FD_SOCKET_ASYNC: + + /* + * shutdown() the send side and then read() from client until EOF + * or a timeout expires. This is done to minimize the potential + * that a TCP RST will be sent by our TCP stack in response to + * receipt of additional data from the client. The RST would + * cause the client to discard potentially useful response data. + */ + + if (shutdown(fdTable[fd].fid.sock, SD_SEND) == 0) + { + struct timeval tv; + fd_set rfds; + SOCKET sock = fdTable[fd].fid.sock; + int rv; + char trash[1024]; + + FD_ZERO(&rfds); + + do + { +#pragma warning( disable : 4127 ) + FD_SET(sock, &rfds); +#pragma warning( default : 4127 ) + tv.tv_sec = 2; + tv.tv_usec = 0; + rv = select(0, &rfds, NULL, NULL, &tv); + } + while (rv > 0 && recv(sock, trash, sizeof(trash), 0) > 0); + } + + closesocket(fdTable[fd].fid.sock); + + break; + + default: + + ret = -1; /* fake failure */ + } + + Win32FreeDescriptor(fd); + return ret; +} + +/* + *-------------------------------------------------------------- + * + * OS_CloseRead -- + * + * Cancel outstanding asynchronous reads and prevent subsequent + * reads from completing. + * + * Results: + * Socket or file is shutdown. Return values mimic Unix shutdown: + * 0 success, -1 failure + * + *-------------------------------------------------------------- + */ +int OS_CloseRead(int fd) +{ + int ret = 0; + + /* + * Catch it if fd is a bogus value + */ + ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); + ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC + || fdTable[fd].type == FD_SOCKET_SYNC); + + if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR) + ret = -1; + return ret; +} + +/* + *-------------------------------------------------------------- + * + * OS_DoIo -- + * + * This function was formerly OS_Select. It's purpose is + * to pull I/O completion events off the queue and dispatch + * them to the appropriate place. + * + * Results: + * Returns 0. + * + * Side effects: + * Handlers are called. + * + *-------------------------------------------------------------- + */ +int OS_DoIo(struct timeval *tmo) +{ + ULONG_PTR fd; + unsigned long bytes; + POVERLAPPED_REQUEST pOv; + struct timeb tb; + time_t ms; + time_t ms_last; + int err; + + /* XXX + * We can loop in here, but not too long, as wait handlers + * must run. + * For cgi stdin, apparently select returns when io completion + * ports don't, so don't wait the full timeout. + */ + if(tmo) + ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2; + else + ms = 1000; + ftime(&tb); + ms_last = tb.time*1000 + tb.millitm; + while (ms >= 0) { + if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100) + ms = 100; + ASSERT(0 <= ms && ms < 0xFFFFFFFF); + if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd, + (LPOVERLAPPED *)&pOv, (DWORD)ms) && !pOv) { + err = WSAGetLastError(); + return 0; /* timeout */ + } + + ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); + /* call callback if descriptor still valid */ + ASSERT(pOv); + if(pOv->instance == fdTable[fd].instance) + (*pOv->procPtr)(pOv->clientData, bytes); + free(pOv); + + ftime(&tb); + ms -= (tb.time*1000 + tb.millitm - ms_last); + ms_last = tb.time*1000 + tb.millitm; + } + return 0; +} + +static int isAddrOK(struct sockaddr_in * inet_sockaddr, const char * okAddrs) +{ + static const char *token = " ,;:\t"; + char *ipaddr; + char *p; + + if (okAddrs == NULL) return TRUE; + + ipaddr = inet_ntoa(inet_sockaddr->sin_addr); + p = strstr(okAddrs, ipaddr); + + if (p == NULL) return FALSE; + + if (p == okAddrs) + { + p += strlen(ipaddr); + return (strchr(token, *p) != NULL); + } + + if (strchr(token, *--p) != NULL) + { + p += strlen(ipaddr) + 1; + return (strchr(token, *p) != NULL); + } + + return FALSE; +} + +#ifndef NO_WSAACEPT +static int CALLBACK isAddrOKCallback(LPWSABUF lpCallerId, + LPWSABUF dc0, + LPQOS dc1, + LPQOS dc2, + LPWSABUF dc3, + LPWSABUF dc4, + GROUP *dc5, + DWORD_PTR data) +{ + struct sockaddr_in *sockaddr = (struct sockaddr_in *) lpCallerId->buf; + + // Touch the args to avoid warnings + dc0 = NULL; dc1 = NULL; dc2 = NULL; dc3 = NULL; dc4 = NULL; dc5 = NULL; + + if ((void *) data == NULL) return CF_ACCEPT; + + if (sockaddr->sin_family != AF_INET) return CF_ACCEPT; + + return isAddrOK(sockaddr, (const char *) data) ? CF_ACCEPT : CF_REJECT; +} +#endif + +static void printLastError(const char * text) +{ + LPVOID buf; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, + (LPTSTR) &buf, + 0, + NULL + ); + + fprintf(stderr, "%s: %s\n", text, (LPCTSTR) buf); + LocalFree(buf); +} + +static int acceptNamedPipe() +{ + int ipcFd = -1; + + if (! ConnectNamedPipe(hListen, NULL)) + { + switch (GetLastError()) + { + case ERROR_PIPE_CONNECTED: + + // A client connected after CreateNamedPipe but + // before ConnectNamedPipe. Its a good connection. + + break; + + case ERROR_IO_PENDING: + + // The NamedPipe was opened with an Overlapped structure + // and there is a pending io operation. mod_fastcgi + // did this in 2.2.12 (fcgi_pm.c v1.52). + + case ERROR_PIPE_LISTENING: + + // The pipe handle is in nonblocking mode. + + case ERROR_NO_DATA: + + // The previous client closed its handle (and we failed + // to call DisconnectNamedPipe) + + default: + + printLastError("unexpected ConnectNamedPipe() error"); + } + } + + ASSERT(INT_MIN <= (intptr_t)hListen && (intptr_t)hListen <= INT_MAX); + ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)(intptr_t)hListen, -1); + if (ipcFd == -1) + { + DisconnectNamedPipe(hListen); + } + + return ipcFd; +} + +static int acceptSocket(const char *webServerAddrs) +{ + SOCKET hSock; + int ipcFd = -1; + + for (;;) + { + struct sockaddr sockaddr; + int sockaddrLen = sizeof(sockaddr); + + for (;;) + { + const struct timeval timeout = {1, 0}; + fd_set readfds; + + FD_ZERO(&readfds); + +#pragma warning( disable : 4127 ) + FD_SET((SOCKET)hListen, &readfds); +#pragma warning( default : 4127 ) + + if (select(0, &readfds, NULL, NULL, &timeout) == 0) + { + if (shutdownPending) + { + OS_LibShutdown(); + return -1; + } + } + else + { + break; + } + } + +#if NO_WSAACEPT + hSock = accept((SOCKET) hListen, &sockaddr, &sockaddrLen); + + if (hSock == INVALID_SOCKET) + { + break; + } + + if (isAddrOK((struct sockaddr_in *) &sockaddr, webServerAddrs)) + { + break; + } + + closesocket(hSock); +#else + hSock = WSAAccept((SOCKET)hListen, + &sockaddr, + &sockaddrLen, + isAddrOKCallback, + (DWORD_PTR) webServerAddrs); + + if (hSock != INVALID_SOCKET) + { + break; + } + + if (WSAGetLastError() != WSAECONNREFUSED) + { + break; + } +#endif + } + + if (hSock == INVALID_SOCKET) + { + /* Use FormatMessage() */ + fprintf(stderr, "accept()/WSAAccept() failed: %d", WSAGetLastError()); + return -1; + } + + ASSERT(INT_MIN <= (intptr_t)hSock && (intptr_t)hSock <= INT_MAX); + ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, (int)(intptr_t)hSock, -1); + if (ipcFd == -1) + { + closesocket(hSock); + } + + return ipcFd; +} + +/* + *---------------------------------------------------------------------- + * + * OS_Accept -- + * + * Accepts a new FastCGI connection. This routine knows whether + * we're dealing with TCP based sockets or NT Named Pipes for IPC. + * + * fail_on_intr is ignored in the Win lib. + * + * Results: + * -1 if the operation fails, otherwise this is a valid IPC fd. + * + *---------------------------------------------------------------------- + */ +int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs) +{ + int ipcFd = -1; + + // Touch args to prevent warnings + listen_sock = 0; fail_on_intr = 0; + + // @todo Muliple listen sockets and sockets other than 0 are not + // supported due to the use of globals. + + if (shutdownPending) + { + OS_LibShutdown(); + return -1; + } + + // The mutex is to keep other processes (and threads, when supported) + // from going into the accept cycle. The accept cycle needs to + // periodically break out to check the state of the shutdown flag + // and there's no point to having more than one thread do that. + + if (acceptMutex != INVALID_HANDLE_VALUE) + { + if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED) + { + printLastError("WaitForSingleObject() failed"); + return -1; + } + } + + if (shutdownPending) + { + OS_LibShutdown(); + } + else if (listenType == FD_PIPE_SYNC) + { + ipcFd = acceptNamedPipe(); + } + else if (listenType == FD_SOCKET_SYNC) + { + ipcFd = acceptSocket(webServerAddrs); + } + else + { + fprintf(stderr, "unknown listenType (%d)\n", listenType); + } + + if (acceptMutex != INVALID_HANDLE_VALUE) + { + ReleaseMutex(acceptMutex); + } + + return ipcFd; +} + +/* + *---------------------------------------------------------------------- + * + * OS_IpcClose + * + * OS IPC routine to close an IPC connection. + * + * Results: + * + * + * Side effects: + * IPC connection is closed. + * + *---------------------------------------------------------------------- + */ +int OS_IpcClose(int ipcFd) +{ + if (ipcFd == -1) return 0; + + /* + * Catch it if fd is a bogus value + */ + ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX)); + ASSERT(fdTable[ipcFd].type != FD_UNUSED); + + switch (listenType) + { + case FD_PIPE_SYNC: + /* + * Make sure that the client (ie. a Web Server in this case) has + * read all data from the pipe before we disconnect. + */ + if (! FlushFileBuffers(fdTable[ipcFd].fid.fileHandle)) return -1; + + if (! DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) return -1; + + /* fall through */ + + case FD_SOCKET_SYNC: + + OS_Close(ipcFd); + break; + + case FD_UNUSED: + default: + + exit(106); + break; + } + + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * OS_IsFcgi -- + * + * Determines whether this process is a FastCGI process or not. + * + * Results: + * Returns 1 if FastCGI, 0 if not. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +int OS_IsFcgi(int sock) +{ + // Touch args to prevent warnings + sock = 0; + + /* XXX This is broken for sock */ + + return (listenType != FD_UNUSED); +} + +/* + *---------------------------------------------------------------------- + * + * OS_SetFlags -- + * + * Sets selected flag bits in an open file descriptor. Currently + * this is only to put a SOCKET into non-blocking mode. + * + *---------------------------------------------------------------------- + */ +void OS_SetFlags(int fd, int flags) +{ + unsigned long pLong = 1L; + int err; + + if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) { + if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) == + SOCKET_ERROR) { + exit(WSAGetLastError()); + } + if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock, + hIoCompPort, fd, 1)) { + err = GetLastError(); + exit(err); + } + + fdTable[fd].type = FD_SOCKET_ASYNC; + } + return; +} + diff --git a/library/server/libfcgi/spec/lib/win64/msc/libfcgi.dll b/library/server/libfcgi/spec/lib/win64/msc/libfcgi.dll index 6b18c88f16aa50ecc0b936ff819559d6fb26cf09..1d58f296d86fb01579041dec1cc1b06b71609eb0 100644 GIT binary patch delta 20349 zcmeHvdt8*&_V=C#hC2?5k6Z@^91&3z0To3Q6!lR-Nm0owCSJ2bgET7#Ma=0DMYffh z7Iv)T?U-dH+HFb`QjeK+%CZu>oHL{rR>v&M=l!nd*<+)p-|v0@eE&G}Y0Y=-wbx#I z@3r?{`!ZNnoln(IQe9Q4|Hp-WhQ88yj)zLRzd2_F(x#e?!M{^JmQ?n;Tj>+0D#$&#^H~eA-x|Em#U-Y#szl4WUxb zqfK3d!c2_$>JmQMl$zSMm$A}zjGcOtu`*Fwhc?EJ1b7Qftc+ilGWjPkvUm=1!a(zQ zkQCRhCUzy1-F2yZCe|x}9zb78#nv_VT?Dxxq1p#z=t1{U1pwrh1>` zdf$lNFG5g}R-IEyOl4QMIgK$zH4my_Y*@zG0;hHDXN&eXXXaxW75c zbPw-o?lE@Ay-;lOfo*o7lcJ6eP-@<-u((xtOlcab`$xU1)S8!_g&_M(MFRNcf|tRs z6nvnf9@pfr`A&0u;%kectD2M=S4EJbHY-|}UAknXZrt4ZVJQFBoNQXa!~MF2xdV@djXAjx1h_QoKG)|MR@N*E{{)gZwxA@0qcJl~O&DOhw zLkxceQeT3}4aobY{D!6bbW3mjAsEir`^OJm2Z5q`8d;9Mld}y63OB2v-9CX5%=*LR zG`&D*oDi8?2lC7QR!fpD9*53iBw87_1|(R%yN9ttH8-FTJ~6kZ@xp+lc;^|zRDdSG zf$R(IF|H2@Sts|Cygr~{=x}5xs>fD&;GeZg4%7>s*4rfO1?1}=lWnShAWsOiTE4j3 z+fFVo42-_*S)r4l$-9tU<64@JIkXWbxqf+D-W@)3maXJ~=?wxXVbSNG^@q0fqp zU?ZdTp;*2msHbTf-xri*dWC-+q?jJn`PFm3sApzf%1`0IN*Ja&dlu46%%A1y9RpgW!#2^dsLK8t3rY ziBYIr>fVB7uGEIsT+y^9^MxIHOkqxQD>(P|BFu=K&%v^aQceD39%C726{k6X>^ZH0 z08Ks&sZ#AqgTRoRe>qL^-6VHfeMz_4Rf=MlApu$~-{4)4W!Qav@r3h@85}97Jw*TJJs0cejgmOduhqBc}Q&xQ3>>JW|NA z+bB6xZZ9(xr)J6zBs2?fnRriK5JSyyrbfA#`7?vj06z=cR{zsEkH=0L> z50HvS^NHch>O1b|A9Rf8L*%5m4`)$5hXa)C8DWbrp~;&37KzmNG`VpWZ|f4x8)RSJ zvC{}?=gj@nI%S!p&dUDBBjQYw&j@}XazXlEO7#}xsUPZ%xawYZRnzjak63EzFgr%h zx`^hXX|)BtOhRCBrP&R5b!e`pgF$Z3ZhA;xmaIW�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 X5WrRs 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 Y-qyIy^1SW&qUZPB-}JbFH5&r{7d?qB)Bpeg diff --git a/library/server/libfcgi/spec/lib/win64/msc/libfcgi.lib b/library/server/libfcgi/spec/lib/win64/msc/libfcgi.lib index 839cda3f7123e43265e605d0e130ff7e675b21b9..8ab594656f95a20e3d40aa10fe9bdbd24cde6c11 100644 GIT binary patch delta 3319 zcma)8T}+c#7=Byp6bFK|VkuHur_O*fDJ_)$wql|Dl$QSr(iRG|1p)B~H`#`X7mLdr zaBhfk#$`*k%mraw$py=nT^O^AZ5NAKykN;LxLD?vctMsKcfR&~@A*FYNaTWx=REK8 zKJRmWzOFr1tvy!F8g^NV?DjHS$+9`DCYN{IBs1z~Wa@~G7_8YVt(tFllY2$EE9+S~ z>Xm(I-=rA!yk(OA6%{juIdk?(VYWp@HVpM7WcZ-$DFBSa1?YuQI0qwe1+Kyv48l9` zF1!uXFal8?VrBB`r67zZe)X`}L&4lCCk0Kk^c9PAEB5Na8RZAr*E4S<>lQ^Y|sFi$*{eAp1yFi?)usWu@QYf$Z>aOGRcKn zH^&c<|7vxK6e3jfARS0$3XmV<=Xm+FHbKd!lw3+#O3x#IoyMS;dV*3wDMm`!yVpc1 zspP&(%bV#5np<K4`=#4j5*^eN9K1T__v22A#;Q=`x_KAe{A5I*vQ(*!D~TQe!KA`HbSPli8B$5 zOTc|GR{o0w#4*d8xg}(N-mIXR>g6CZFG@hveBxEmbhjv!FDijqKF&nV_k0SP8%j){ zUxB&m=bD((p;iuJqZ_Tf9Fu?6s*wDdHeOCOGlf@5PtRI4yC?kRl>w_N4G-x zSKSKbnE}oezSJjz?D9?rN-!zmk-jj<7B&KQTtI7`XSgp5@I@_l^`4Uzgo|3d~6fh+d!cNxVuEs||7o=yxe7_xnQ3 zH!H+r#FKbEw2k(kA+C+nb%(iUZ1{bcH$>0J`nfJXle7I>pUL?F{z}rPGx3Eo(<>Aa zzEJ*?`^m=x_=_j;%kJqgI~IQoNF0iX(-%>2xEkROaTe1cXUdM-jX|ChZsDec{m+Q_ zm=7uFJ{*$k=7@eR!+d(-AU%(1wZem;qtlMjXyoln0Gl z@>`S}!>xA2I8*44L4FzIy3APeMbgC!w_dy}aV~>9yB?R!hI=u>lfcYYN4PZvO{2#D E0g%G1-2eap delta 3335 zcma)8TTE0}6x}mo1quZjkPFDjv?@jvVHnOkPG(!t|cfULN2F$}LI1LMM24>)On1!=&4vxb#oPalB z3Qoctn80ki?E~Khlrj_a$tH zx)zjCSFvRYca*ChKQAkxwjm4Fa$fM2tLB%=IV1rpr@LPmh!d$VTT}3Q1$jg&I#&@= z6pZ^ZgYq1BDpfz@l~v?trBZFXQQ5f}oT?&FJzd34sjwpnJ6RUH7`7<9U1iZ~W!iML zn)ln){ftZaiAyygIqMit!b7gOpy4Y{7vqWepEF)*=NIjEi*}~89{eVBwpWg-lIusE zT&hUEA`el9D7A_qDxXJFY%2bF^f|LSQ)@0VrHt5THL7%3O_L_AEhx5fgtkrC+DJQ~ zzfI`tQzLcN+NsdyQNdfa4g!fOxtZy4tIS1r5i!5gz`xu)GB&@;9I7+W{H~5Qsga|e z!3$os{91h$H6o_Dftdsbbl}bjwfrX?NZTxGWJ|>SxY0l}s)<2jp3;G&`Jl-_)75NH ze#;2l)xu2DJl|rV`Ot{j;xS;}^ROnR^lB@E)aX(xms9fpv>GJe+s5TWGftjkNz7WA zm}6IUjoUkmu?>6a#3-|a?ICr+D|J~1l9zwm4ZQd}47_~XVW4U6WRNtkcCsdcVO-Mo{YWTH}8V5$ zWZW)|a87iDPju|RC$-0X)IfKAG*mZ5^J^dD-OC$!I$^QM4@SJ$?X=i!bDP&Wh)1na8Cf-h2hAanD@I}>0TI@yhYZf|rJ Rlbi%)c5jlc5tue*{U6sjX6XO`