source: box/trunk/lib/server/ServerStream.h @ 3049

Revision 3049, 9.6 KB checked in by chris, 5 months ago (diff)

Add remote host and port to post-login login message, requested by Pete Jalajas.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    ServerStream.h
5//              Purpose: Stream based server daemons
6//              Created: 2003/07/31
7//
8// --------------------------------------------------------------------------
9
10#ifndef SERVERSTREAM__H
11#define SERVERSTREAM__H
12
13#include <stdlib.h>
14#include <errno.h>
15
16#ifndef WIN32
17        #include <sys/wait.h>
18#endif
19
20#include "Daemon.h"
21#include "SocketListen.h"
22#include "Utils.h"
23#include "Configuration.h"
24#include "WaitForEvent.h"
25
26#include "MemLeakFindOn.h"
27
28// --------------------------------------------------------------------------
29//
30// Class
31//              Name:    ServerStream
32//              Purpose: Stream based server daemon
33//              Created: 2003/07/31
34//
35// --------------------------------------------------------------------------
36template<typename StreamType, int Port, int ListenBacklog = 128, bool ForkToHandleRequests = true>
37class ServerStream : public Daemon
38{
39public:
40        ServerStream()
41        {
42        }
43        ~ServerStream()
44        {
45                DeleteSockets();
46        }
47private:
48        ServerStream(const ServerStream &rToCopy)
49        {
50        }
51
52        std::string mConnectionDetails;
53
54protected:
55        const std::string& GetConnectionDetails()
56        {
57                return mConnectionDetails;
58        }
59
60public:
61
62        virtual const char *DaemonName() const
63        {
64                return "generic-stream-server";
65        }
66
67        virtual void OnIdle() { }
68
69        virtual void Run()
70        {
71                // Set process title as appropriate
72                SetProcessTitle(ForkToHandleRequests?"server":"idle");
73       
74                // Handle exceptions and child task quitting gracefully.
75                bool childExit = false;
76                try
77                {
78                        Run2(childExit);
79                }
80                catch(BoxException &e)
81                {
82                        if(childExit)
83                        {
84                                BOX_ERROR("Error in child process, "
85                                        "terminating connection: exception " <<
86                                        e.what() << "(" << e.GetType() <<
87                                        "/" << e.GetSubType() << ")");
88                                _exit(1);
89                        }
90                        else throw;
91                }
92                catch(std::exception &e)
93                {
94                        if(childExit)
95                        {
96                                BOX_ERROR("Error in child process, "
97                                        "terminating connection: exception " <<
98                                        e.what());
99                                _exit(1);
100                        }
101                        else throw;
102                }
103                catch(...)
104                {
105                        if(childExit)
106                        {
107                                BOX_ERROR("Error in child process, "
108                                        "terminating connection: "
109                                        "unknown exception");
110                                _exit(1);
111                        }
112                        else throw;
113                }
114
115                // if it's a child fork, exit the process now
116                if(childExit)
117                {
118                        // Child task, dump leaks to trace, which we make sure is on
119                        #ifdef BOX_MEMORY_LEAK_TESTING
120                                #ifndef BOX_RELEASE_BUILD
121                                        TRACE_TO_SYSLOG(true);
122                                        TRACE_TO_STDOUT(true);
123                                #endif
124                                memleakfinder_traceblocksinsection();
125                        #endif
126
127                        // If this is a child quitting, exit now to stop bad things happening
128                        _exit(0);
129                }
130        }
131       
132protected:
133        virtual void NotifyListenerIsReady() { }
134        virtual void LogConnectionDetails(std::string details)
135        {
136                BOX_NOTICE("Handling incoming connection from " << details);
137        }
138       
139public:
140        virtual void Run2(bool &rChildExit)
141        {
142                try
143                {
144                        // Wait object with a timeout of 1 second, which
145                        // is a reasonable time to wait before cleaning up
146                        // finished child processes, and allows the daemon
147                        // to terminate reasonably quickly on request.
148                        WaitForEvent connectionWait(1000);
149                       
150                        // BLOCK
151                        {
152                                // Get the address we need to bind to
153                                // this-> in next line required to build under some gcc versions
154                                const Configuration &config(this->GetConfiguration());
155                                const Configuration &server(config.GetSubConfiguration("Server"));
156                                std::string addrs = server.GetKeyValue("ListenAddresses");
157       
158                                // split up the list of addresses
159                                std::vector<std::string> addrlist;
160                                SplitString(addrs, ',', addrlist);
161       
162                                for(unsigned int a = 0; a < addrlist.size(); ++a)
163                                {
164                                        // split the address up into components
165                                        std::vector<std::string> c;
166                                        SplitString(addrlist[a], ':', c);
167       
168                                        // listen!
169                                        SocketListen<StreamType, ListenBacklog> *psocket = new SocketListen<StreamType, ListenBacklog>;
170                                        try
171                                        {
172                                                if(c[0] == "inet")
173                                                {
174                                                        // Check arguments
175                                                        if(c.size() != 2 && c.size() != 3)
176                                                        {
177                                                                THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs)
178                                                        }
179                                                       
180                                                        // Which port?
181                                                        int port = Port;
182                                                       
183                                                        if(c.size() == 3)
184                                                        {
185                                                                // Convert to number
186                                                                port = ::atol(c[2].c_str());
187                                                                if(port <= 0 || port > ((64*1024)-1))
188                                                                {
189                                                                        THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs)
190                                                                }
191                                                        }
192                                                       
193                                                        // Listen
194                                                        psocket->Listen(Socket::TypeINET, c[1].c_str(), port);
195                                                }
196                                                else if(c[0] == "unix")
197                                                {
198                                                        #ifdef WIN32
199                                                                BOX_WARNING("Ignoring request to listen on a Unix socket on Windows: " << addrlist[a]);
200                                                                delete psocket;
201                                                                psocket = NULL;
202                                                        #else
203                                                                // Check arguments size
204                                                                if(c.size() != 2)
205                                                                {
206                                                                        THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs)
207                                                                }
208
209                                                                // unlink anything there
210                                                                ::unlink(c[1].c_str());
211                                                               
212                                                                psocket->Listen(Socket::TypeUNIX, c[1].c_str());
213                                                        #endif // WIN32
214                                                }
215                                                else
216                                                {
217                                                        delete psocket;
218                                                        THROW_EXCEPTION(ServerException, ServerStreamBadListenAddrs)
219                                                }
220                                               
221                                                if (psocket != NULL)
222                                                {
223                                                        // Add to list of sockets
224                                                        mSockets.push_back(psocket);
225                                                }
226                                        }
227                                        catch(...)
228                                        {
229                                                delete psocket;
230                                                throw;
231                                        }
232
233                                        if (psocket != NULL)
234                                        {
235                                                // Add to the list of things to wait on
236                                                connectionWait.Add(psocket);
237                                        }
238                                }
239                        }
240                       
241                        NotifyListenerIsReady();
242       
243                        while(!StopRun())
244                        {
245                                // Wait for a connection, or timeout
246                                SocketListen<StreamType, ListenBacklog> *psocket
247                                        = (SocketListen<StreamType, ListenBacklog> *)connectionWait.Wait();
248
249                                if(psocket)
250                                {
251                                        // Get the incoming connection
252                                        // (with zero wait time)
253                                        std::auto_ptr<StreamType> connection(
254                                                psocket->Accept(0,
255                                                        &mConnectionDetails));
256
257                                        // Was there one (there should be...)
258                                        if(connection.get())
259                                        {
260                                                // Since this is a template parameter, the if() will be optimised out by the compiler
261                                                #ifndef WIN32 // no fork on Win32
262                                                if(ForkToHandleRequests && !IsSingleProcess())
263                                                {
264                                                        pid_t pid = ::fork();
265                                                        switch(pid)
266                                                        {
267                                                        case -1:
268                                                                // Error!
269                                                                THROW_EXCEPTION(ServerException, ServerForkError)
270                                                                break;
271                                                               
272                                                        case 0:
273                                                                // Child process
274                                                                rChildExit = true;
275                                                                // Close listening sockets
276                                                                DeleteSockets();
277                                                               
278                                                                // Set up daemon
279                                                                EnterChild();
280                                                                SetProcessTitle("transaction");
281                                                                LogConnectionDetails(mConnectionDetails);
282                                                               
283                                                                // Memory leak test the forked process
284                                                                #ifdef BOX_MEMORY_LEAK_TESTING
285                                                                        memleakfinder_startsectionmonitor();
286                                                                #endif
287                                                               
288                                                                // The derived class does some server magic with the connection
289                                                                HandleConnection(*connection);
290                                                                // Since rChildExit == true, the forked process will call _exit() on return from this fn
291                                                                return;
292                       
293                                                        default:
294                                                                // parent daemon process
295                                                                break;
296                                                        }
297                                                       
298                                                        // Log it
299                                                        BOX_TRACE("Forked child process " << pid << 
300                                                                "to handle connection from " <<
301                                                                mConnectionDetails);
302                                                }
303                                                else
304                                                {
305                                                #endif // !WIN32
306                                                        // Just handle in this process
307                                                        SetProcessTitle("handling");
308                                                        HandleConnection(*connection);
309                                                        SetProcessTitle("idle");                                                                               
310                                                #ifndef WIN32
311                                                }
312                                                #endif // !WIN32
313                                        }
314                                }
315
316                                OnIdle();
317
318                                #ifndef WIN32
319                                // Clean up child processes (if forking daemon)
320                                if(ForkToHandleRequests && !IsSingleProcess())
321                                {
322                                        WaitForChildren();
323                                }
324                                #endif // !WIN32
325                        }
326                }
327                catch(...)
328                {
329                        DeleteSockets();
330                        throw;
331                }
332               
333                // Delete the sockets
334                DeleteSockets();
335        }
336
337        #ifndef WIN32 // no waitpid() on Windows
338        void WaitForChildren()
339        {
340                int p = 0;
341                do
342                {
343                        int status = 0;
344                        p = ::waitpid(0 /* any child in process group */,
345                                &status, WNOHANG);
346
347                        if(p == -1 && errno != ECHILD && errno != EINTR)
348                        {
349                                THROW_EXCEPTION(ServerException,
350                                        ServerWaitOnChildError)
351                        }
352                        else if(p == 0)
353                        {
354                                // no children exited, will return from
355                                // function
356                        }
357                        else if(WIFEXITED(status))
358                        {
359                                BOX_INFO("child process " << p << " "
360                                        "terminated normally");
361                        }
362                        else if(WIFSIGNALED(status))
363                        {
364                                int sig = WTERMSIG(status);
365                                BOX_ERROR("child process " << p << " "
366                                        "terminated abnormally with "
367                                        "signal " << sig);
368                        }
369                        else
370                        {
371                                BOX_WARNING("something unknown happened "
372                                        "to child process " << p << ": "
373                                        "status = " << status);
374                        }
375                }
376                while(p > 0);
377        }
378        #endif
379
380        virtual void HandleConnection(StreamType &rStream)
381        {
382                Connection(rStream);
383        }
384
385        virtual void Connection(StreamType &rStream) = 0;
386       
387protected:
388        // For checking code in derived classes -- use if you have an algorithm which
389        // depends on the forking model in case someone changes it later.
390        bool WillForkToHandleRequests()
391        {
392                #ifdef WIN32
393                return false;
394                #else
395                return ForkToHandleRequests && !IsSingleProcess();
396                #endif // WIN32
397        }
398
399private:
400        // --------------------------------------------------------------------------
401        //
402        // Function
403        //              Name:    ServerStream::DeleteSockets()
404        //              Purpose: Delete sockets
405        //              Created: 9/3/04
406        //
407        // --------------------------------------------------------------------------
408        void DeleteSockets()
409        {
410                for(unsigned int l = 0; l < mSockets.size(); ++l)
411                {
412                        if(mSockets[l])
413                        {
414                                mSockets[l]->Close();
415                                delete mSockets[l];
416                        }
417                        mSockets[l] = 0;
418                }
419                mSockets.clear();
420        }
421
422private:
423        std::vector<SocketListen<StreamType, ListenBacklog> *> mSockets;
424};
425
426#define SERVERSTREAM_VERIFY_SERVER_KEYS(DEFAULT_ADDRESSES) \
427        ConfigurationVerifyKey("ListenAddresses", 0, DEFAULT_ADDRESSES), \
428        DAEMON_VERIFY_SERVER_KEYS
429
430#include "MemLeakFindOff.h"
431
432#endif // SERVERSTREAM__H
433
434
435
Note: See TracBrowser for help on using the repository browser.