// distribution boxbackup-0.11rc1 (svn version: 2023_2024) // // Copyright (c) 2003 - 2006 // Ben Summers and contributors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // 3. All use of this software and associated advertising materials must // display the following acknowledgment: // This product includes software developed by Ben Summers. // 4. The names of the Authors may not be used to endorse or promote // products derived from this software without specific prior written // permission. // // [Where legally impermissible the Authors do not disclaim liability for // direct physical injury or death caused solely by defects in the software // unless it is modified by a third party.] // // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // // // -------------------------------------------------------------------------- // // File // Name: ServerStream.h // Purpose: Stream based server daemons // Created: 2003/07/31 // // -------------------------------------------------------------------------- #ifndef SERVERSTREAM__H #define SERVERSTREAM__H #include #include #ifndef WIN32 #include #endif #include "Daemon.h" #include "SocketListen.h" #include "Utils.h" #include "Configuration.h" #include "WaitForEvent.h" #include "MemLeakFindOn.h" // -------------------------------------------------------------------------- // // Class // Name: ServerStream // Purpose: Stream based server daemon // Created: 2003/07/31 // // -------------------------------------------------------------------------- template class ServerStream : public Daemon { public: ServerStream() { } ~ServerStream() { DeleteSockets(); } private: ServerStream(const ServerStream &rToCopy) { } public: virtual const char *DaemonName() const { return "generic-stream-server"; } virtual void OnIdle() { } virtual void Run() { // Set process title as appropriate SetProcessTitle(ForkToHandleRequests?"server":"idle"); // Handle exceptions and child task quitting gracefully. bool childExit = false; try { Run2(childExit); } catch(BoxException &e) { if(childExit) { BOX_ERROR("Error in child process, " "terminating connection: exception " << e.what() << "(" << e.GetType() << "/" << e.GetSubType() << ")"); _exit(1); } else throw; } catch(std::exception &e) { if(childExit) { BOX_ERROR("Error in child process, " "terminating connection: exception " << e.what()); _exit(1); } else throw; } catch(...) { if(childExit) { BOX_ERROR("Error in child process, " "terminating connection: " "unknown exception"); _exit(1); } else throw; } // if it's a child fork, exit the process now if(childExit) { // Child task, dump leaks to trace, which we make sure is on #ifdef BOX_MEMORY_LEAK_TESTING #ifndef NDEBUG TRACE_TO_SYSLOG(true); TRACE_TO_STDOUT(true); #endif memleakfinder_traceblocksinsection(); #endif // If this is a child quitting, exit now to stop bad things happening _exit(0); } } virtual void Run2(bool &rChildExit) { try { // Wait object with a timeout of 10 seconds, which is a reasonable time to wait before // cleaning up finished child processes. WaitForEvent connectionWait(10000); // BLOCK { // Get the address we need to bind to // this-> in next line required to build under some gcc versions const Configuration &config(this->GetConfiguration()); const Configuration &server(config.GetSubConfiguration("Server")); std::string addrs = server.GetKeyValue("ListenAddresses"); // split up the list of addresses std::vector addrlist; SplitString(addrs, ',', addrlist); for(unsigned int a = 0; a < addrlist.size(); ++a) { // split the address up into components std::vector c; SplitString(addrlist[a], ':', c); // listen! SocketListen *psocket = new SocketListen; try { if(c[0] == "inet") { // Check arguments if(c.size() != 2 && c.size() != 3) { THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs) } // Which port? int port = Port; if(c.size() == 3) { // Convert to number port = ::atol(c[2].c_str()); if(port <= 0 || port > ((64*1024)-1)) { THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs) } } // Listen psocket->Listen(Socket::TypeINET, c[1].c_str(), port); } else if(c[0] == "unix") { #ifdef WIN32 BOX_WARNING("Ignoring request to listen on a Unix socket on Windows: " << addrlist[a]); delete psocket; psocket = NULL; #else // Check arguments size if(c.size() != 2) { THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs) } // unlink anything there ::unlink(c[1].c_str()); psocket->Listen(Socket::TypeUNIX, c[1].c_str()); #endif // WIN32 } else { delete psocket; THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs) } if (psocket != NULL) { // Add to list of sockets mSockets.push_back(psocket); } } catch(...) { delete psocket; throw; } if (psocket != NULL) { // Add to the list of things to wait on connectionWait.Add(psocket); } } } while(!StopRun()) { // Wait for a connection, or timeout SocketListen *psocket = (SocketListen *)connectionWait.Wait(); if(psocket) { // Get the incoming connection // (with zero wait time) std::string logMessage; std::auto_ptr connection(psocket->Accept(0, &logMessage)); // Was there one (there should be...) if(connection.get()) { // Since this is a template parameter, the if() will be optimised out by the compiler #ifndef WIN32 // no fork on Win32 if(ForkToHandleRequests && !IsSingleProcess()) { pid_t pid = ::fork(); switch(pid) { case -1: // Error! THROW_EXCEPTION(ServerException, ServerForkError) break; case 0: // Child process rChildExit = true; // Close listening sockets DeleteSockets(); // Set up daemon EnterChild(); SetProcessTitle("transaction"); // Memory leak test the forked process #ifdef BOX_MEMORY_LEAK_TESTING memleakfinder_startsectionmonitor(); #endif // The derived class does some server magic with the connection HandleConnection(*connection); // Since rChildExit == true, the forked process will call _exit() on return from this fn return; default: // parent daemon process break; } // Log it BOX_WARNING("Message from child process " << pid << ": " << logMessage); } else { #endif // !WIN32 // Just handle in this process SetProcessTitle("handling"); HandleConnection(*connection); SetProcessTitle("idle"); #ifndef WIN32 } #endif // !WIN32 } } OnIdle(); #ifndef WIN32 // Clean up child processes (if forking daemon) if(ForkToHandleRequests && !IsSingleProcess()) { int status = 0; int p = 0; do { if((p = ::waitpid(0 /* any child in process group */, &status, WNOHANG)) == -1 && errno != ECHILD && errno != EINTR) { THROW_EXCEPTION(ServerException, ServerWaitOnChildError) } } while(p > 0); } #endif // !WIN32 } } catch(...) { DeleteSockets(); throw; } // Delete the sockets DeleteSockets(); } virtual void HandleConnection(StreamType &rStream) { Connection(rStream); } virtual void Connection(StreamType &rStream) = 0; protected: // For checking code in derived classes -- use if you have an algorithm which // depends on the forking model in case someone changes it later. bool WillForkToHandleRequests() { #ifdef WIN32 return false; #else return ForkToHandleRequests && !IsSingleProcess(); #endif // WIN32 } private: // -------------------------------------------------------------------------- // // Function // Name: ServerStream::DeleteSockets() // Purpose: Delete sockets // Created: 9/3/04 // // -------------------------------------------------------------------------- void DeleteSockets() { for(unsigned int l = 0; l < mSockets.size(); ++l) { if(mSockets[l]) { mSockets[l]->Close(); delete mSockets[l]; } mSockets[l] = 0; } mSockets.clear(); } private: std::vector *> mSockets; }; #define SERVERSTREAM_VERIFY_SERVER_KEYS(DEFAULT_ADDRESSES) \ {"ListenAddresses", DEFAULT_ADDRESSES, 0, 0}, \ DAEMON_VERIFY_SERVER_KEYS #include "MemLeakFindOff.h" #endif // SERVERSTREAM__H