From a638d685682b5f444911bdf2b65992da1b0f23ae Mon Sep 17 00:00:00 2001 From: Louis Rannou Date: Tue, 5 May 2020 18:56:28 +0200 Subject: [PATCH] Initialize the library on the specified socket instead of stdin Initialization of the library could not be done without creating a request on the standard input. This can be a problem if the program has disabled the standard input (a linux daemon for example). Then this commit enables to initialize the library on a specified socket without using the standard input. --- Makefile.am | 1 + examples/.gitignore | 1 + examples/Makefile.am | 2 + examples/echo-x-socket.c | 85 ++++++++++++++++ examples/echoxsocket.mak | 203 +++++++++++++++++++++++++++++++++++++++ include/fcgiapp.h | 42 ++++++++ include/fcgios.h | 1 + libfcgi/fcgiapp.c | 72 ++++++++++++++ libfcgi/os_unix.c | 15 +++ 9 files changed, 422 insertions(+) create mode 100644 examples/echo-x-socket.c create mode 100644 examples/echoxsocket.mak diff --git a/Makefile.am b/Makefile.am index 34716e4..f82708c 100755 --- a/Makefile.am +++ b/Makefile.am @@ -34,6 +34,7 @@ EXTRA_DIST = LICENSE.TERMS \ examples/authorizer.mak \ examples/echo.mak \ examples/echox.mak \ + examples/echoxsocket.mak \ examples/size.mak \ examples/echo-cpp.mak \ libfcgi/libfcgi.mak \ diff --git a/examples/.gitignore b/examples/.gitignore index 3da09b1..0f6e1d4 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -2,6 +2,7 @@ authorizer size echo echo-x +echo-x-socket log-dump threaded echo-cpp diff --git a/examples/Makefile.am b/examples/Makefile.am index ff71519..546d74d 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -3,6 +3,7 @@ noinst_PROGRAMS = authorizer \ echo \ echo-x \ + echo-x-socket \ log-dump \ size \ @THREADED@ \ @@ -27,6 +28,7 @@ LDADD = $(LIBFCGI) echo_SOURCES = $(INCLUDE_FILES) echo.c echo_x_SOURCES = $(INCLUDE_FILES) echo-x.c +echo_x_socket_SOURCES = $(INCLUDE_FILES) echo-x-socket.c log_dump_SOURCES = $(INCLUDE_FILES) log-dump.c authorizer_SOURCES = $(INCLUDE_FILES) authorizer.c diff --git a/examples/echo-x-socket.c b/examples/echo-x-socket.c new file mode 100644 index 0000000..2678c10 --- /dev/null +++ b/examples/echo-x-socket.c @@ -0,0 +1,85 @@ +/* + * echo2.c -- + * + * Produce a page containing all the inputs (fcgiapp version) + * + * + * 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. + * + */ + +#include "fcgi_config.h" + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef _WIN32 +#include +#else +extern char **environ; +#endif + +#include "fcgiapp.h" + +static void PrintEnv(FCGX_Stream *out, char *label, char **envp) +{ + FCGX_FPrintF(out, "%s:
\n
\n", label);
+    for( ; *envp != NULL; envp++) {
+        FCGX_FPrintF(out, "%s\n", *envp);
+    }
+    FCGX_FPrintF(out, "

\n"); +} + +int main () +{ + FCGX_Stream *in, *out, *err; + FCGX_ParamArray envp; + int count = 0; + + if (0 != FCGX_InitS("127.0.0.1:1235", 1)) + return -1; + + while (FCGX_Accept(&in, &out, &err, &envp) >= 0) { + char *contentLength = FCGX_GetParam("CONTENT_LENGTH", envp); + int len = 0; + + FCGX_FPrintF(out, + "Content-type: text/html\r\n" + "\r\n" + "FastCGI echo (fcgiapp version)" + "

FastCGI echo (fcgiapp version)

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

\n", ++count, getpid()); + + if (contentLength != NULL) + len = strtol(contentLength, NULL, 10); + + if (len <= 0) { + FCGX_FPrintF(out, "No data from standard input.

\n"); + } + else { + int i, ch; + + FCGX_FPrintF(out, "Standard input:
\n

\n");
+            for (i = 0; i < len; i++) {
+                if ((ch = FCGX_GetChar(in)) < 0) {
+                    FCGX_FPrintF(out,
+                        "Error: Not enough bytes received on standard input

\n"); + break; + } + FCGX_PutChar(ch, out); + } + FCGX_FPrintF(out, "\n

\n"); + } + + PrintEnv(out, "Request environment", envp); + PrintEnv(out, "Initial environment", environ); + } /* while */ + + return 0; +} diff --git a/examples/echoxsocket.mak b/examples/echoxsocket.mak new file mode 100644 index 0000000..ef983fa --- /dev/null +++ b/examples/echoxsocket.mak @@ -0,0 +1,203 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on echoxsocket.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 "echoxsocket.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=.\..\examples\echo-x-socket\Release +INTDIR=.\..\examples\echo-x-socket\Release +# Begin Custom Macros +OutDir=.\..\examples\echo-x-socket\Release +# End Custom Macros + +ALL : "$(OUTDIR)\echo-x-socket.exe" + +CLEAN : + -@erase "$(INTDIR)\echo-x-socket.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\echo-x-socket.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Gi /O2 /Ob2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\echoxsocket.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)\echoxsocket.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=libfcgi.lib /nologo /pdb:none /machine:IX86 /out:"$(OUTDIR)\echo-x-socket.exe" /libpath:"..\libfcgi\Release" +LINK32_OBJS= \ + "$(INTDIR)\echo-x-socket.obj" \ + "..\libfcgi\Release\libfcgi.lib" + +"$(OUTDIR)\echo-x-socket.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "debug" + +OUTDIR=.\../examples/echo-x-socket\Debug +INTDIR=.\../examples/echo-x-socket\Debug +# Begin Custom Macros +OutDir=.\../examples/echo-x-socket\Debug +# End Custom Macros + +ALL : "$(OUTDIR)\echo-x-socket.exe" "$(OUTDIR)\echoxsocket.bsc" + +CLEAN : + -@erase "$(INTDIR)\echo-x-socket.obj" + -@erase "$(INTDIR)\echo-x-socket.sbr" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(OUTDIR)\echo-x-socket.exe" + -@erase "$(OUTDIR)\echoxsocket.bsc" + +"$(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)\echoxsocket.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)\echoxsocket.bsc" +BSC32_SBRS= \ + "$(INTDIR)\echo-x-socket.sbr" + +"$(OUTDIR)\echoxsocket.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +LINK32_FLAGS=libfcgi.lib /nologo /profile /debug /machine:IX86 /out:"$(OUTDIR)\echo-x-socket.exe" /libpath:"..\libfcgi\Debug" +LINK32_OBJS= \ + "$(INTDIR)\echo-x-socket.obj" \ + "..\libfcgi\Debug\libfcgi.lib" + +"$(OUTDIR)\echo-x-socket.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + + +"..\examples\echo-x-socket.c" : \ + "..\include\fcgi_config.h"\ + "..\include\fcgiapp.h"\ + + +!IF "$(CFG)" == "release" || "$(CFG)" == "debug" +SOURCE="..\examples\echo-x-socket.c" + +!IF "$(CFG)" == "release" + + +"$(INTDIR)\echo-x-socket.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "debug" + + +"$(INTDIR)\echo-x-socket.obj" "$(INTDIR)\echo-x-socket.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +!ENDIF + diff --git a/include/fcgiapp.h b/include/fcgiapp.h index 8cadde1..4cb0c67 100644 --- a/include/fcgiapp.h +++ b/include/fcgiapp.h @@ -142,6 +142,26 @@ DLLAPI int FCGX_IsCGI(void); */ DLLAPI int FCGX_Init(void); +/* + *---------------------------------------------------------------------- + * + * FCGX_InitS -- + * + * Initialize the FCGX library whith creation of a FastCGI + * socket. Call in multi-threaded apps before calling + * FCGX_Accept_r(). + * + * 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 0 upon success. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_InitS(const char * socket_path, int backlog); + /* *---------------------------------------------------------------------- * @@ -294,6 +314,28 @@ DLLAPI int FCGX_Accept( */ DLLAPI void FCGX_Finish(void); +/* + *---------------------------------------------------------------------- + * + * FCGX_Finish -- + * + * Finish the current request (NOT multi-thread safe) and close + * the FastCGI socket. + * + * 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_FinishS(void); + /* *---------------------------------------------------------------------- * diff --git a/include/fcgios.h b/include/fcgios.h index 2069731..7274892 100755 --- a/include/fcgios.h +++ b/include/fcgios.h @@ -103,6 +103,7 @@ 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 void OS_CloseLocalIpcFd(int listenSock); 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); diff --git a/libfcgi/fcgiapp.c b/libfcgi/fcgiapp.c index e978b7c..0b42228 100644 --- a/libfcgi/fcgiapp.c +++ b/libfcgi/fcgiapp.c @@ -1980,6 +1980,33 @@ int FCGX_IsCGI(void) return !isFastCGI; } +/* + *---------------------------------------------------------------------- + * + * FCGX_FinishS -- + * + * Finishes the current request from the HTTP server and close + * the FastCGI socket. + * + * 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_FinishS(void) +{ + OS_CloseLocalIpcFd(the_request.listen_sock); + FCGX_Finish_r(&the_request); +} + /* *---------------------------------------------------------------------- * @@ -2121,6 +2148,51 @@ int FCGX_Init(void) return 0; } +/* + *---------------------------------------------------------------------- + * + * FCGX_InitS -- + * + * Initialize the FCGX library with creation of a FastCGI socket. + * This is called by FCGX_Accept() but must be called by the user + * when using FCGX_Accept_r(). + * + * 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. + * + * Results: + * 0 for successful call. + * + *---------------------------------------------------------------------- + */ +int FCGX_InitS(const char * socket_path, int backlog) +{ + char *p; + + if (libInitialized) { + return 0; + } + + int socketfd = FCGX_OpenSocket(socket_path, backlog); + if (socketfd == -1) { + return -1; + } + + FCGX_InitRequest(&the_request, socketfd, 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; +} + /* *---------------------------------------------------------------------- * diff --git a/libfcgi/os_unix.c b/libfcgi/os_unix.c index 171c70e..71861b8 100755 --- a/libfcgi/os_unix.c +++ b/libfcgi/os_unix.c @@ -389,6 +389,21 @@ int OS_CreateLocalIpcFd(const char *bindPath, int backlog) return listenSock; } +/* + * OS_CloseLocalIpcFd -- + * + * Close the listener socket opened with OS_CreateLocalIpcFd + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +void OS_CloseLocalIpcFd(int listenSock) +{ + close(listenSock); +} + /* *---------------------------------------------------------------------- *