source: box/trunk/lib/server/WinNamedPipeStream.cpp @ 2318

Revision 2318, 14.7 KB checked in by chris, 4 years ago (diff)

Remove Win32 command socket thread, as it has caused too much trouble.

Handle command socket on Win32 the same as all other platforms, removing
#ifdefs from BackupDaemon?.

Will replace this thread with regular but not excessive command socket
polling using timers in future.

Change error messages when command socket comms fail to make them clearer.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    WinNamedPipeStream.cpp
5//              Purpose: I/O stream interface for Win32 named pipes
6//              Created: 2005/12/07
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#ifdef WIN32
13
14#ifdef HAVE_UNISTD_H
15        #include <unistd.h>
16#endif
17
18#include <sys/types.h>
19#include <errno.h>
20#include <windows.h>
21
22#include "WinNamedPipeStream.h"
23#include "ServerException.h"
24#include "CommonException.h"
25#include "Socket.h"
26
27#include "MemLeakFindOn.h"
28
29std::string WinNamedPipeStream::sPipeNamePrefix = "\\\\.\\pipe\\";
30
31// --------------------------------------------------------------------------
32//
33// Function
34//              Name:    WinNamedPipeStream::WinNamedPipeStream()
35//              Purpose: Constructor (create stream ready for Open() call)
36//              Created: 2005/12/07
37//
38// --------------------------------------------------------------------------
39WinNamedPipeStream::WinNamedPipeStream()
40        : mSocketHandle(INVALID_HANDLE_VALUE),
41          mReadableEvent(INVALID_HANDLE_VALUE),
42          mBytesInBuffer(0),
43          mReadClosed(false),
44          mWriteClosed(false),
45          mIsServer(false),
46          mIsConnected(false)
47{ }
48
49// --------------------------------------------------------------------------
50//
51// Function
52//              Name:    WinNamedPipeStream::WinNamedPipeStream(HANDLE)
53//              Purpose: Constructor (with already-connected pipe handle)
54//              Created: 2008/10/01
55//
56// --------------------------------------------------------------------------
57WinNamedPipeStream::WinNamedPipeStream(HANDLE hNamedPipe)
58        : mSocketHandle(hNamedPipe),
59          mReadableEvent(INVALID_HANDLE_VALUE),
60          mBytesInBuffer(0),
61          mReadClosed(false),
62          mWriteClosed(false),
63          mIsServer(true),
64          mIsConnected(true)
65{ 
66        // create the Readable event
67        mReadableEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
68
69        if (mReadableEvent == INVALID_HANDLE_VALUE)
70        {
71                BOX_ERROR("Failed to create the Readable event: " <<
72                        GetErrorMessage(GetLastError()));
73                Close();
74                THROW_EXCEPTION(CommonException, Internal)
75        }
76
77        // initialise the OVERLAPPED structure
78        memset(&mReadOverlap, 0, sizeof(mReadOverlap));
79        mReadOverlap.hEvent = mReadableEvent;
80
81        // start the first overlapped read
82        if (!ReadFile(mSocketHandle, mReadBuffer, sizeof(mReadBuffer),
83                NULL, &mReadOverlap))
84        {
85                DWORD err = GetLastError();
86
87                if (err != ERROR_IO_PENDING)
88                {
89                        BOX_ERROR("Failed to start overlapped read: " <<
90                                GetErrorMessage(err));
91                        Close();
92                        THROW_EXCEPTION(ConnectionException, 
93                                Conn_SocketReadError)
94                }
95        }
96}
97
98// --------------------------------------------------------------------------
99//
100// Function
101//              Name:    WinNamedPipeStream::~WinNamedPipeStream()
102//              Purpose: Destructor, closes stream if open
103//              Created: 2005/12/07
104//
105// --------------------------------------------------------------------------
106WinNamedPipeStream::~WinNamedPipeStream()
107{
108        if (mSocketHandle != INVALID_HANDLE_VALUE)
109        {
110                try
111                {
112                        Close();
113                }
114                catch (std::exception &e)
115                {
116                        BOX_ERROR("Caught exception while destroying "
117                                "named pipe, ignored: " << e.what());
118                }
119        }
120}
121
122// --------------------------------------------------------------------------
123//
124// Function
125//              Name:    WinNamedPipeStream::Accept(const std::string& rName)
126//              Purpose: Creates a new named pipe with the given name,
127//                      and wait for a connection on it
128//              Created: 2005/12/07
129//
130// --------------------------------------------------------------------------
131/*
132void WinNamedPipeStream::Accept()
133{
134        if (mSocketHandle == INVALID_HANDLE_VALUE)
135        {
136                THROW_EXCEPTION(ServerException, BadSocketHandle);
137        }
138
139        if (mIsConnected)
140        {
141                THROW_EXCEPTION(ServerException, SocketAlreadyOpen);
142        }
143
144        bool connected = ConnectNamedPipe(mSocketHandle, (LPOVERLAPPED) NULL);
145
146        if (!connected)
147        {
148                BOX_ERROR("Failed to ConnectNamedPipe(" << socket << "): " <<
149                        GetErrorMessage(GetLastError()));
150                Close();
151                THROW_EXCEPTION(ServerException, SocketOpenError)
152        }
153       
154        mBytesInBuffer = 0;
155        mReadClosed  = false;
156        mWriteClosed = false;
157        mIsServer    = true; // must flush and disconnect before closing
158        mIsConnected = true;
159
160        // create the Readable event
161        mReadableEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
162
163        if (mReadableEvent == INVALID_HANDLE_VALUE)
164        {
165                BOX_ERROR("Failed to create the Readable event: " <<
166                        GetErrorMessage(GetLastError()));
167                Close();
168                THROW_EXCEPTION(CommonException, Internal)
169        }
170
171        // initialise the OVERLAPPED structure
172        memset(&mReadOverlap, 0, sizeof(mReadOverlap));
173        mReadOverlap.hEvent = mReadableEvent;
174
175        // start the first overlapped read
176        if (!ReadFile(mSocketHandle, mReadBuffer, sizeof(mReadBuffer),
177                NULL, &mReadOverlap))
178        {
179                DWORD err = GetLastError();
180
181                if (err != ERROR_IO_PENDING)
182                {
183                        BOX_ERROR("Failed to start overlapped read: " <<
184                                GetErrorMessage(err));
185                        Close();
186                        THROW_EXCEPTION(ConnectionException,
187                                Conn_SocketReadError)
188                }
189        }
190}
191*/
192
193// --------------------------------------------------------------------------
194//
195// Function
196//              Name:    WinNamedPipeStream::Connect(const std::string& rName)
197//              Purpose: Opens a connection to a listening named pipe
198//              Created: 2005/12/07
199//
200// --------------------------------------------------------------------------
201void WinNamedPipeStream::Connect(const std::string& rName)
202{
203        if (mSocketHandle != INVALID_HANDLE_VALUE || mIsConnected) 
204        {
205                THROW_EXCEPTION(ServerException, SocketAlreadyOpen)
206        }
207
208        std::string socket = sPipeNamePrefix + rName;
209       
210        mSocketHandle = CreateFileA( 
211                socket.c_str(), // pipe name
212                GENERIC_READ |  // read and write access
213                GENERIC_WRITE, 
214                0,              // no sharing
215                NULL,           // default security attributes
216                OPEN_EXISTING,
217                0,              // default attributes
218                NULL);          // no template file
219
220        if (mSocketHandle == INVALID_HANDLE_VALUE)
221        {
222                DWORD err = GetLastError();
223                if (err == ERROR_PIPE_BUSY)
224                {
225                        BOX_ERROR("Failed to connect to backup daemon: "
226                                "it is busy with another connection");
227                }
228                else
229                {
230                        BOX_ERROR("Failed to connect to backup daemon: " <<
231                                GetErrorMessage(err));
232                }
233                THROW_EXCEPTION(ServerException, SocketOpenError)
234        }
235
236        mReadClosed  = false;
237        mWriteClosed = false;
238        mIsServer    = false; // just close the socket
239        mIsConnected = true;
240}
241
242// --------------------------------------------------------------------------
243//
244// Function
245//              Name:    WinNamedPipeStream::Read(void *pBuffer, int NBytes)
246//              Purpose: Reads data from stream. Maybe returns less than asked for.
247//              Created: 2003/07/31
248//
249// --------------------------------------------------------------------------
250int WinNamedPipeStream::Read(void *pBuffer, int NBytes, int Timeout)
251{
252        // TODO no support for timeouts yet
253        if (!mIsServer && Timeout != IOStream::TimeOutInfinite)
254        {
255                THROW_EXCEPTION(CommonException, AssertFailed)
256        }
257       
258        if (mSocketHandle == INVALID_HANDLE_VALUE || !mIsConnected) 
259        {
260                THROW_EXCEPTION(ServerException, BadSocketHandle)
261        }
262
263        if (mReadClosed)
264        {
265                THROW_EXCEPTION(ConnectionException, SocketShutdownError)
266        }
267
268        // ensure safe to cast NBytes to unsigned
269        if (NBytes < 0)
270        {
271                THROW_EXCEPTION(CommonException, AssertFailed)
272        }
273
274        DWORD NumBytesRead;
275
276        if (mIsServer)
277        {
278                // satisfy from buffer if possible, to avoid
279                // blocking on read.
280                bool needAnotherRead = false;
281                if (mBytesInBuffer == 0)
282                {
283                        // overlapped I/O completed successfully?
284                        // (wait if needed)
285                        DWORD waitResult = WaitForSingleObject(
286                                mReadOverlap.hEvent, Timeout);
287
288                        if (waitResult == WAIT_ABANDONED)
289                        {
290                                BOX_ERROR("Wait for command socket read "
291                                        "abandoned by system");
292                                THROW_EXCEPTION(ServerException,
293                                        BadSocketHandle);
294                        }
295                        else if (waitResult == WAIT_TIMEOUT)
296                        {
297                                // wait timed out, nothing to read
298                                NumBytesRead = 0;
299                        }
300                        else if (waitResult != WAIT_OBJECT_0)
301                        {
302                                BOX_ERROR("Failed to wait for command "
303                                        "socket read: unknown result " <<
304                                        waitResult);
305                        }
306                        // object is ready to read from
307                        else if (GetOverlappedResult(mSocketHandle,
308                                &mReadOverlap, &NumBytesRead, TRUE))
309                        {
310                                needAnotherRead = true;
311                        }
312                        else
313                        {
314                                DWORD err = GetLastError();
315
316                                if (err == ERROR_HANDLE_EOF)
317                                {
318                                        mReadClosed = true;
319                                }
320                                else 
321                                {
322                                        if (err == ERROR_BROKEN_PIPE)
323                                        {
324                                                BOX_NOTICE("Control client "
325                                                        "disconnected");
326                                        }
327                                        else
328                                        {
329                                                BOX_ERROR("Failed to wait for "
330                                                        "ReadFile to complete: "
331                                                        << GetErrorMessage(err));
332                                        }
333
334                                        Close();
335                                        THROW_EXCEPTION(ConnectionException, 
336                                                Conn_SocketReadError)
337                                }
338                        }
339                }
340                else
341                {
342                        NumBytesRead = 0;
343                }
344
345                size_t BytesToCopy = NumBytesRead + mBytesInBuffer;
346                size_t BytesRemaining = 0;
347
348                if (BytesToCopy > (size_t)NBytes)
349                {
350                        BytesRemaining = BytesToCopy - NBytes;
351                        BytesToCopy = NBytes;
352                }
353
354                memcpy(pBuffer, mReadBuffer, BytesToCopy);
355                memmove(mReadBuffer, mReadBuffer + BytesToCopy, BytesRemaining);
356
357                mBytesInBuffer = BytesRemaining;
358                NumBytesRead = BytesToCopy;
359
360                if (needAnotherRead)
361                {
362                        // reinitialise the OVERLAPPED structure
363                        memset(&mReadOverlap, 0, sizeof(mReadOverlap));
364                        mReadOverlap.hEvent = mReadableEvent;
365                }
366
367                // start the next overlapped read
368                if (needAnotherRead && !ReadFile(mSocketHandle, 
369                        mReadBuffer + mBytesInBuffer, 
370                        sizeof(mReadBuffer) - mBytesInBuffer,
371                        NULL, &mReadOverlap))
372                {
373                        DWORD err = GetLastError();
374                        if (err == ERROR_IO_PENDING)
375                        {
376                                // Don't reset yet, there might be data
377                                // in the buffer waiting to be read,
378                                // will check below.
379                                // ResetEvent(mReadableEvent);
380                        }
381                        else if (err == ERROR_HANDLE_EOF)
382                        {
383                                mReadClosed = true;
384                        }
385                        else if (err == ERROR_BROKEN_PIPE)
386                        {
387                                BOX_ERROR("Control client disconnected");
388                                mReadClosed = true;
389                        }
390                        else
391                        {
392                                BOX_ERROR("Failed to start overlapped read: "
393                                        << GetErrorMessage(err));
394                                Close();
395                                THROW_EXCEPTION(ConnectionException, 
396                                        Conn_SocketReadError)
397                        }
398                }
399        }
400        else
401        {
402                if (!ReadFile( 
403                        mSocketHandle, // pipe handle
404                        pBuffer,       // buffer to receive reply
405                        NBytes,        // size of buffer
406                        &NumBytesRead, // number of bytes read
407                        NULL))         // not overlapped
408                {
409                        DWORD err = GetLastError();
410               
411                        Close();
412
413                        // ERROR_NO_DATA is a strange name for
414                        // "The pipe is being closed". No exception wanted.
415
416                        if (err == ERROR_NO_DATA || 
417                                err == ERROR_PIPE_NOT_CONNECTED) 
418                        {
419                                NumBytesRead = 0;
420                        }
421                        else
422                        {
423                                BOX_ERROR("Failed to read from control socket: "
424                                        << GetErrorMessage(err));
425                                THROW_EXCEPTION(ConnectionException, 
426                                        Conn_SocketReadError)
427                        }
428                }
429               
430                // Closed for reading at EOF?
431                if (NumBytesRead == 0)
432                {
433                        mReadClosed = true;
434                }
435        }
436               
437        return NumBytesRead;
438}
439
440// --------------------------------------------------------------------------
441//
442// Function
443//              Name:    WinNamedPipeStream::Write(void *pBuffer, int NBytes)
444//              Purpose: Writes data, blocking until it's all done.
445//              Created: 2003/07/31
446//
447// --------------------------------------------------------------------------
448void WinNamedPipeStream::Write(const void *pBuffer, int NBytes)
449{
450        if (mSocketHandle == INVALID_HANDLE_VALUE || !mIsConnected) 
451        {
452                THROW_EXCEPTION(ServerException, BadSocketHandle)
453        }
454       
455        // Buffer in byte sized type.
456        ASSERT(sizeof(char) == 1);
457        const char *pByteBuffer = (char *)pBuffer;
458       
459        int NumBytesWrittenTotal = 0;
460
461        while (NumBytesWrittenTotal < NBytes)
462        {
463                DWORD NumBytesWrittenThisTime = 0;
464
465                bool Success = WriteFile( 
466                        mSocketHandle,    // pipe handle
467                        pByteBuffer + NumBytesWrittenTotal, // message
468                        NBytes      - NumBytesWrittenTotal, // message length
469                        &NumBytesWrittenThisTime, // bytes written this time
470                        NULL);            // not overlapped
471
472                if (!Success)
473                {
474                        // ERROR_NO_DATA is a strange name for
475                        // "The pipe is being closed".
476
477                        DWORD err = GetLastError();
478
479                        if (err != ERROR_NO_DATA)
480                        {
481                                BOX_ERROR("Failed to write to control "
482                                        "socket: " << GetErrorMessage(err));
483                        }
484
485                        Close();
486
487                        THROW_EXCEPTION(ConnectionException, 
488                                Conn_SocketWriteError)
489                }
490
491                NumBytesWrittenTotal += NumBytesWrittenThisTime;
492        }
493}
494
495// --------------------------------------------------------------------------
496//
497// Function
498//              Name:    WinNamedPipeStream::Close()
499//              Purpose: Closes connection to remote socket
500//              Created: 2003/07/31
501//
502// --------------------------------------------------------------------------
503void WinNamedPipeStream::Close()
504{
505        if (mSocketHandle == INVALID_HANDLE_VALUE && mIsConnected)
506        {
507                BOX_ERROR("Named pipe: inconsistent connected state");
508                mIsConnected = false;
509        }
510
511        if (mSocketHandle == INVALID_HANDLE_VALUE) 
512        {
513                THROW_EXCEPTION(ServerException, BadSocketHandle)
514        }
515
516        if (mIsServer)
517        {
518                if (!CancelIo(mSocketHandle))
519                {
520                        BOX_ERROR("Failed to cancel outstanding I/O: " <<
521                                GetErrorMessage(GetLastError()));
522                }
523
524                if (mReadableEvent == INVALID_HANDLE_VALUE)
525                {
526                        BOX_ERROR("Failed to destroy Readable event: "
527                                "invalid handle");
528                }
529                else if (!CloseHandle(mReadableEvent))
530                {
531                        BOX_ERROR("Failed to destroy Readable event: " <<
532                                GetErrorMessage(GetLastError()));
533                }
534
535                mReadableEvent = INVALID_HANDLE_VALUE;
536
537                if (!FlushFileBuffers(mSocketHandle))
538                {
539                        BOX_ERROR("Failed to FlushFileBuffers: " <<
540                                GetErrorMessage(GetLastError()));
541                }
542       
543                if (!DisconnectNamedPipe(mSocketHandle))
544                {
545                        DWORD err = GetLastError();
546                        if (err != ERROR_PIPE_NOT_CONNECTED)
547                        {
548                                BOX_ERROR("Failed to DisconnectNamedPipe: " <<
549                                        GetErrorMessage(err));
550                        }
551                }
552
553                mIsServer = false;
554        }
555
556        bool result = CloseHandle(mSocketHandle);
557
558        mSocketHandle = INVALID_HANDLE_VALUE;
559        mIsConnected = false;
560        mReadClosed  = true;
561        mWriteClosed = true;
562
563        if (!result) 
564        {
565                BOX_ERROR("Failed to CloseHandle: " <<
566                        GetErrorMessage(GetLastError()));
567                THROW_EXCEPTION(ServerException, SocketCloseError)
568        }
569}
570
571// --------------------------------------------------------------------------
572//
573// Function
574//              Name:    WinNamedPipeStream::StreamDataLeft()
575//              Purpose: Still capable of reading data?
576//              Created: 2003/08/02
577//
578// --------------------------------------------------------------------------
579bool WinNamedPipeStream::StreamDataLeft()
580{
581        return !mReadClosed;
582}
583
584// --------------------------------------------------------------------------
585//
586// Function
587//              Name:    WinNamedPipeStream::StreamClosed()
588//              Purpose: Connection been closed?
589//              Created: 2003/08/02
590//
591// --------------------------------------------------------------------------
592bool WinNamedPipeStream::StreamClosed()
593{
594        return mWriteClosed;
595}
596
597// --------------------------------------------------------------------------
598//
599// Function
600//              Name:    IOStream::WriteAllBuffered()
601//              Purpose: Ensures that any data which has been buffered is written to the stream
602//              Created: 2003/08/26
603//
604// --------------------------------------------------------------------------
605void WinNamedPipeStream::WriteAllBuffered()
606{
607        if (mSocketHandle == INVALID_HANDLE_VALUE || !mIsConnected) 
608        {
609                THROW_EXCEPTION(ServerException, BadSocketHandle)
610        }
611       
612        if (!FlushFileBuffers(mSocketHandle))
613        {
614                BOX_ERROR("Failed to FlushFileBuffers: " <<
615                        GetErrorMessage(GetLastError()));
616        }
617}
618
619
620#endif // WIN32
Note: See TracBrowser for help on using the repository browser.