1 /* $Id: ncbi_namedpipe.cpp 627741 2021-03-17 19:22:00Z ivanov $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author:  Anton Lavrentiev, Mike DiCuccio, Vladimir Ivanov
27  *
28  * File Description:
29  *   Portable interprocess named pipe API for:  UNIX, MS-Win
30  *
31  */
32 
33 #include <ncbi_pch.hpp>
34 #include <connect/error_codes.hpp>
35 #include <connect/ncbi_namedpipe.hpp>
36 #include <connect/ncbi_util.h>
37 #include <corelib/ncbifile.hpp>
38 
39 #if defined(NCBI_OS_UNIX)
40 
41 #  include <connect/ncbi_socket_unix.h>
42 #  include <errno.h>
43 #  include <unistd.h>
44 #  include <sys/socket.h>
45 #  include <sys/types.h>
46 
47 #elif !defined(NCBI_OS_MSWIN)
48 #  error "The CNamedPipe class is supported only on Windows and Unix"
49 #endif
50 
51 
52 #define NCBI_USE_ERRCODE_X   Connect_Pipe
53 
54 
55 #define NAMEDPIPE_THROW(err, errtxt)                \
56     THROW0_TRACE(x_FormatError(int(err), errtxt))
57 
58 
59 BEGIN_NCBI_SCOPE
60 
61 
62 #if defined(HAVE_SOCKLEN_T)  ||  defined(_SOCKLEN_T)
63 typedef socklen_t  SOCK_socklen_t;
64 #else
65 typedef int        SOCK_socklen_t;
66 #endif /*HAVE_SOCKLEN_T || _SOCKLEN_T*/
67 
68 
69 //////////////////////////////////////////////////////////////////////////////
70 //
71 // Auxiliary functions
72 //
73 
s_SetTimeout(const STimeout * from,STimeout * to)74 static inline const STimeout* s_SetTimeout(const STimeout* from, STimeout* to)
75 {
76     if ( !from ) {
77         return kInfiniteTimeout;
78     }
79     to->sec  = from->usec / kMicroSecondsPerSecond + from->sec;
80     to->usec = from->usec % kMicroSecondsPerSecond;
81     return to;
82 }
83 
84 
85 typedef AutoPtr< char, CDeleter<char> >  TTempCharPtr;
86 
87 
x_FormatError(int error,const string & message)88 static string x_FormatError(int error, const string& message)
89 {
90     const char* errstr;
91 
92 #ifdef NCBI_OS_MSWIN
93     string errmsg;
94     if ( error ) {
95         TCHAR* tmpstr = NULL;
96         DWORD rv = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
97                                    FORMAT_MESSAGE_FROM_SYSTEM     |
98                                    FORMAT_MESSAGE_MAX_WIDTH_MASK  |
99                                    FORMAT_MESSAGE_IGNORE_INSERTS,
100                                    NULL, (DWORD) error,
101                                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
102                                    (LPTSTR) &tmpstr, 0, NULL);
103         if (rv  &&  tmpstr) {
104             errmsg = _T_CSTRING(tmpstr);
105             errstr = errmsg.c_str();
106         } else {
107             errstr = "";
108         }
109         if ( tmpstr ) {
110             ::LocalFree((HLOCAL) tmpstr);
111         }
112     } else
113 #endif /*NCBI_OS_MSWIN*/
114         errstr = 0;
115 
116     int dynamic = 0/*false*/;
117     const char* result = ::NcbiMessagePlusError(&dynamic, message.c_str(),
118                                                 error, errstr);
119     TTempCharPtr retval(const_cast<char*> (result),
120                         dynamic ? eTakeOwnership : eNoOwnership);
121     return retval.get() ? retval.get() : message;
122 }
123 
124 
s_FormatErrorMessage(const string & where,const string & what)125 static string s_FormatErrorMessage(const string& where, const string& what)
126 {
127     return "[CNamedPipe::" + where + "]  " + what;
128 }
129 
130 
131 
132 //////////////////////////////////////////////////////////////////////////////
133 //
134 // Class CNamedPipeHandle handles forwarded requests from CNamedPipe.
135 // This class is reimplemented in a platform-specific fashion where needed.
136 //
137 
138 #if defined(NCBI_OS_MSWIN)
139 
140 
141 const unsigned long kWaitPrecision = 100;  // Timeout time slice (milliseconds)
142 
143 
x_IsDisconnectError(DWORD error)144 static inline bool x_IsDisconnectError(DWORD error)
145 {
146     return (error == ERROR_NO_DATA      ||
147             error == ERROR_BROKEN_PIPE  ||
148             error == ERROR_PIPE_NOT_CONNECTED ? true : false);
149 }
150 
151 
152 //////////////////////////////////////////////////////////////////////////////
153 //
154 // CNamedPipeHandle -- MS Windows version
155 //
156 
157 class CNamedPipeHandle
158 {
159 public:
160     CNamedPipeHandle(void);
161     ~CNamedPipeHandle();
162 
163     // client-side
164 
165     EIO_Status Open(const string& pipename, const STimeout* timeout,
166                     size_t pipesize, CNamedPipeClient::TFlags flags);
167 
168     // server-side
169 
170     EIO_Status Create(const string& pipename, size_t pipesize);
171     EIO_Status Listen(const STimeout* timeout);
172     EIO_Status Disconnect(void);
173 
174     // common
175 
176     EIO_Status Close(void);
177     EIO_Status Read (void*       buf, size_t count, size_t* n_read,
178                      const STimeout* timeout);
179     EIO_Status Write(const void* buf, size_t count, size_t* n_written,
180                      const STimeout* timeout);
181     EIO_Status Wait(EIO_Event event, const STimeout* timeout);
182     EIO_Status Status(EIO_Event direction) const;
183 
184 private:
185     EIO_Status x_Flush(void);
186     EIO_Status x_Disconnect(bool orderly = true);
187     EIO_Status x_WaitForRead(const STimeout* timeout, DWORD* in_avail);
188 
189     HANDLE     m_Pipe;         // pipe I/O handle
190     string     m_PipeName;     // pipe name
191     bool       m_Flushed;      // false if data written
192     int        m_Connected;    // if connected (-1=server; 1|3=client)
193     EIO_Status m_ReadStatus;   // last read status
194     EIO_Status m_WriteStatus;  // last write status
195 };
196 
197 
CNamedPipeHandle(void)198 CNamedPipeHandle::CNamedPipeHandle(void)
199     : m_Pipe(INVALID_HANDLE_VALUE), m_Flushed(true), m_Connected(0),
200       m_ReadStatus(eIO_Closed), m_WriteStatus(eIO_Closed)
201 {
202     return;
203 }
204 
205 
~CNamedPipeHandle()206 CNamedPipeHandle::~CNamedPipeHandle()
207 {
208     Close();
209 }
210 
211 
Open(const string & pipename,const STimeout * timeout,size_t,CNamedPipeClient::TFlags flags)212 EIO_Status CNamedPipeHandle::Open(const string&            pipename,
213                                   const STimeout*          timeout,
214                                   size_t                   /*pipesize*/,
215                                   CNamedPipeClient::TFlags flags)
216 {
217     EIO_Status status = eIO_Unknown;
218 
219     try {
220         if (m_Pipe != INVALID_HANDLE_VALUE) {
221             NAMEDPIPE_THROW(0,
222                             "Named pipe \"" + m_PipeName
223                             + "\" already open");
224         }
225         _ASSERT(m_Flushed  &&  !m_Connected);
226 
227         // Set the base security attributes
228         SECURITY_ATTRIBUTES attr;
229         attr.nLength = sizeof(attr);
230         attr.bInheritHandle = TRUE;
231         attr.lpSecurityDescriptor = NULL;
232 
233         // Wait until either a time-out interval elapses or an instance of
234         // the specified named pipe is available for connection (that is, the
235         // pipe's server process has a pending Listen() operation on the pipe).
236 
237         // NOTE:  We do not use WaitNamedPipe() here because it works
238         //        incorrectly in some cases!
239         HANDLE pipe;
240 
241         DWORD x_timeout = timeout ? NcbiTimeoutToMs(timeout) : INFINITE;
242 
243         unsigned long x_sleep = 1;
244         for (;;) {
245             // Try to open existing pipe
246             pipe = ::CreateFile(_T_XCSTRING(pipename),
247                                 GENERIC_READ | GENERIC_WRITE,
248                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
249                                 &attr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
250                                 NULL);
251             DWORD error;
252             if (pipe != INVALID_HANDLE_VALUE) {
253                 DWORD mode = PIPE_READMODE_BYTE | PIPE_NOWAIT;  // non-blocking
254                 if ( ::SetNamedPipeHandleState(pipe, &mode, NULL, NULL) ) {
255                     break;
256                 }
257                 error = ::GetLastError();
258                 ::CloseHandle(pipe);
259             } else
260                 error = ::GetLastError();
261             // NB: "pipe" is closed at this point
262             if ((pipe == INVALID_HANDLE_VALUE
263                  &&  error != ERROR_PIPE_BUSY)  ||
264                 (pipe != INVALID_HANDLE_VALUE
265                  &&  error != ERROR_PIPE_NOT_CONNECTED)) {
266                 if (pipe == INVALID_HANDLE_VALUE
267                     &&  error == ERROR_FILE_NOT_FOUND) {
268                     status = eIO_Closed;
269                     if (flags & CNamedPipeClient::fNoLogIfClosed) {
270                         return status;
271                     }
272                 }
273                 NAMEDPIPE_THROW(error,
274                                 "Named pipe \"" + pipename
275                                 + "\" failed to "
276                                 + string(pipe == INVALID_HANDLE_VALUE
277                                          ? "open" : "set non-blocking"));
278             }
279 
280             if ( !x_timeout ) {
281                 return eIO_Timeout;
282             }
283             if (x_timeout != INFINITE) {
284                 if (x_sleep > x_timeout) {
285                     x_sleep = x_timeout;
286                 }
287                 x_timeout -= x_sleep;
288             }
289             SleepMilliSec(x_sleep);
290             x_sleep <<= 1;
291             if (x_sleep > kWaitPrecision) {
292                 x_sleep = kWaitPrecision;
293             }
294         }
295         _ASSERT(pipe != INVALID_HANDLE_VALUE);
296 
297         m_Pipe        = pipe;
298         m_PipeName    = pipename;
299         m_Connected   = 1/*client*/;
300         m_ReadStatus  = eIO_Success;
301         m_WriteStatus = eIO_Success;
302         return eIO_Success;
303     }
304     catch (string& what) {
305         ERR_POST_X(10, s_FormatErrorMessage("Open", what));
306     }
307 
308     return status;
309 }
310 
311 
Create(const string & pipename,size_t pipesize)312 EIO_Status CNamedPipeHandle::Create(const string& pipename,
313                                     size_t        pipesize)
314 {
315     EIO_Status status = eIO_Unknown;
316 
317     try {
318         if (m_Pipe != INVALID_HANDLE_VALUE) {
319             NAMEDPIPE_THROW(0,
320                             "Named pipe \"" + m_PipeName
321                             + "\" already exists");
322         }
323         _ASSERT(m_Flushed  &&  !m_Connected);
324 
325         if (pipesize > numeric_limits<DWORD>::max()) {
326             NAMEDPIPE_THROW(0,
327                             "Named pipe \"" + pipename
328                             + "\" buffer size "
329                             + NStr::NumericToString(pipesize)
330                             + " too large");
331         }
332 
333         // Set the base security attributes
334         SECURITY_ATTRIBUTES attr;
335         attr.nLength = sizeof(attr);
336         attr.bInheritHandle = TRUE;
337         attr.lpSecurityDescriptor = NULL;
338 
339         // Create pipe
340         m_Pipe = ::CreateNamedPipe
341             (_T_XCSTRING(pipename),         // pipe name
342              PIPE_ACCESS_DUPLEX,            // read/write access
343              PIPE_TYPE_BYTE | PIPE_NOWAIT,  // byte-type, non-blocking mode
344              1,                             // one instance only
345              (DWORD) pipesize,              // output buffer size
346              (DWORD) pipesize,              // input  buffer size
347              INFINITE,                      // default client timeout
348              &attr);                        // security attributes
349 
350         if (m_Pipe == INVALID_HANDLE_VALUE) {
351             DWORD error = ::GetLastError();
352             if (error == ERROR_ALREADY_EXISTS) {
353                 status = eIO_Closed;
354             }
355             NAMEDPIPE_THROW(error,
356                             "Named pipe \"" + pipename
357                             + "\" failed to create");
358         }
359 
360         m_PipeName    = pipename;
361         m_ReadStatus  = eIO_Success;
362         m_WriteStatus = eIO_Success;
363         return eIO_Success;
364     }
365     catch (string& what) {
366         ERR_POST_X(11, s_FormatErrorMessage("Create", what));
367     }
368 
369     return status;
370 }
371 
372 
Listen(const STimeout * timeout)373 EIO_Status CNamedPipeHandle::Listen(const STimeout* timeout)
374 {
375     EIO_Status status = eIO_Unknown;
376 
377     try {
378         if (m_Pipe == INVALID_HANDLE_VALUE  ||  m_Connected) {
379             status = eIO_Closed;
380             NAMEDPIPE_THROW(0,
381                             "Named pipe \"" + m_PipeName
382                             + '"' + string(m_Pipe == INVALID_HANDLE_VALUE
383                                            ? " closed"
384                                            : " busy"));
385         }
386         _ASSERT(m_Flushed  &&  !m_Connected);
387 
388         DWORD x_timeout = timeout ? NcbiTimeoutToMs(timeout) : INFINITE;
389 
390         // Wait for the client to connect, or time out.
391         // NOTE:  WaitForSingleObject() does not work with pipes.
392 
393         unsigned long x_sleep = 1;
394         for (;;) {
395             if ( ::ConnectNamedPipe(m_Pipe, NULL) ) {
396                 break; // client connected while within ConnectNamedPipe()
397             }
398             DWORD error = ::GetLastError();
399             if (error == ERROR_PIPE_CONNECTED) {
400                 break; // client connected before ConnectNamedPipe() was called
401             }
402 
403             // NB: status == eIO_Unknown
404             if (error == ERROR_NO_DATA/*not disconnected???*/) {
405                 if (x_Disconnect(false/*abort*/) == eIO_Success) {
406                     continue; // try again
407                 }
408                 NAMEDPIPE_THROW(error,
409                                 "Named pipe \"" + m_PipeName
410                                 + "\" still connected");
411             }
412             if (error != ERROR_PIPE_LISTENING) {
413                 NAMEDPIPE_THROW(error,
414                                 "Named pipe \"" + m_PipeName
415                                 + "\" not listening");
416             }
417 
418             if ( !x_timeout ) {
419                 return eIO_Timeout;
420             }
421             if (x_timeout != INFINITE) {
422                 if (x_sleep > x_timeout) {
423                     x_sleep = x_timeout;
424                 }
425                 x_timeout -= x_sleep;
426             }
427             SleepMilliSec(x_sleep);
428             x_sleep <<= 1;
429             if (x_sleep > kWaitPrecision) {
430                 x_sleep = kWaitPrecision;
431             }
432         }
433 
434         // Pipe connected
435         m_Connected   = -1/*server*/;
436         m_ReadStatus  = eIO_Success;
437         m_WriteStatus = eIO_Success;
438         return eIO_Success;
439     }
440     catch (string& what) {
441         ERR_POST_X(12, s_FormatErrorMessage("Listen", what));
442     }
443 
444     return status;
445 }
446 
447 
x_Flush(void)448 EIO_Status CNamedPipeHandle::x_Flush(void)
449 {
450     _ASSERT(m_Pipe != INVALID_HANDLE_VALUE);
451     if (!m_Flushed) {
452         if (m_Connected  &&  !::FlushFileBuffers(m_Pipe)) {
453             NAMEDPIPE_THROW(::GetLastError(),
454                             "Named pipe \"" + m_PipeName
455                             + "\" failed to flush");
456         }
457         m_Flushed = true;
458     }
459     return eIO_Success;
460 }
461 
462 
x_Disconnect(bool orderly)463 EIO_Status CNamedPipeHandle::x_Disconnect(bool orderly)
464 {
465     EIO_Status status;
466     if (m_Connected <= 0  &&  orderly) {
467         if (m_Pipe == INVALID_HANDLE_VALUE  ||  !m_Connected) {
468             status = eIO_Closed;
469             NAMEDPIPE_THROW(0,
470                             "Named pipe \"" + m_PipeName
471                             + "\" already disconnected");
472         }
473         status = x_Flush();
474     } else {
475         m_Flushed = true;
476         status = eIO_Success;
477     }
478     if (m_Connected <= 0  &&  !::DisconnectNamedPipe(m_Pipe)) {
479         status = eIO_Unknown;
480         if (orderly) {
481             NAMEDPIPE_THROW(::GetLastError(),
482                             "Named pipe \"" + m_PipeName
483                             + "\" failed to disconnect");
484         }
485     } else {
486         // Per documentation, another client can now connect again
487         m_Connected  = 0;
488     }
489     return status;
490 }
491 
492 
Disconnect(void)493 EIO_Status CNamedPipeHandle::Disconnect(void)
494 {
495     EIO_Status status = eIO_Unknown;
496 
497     try {
498         status = x_Disconnect(/*orderly*/);
499 
500         _ASSERT(m_Flushed  &&  !m_Connected);
501         m_ReadStatus  = eIO_Closed;
502         m_WriteStatus = eIO_Closed;
503     }
504     catch (string& what) {
505         ERR_POST_X(13, s_FormatErrorMessage("Disconnect", what));
506     }
507 
508     return status;
509 }
510 
511 
Close(void)512 EIO_Status CNamedPipeHandle::Close(void)
513 {
514     if (m_Pipe == INVALID_HANDLE_VALUE) {
515         _ASSERT(m_Flushed  &&  !m_Connected);
516         return eIO_Closed;
517     }
518     EIO_Status status = eIO_Unknown;
519     try {
520         if (m_Connected < 0) {
521             status = x_Disconnect(/*orderly*/);
522             _ASSERT(m_Flushed  &&  !m_Connected);
523         } else {
524             status = x_Flush();
525             _ASSERT(m_Flushed);
526             m_Connected = 0;
527         }
528     }
529     catch (string& what) {
530         ERR_POST_X(8, s_FormatErrorMessage("Close", what));
531         m_Flushed = true;
532         m_Connected = 0;
533     }
534     (void) ::CloseHandle(m_Pipe);
535     m_Pipe = INVALID_HANDLE_VALUE;
536     m_ReadStatus  = eIO_Closed;
537     m_WriteStatus = eIO_Closed;
538     return status;
539 }
540 
541 
x_WaitForRead(const STimeout * timeout,DWORD * in_avail)542 EIO_Status CNamedPipeHandle::x_WaitForRead(const STimeout* timeout,
543                                            DWORD*          in_avail)
544 {
545     _ASSERT(m_Connected);
546 
547     *in_avail = 0;
548 
549     DWORD x_timeout = timeout ? NcbiTimeoutToMs(timeout) : INFINITE;
550 
551     // Wait for data from the pipe with timeout.
552     // NOTE:  WaitForSingleObject() does not work with pipes.
553 
554     unsigned long x_sleep = 1;
555     for (;;) {
556         BOOL ok = ::PeekNamedPipe(m_Pipe, NULL, 0, NULL, in_avail, NULL);
557         if ( *in_avail ) {
558             if (!(m_Connected & 2)) {
559                 m_Connected |= 2;
560             }
561             m_Flushed = true;
562             break;
563         }
564         if ( !ok ) {
565             // Has peer closed the connection?
566             DWORD error = ::GetLastError();
567             if ((m_Connected & 2)  ||  error != ERROR_PIPE_NOT_CONNECTED) {
568                 if ( !x_IsDisconnectError(error) ) {
569                     m_ReadStatus = eIO_Unknown;
570                     return eIO_Unknown;
571                 }
572                 m_ReadStatus  = eIO_Closed;
573                 m_WriteStatus = eIO_Closed;
574                 return eIO_Closed;
575             }
576         }
577 
578         if ( !x_timeout ) {
579             return eIO_Timeout;
580         }
581         if (x_timeout != INFINITE) {
582             if (x_sleep > x_timeout) {
583                 x_sleep = x_timeout;
584             }
585             x_timeout -= x_sleep;
586         }
587         SleepMilliSec(x_sleep);
588         x_sleep <<= 1;
589         if (x_sleep > kWaitPrecision) {
590             x_sleep = kWaitPrecision;
591         }
592     }
593 
594     _ASSERT(*in_avail);
595     return eIO_Success;
596 }
597 
598 
Read(void * buf,size_t count,size_t * n_read,const STimeout * timeout)599 EIO_Status CNamedPipeHandle::Read(void* buf, size_t count, size_t* n_read,
600                                   const STimeout* timeout)
601 {
602     _ASSERT(n_read  &&  !*n_read);
603 
604     EIO_Status status = eIO_Unknown;
605 
606     try {
607         if (m_Pipe == INVALID_HANDLE_VALUE  ||  !m_Connected) {
608             NAMEDPIPE_THROW(0,
609                             "Named pipe \"" + m_PipeName
610                             + '"' + string(m_Pipe == INVALID_HANDLE_VALUE
611                                            ? " closed"
612                                            : " not connected"));
613         }
614         if (m_ReadStatus == eIO_Closed) {
615             return eIO_Closed;
616         }
617         if ( !count ) {
618             return eIO_Success;
619         }
620 
621         DWORD bytes_avail;
622         if ((status = x_WaitForRead(timeout, &bytes_avail)) == eIO_Success) {
623             _ASSERT(bytes_avail);
624             // We must read only "count" bytes of data regardless of the amount
625             // available to read.
626             if (bytes_avail >         count) {
627                 bytes_avail = (DWORD) count;
628             }
629             BOOL ok = ::ReadFile(m_Pipe, buf, bytes_avail, &bytes_avail, NULL);
630             if ( !bytes_avail ) {
631                 status = eIO_Unknown;
632                 m_ReadStatus = eIO_Unknown;
633                 NAMEDPIPE_THROW(!ok ? ::GetLastError() : 0,
634                                 "Named pipe \"" + m_PipeName
635                                 + "\" read failed");
636             } else {
637                 // NB: status == eIO_Success
638                 m_ReadStatus = eIO_Success;
639                 *n_read = bytes_avail;
640                 _ASSERT(m_Flushed);
641             }
642         } else if (status == eIO_Timeout) {
643             m_ReadStatus = eIO_Timeout;
644         } else if (status != eIO_Closed) {
645             NAMEDPIPE_THROW(::GetLastError(),
646                             "Named pipe \"" + m_PipeName
647                             + "\" peek failed");
648         }
649     }
650     catch (string& what) {
651         ERR_POST_X(14, s_FormatErrorMessage("Read", what));
652     }
653 
654     return status;
655 }
656 
657 
Write(const void * buf,size_t count,size_t * n_written,const STimeout * timeout)658 EIO_Status CNamedPipeHandle::Write(const void* buf, size_t count,
659                                    size_t* n_written, const STimeout* timeout)
660 
661 {
662     _ASSERT(n_written  &&  !*n_written);
663 
664     EIO_Status status = eIO_Unknown;
665 
666     try {
667         if (m_Pipe == INVALID_HANDLE_VALUE  ||  !m_Connected) {
668             NAMEDPIPE_THROW(0,
669                             "Named pipe \"" + m_PipeName
670                             + '"' + string(m_Pipe == INVALID_HANDLE_VALUE
671                                            ? " closed"
672                                            : " not connected"));
673         }
674         if (m_WriteStatus == eIO_Closed) {
675             return eIO_Closed;
676         }
677         if ( !count ) {
678             return eIO_Success;
679         }
680 
681         DWORD x_timeout = timeout ? NcbiTimeoutToMs(timeout) : INFINITE;
682         DWORD to_write  = (count > numeric_limits<DWORD>::max()
683                            ? numeric_limits<DWORD>::max()
684                            : (DWORD) count);
685         DWORD bytes_written = 0;
686 
687         unsigned long x_sleep = 1;
688         for (;;) {
689             BOOL ok = ::WriteFile(m_Pipe, buf, to_write, &bytes_written, NULL);
690             if ( bytes_written ) {
691                 if (!(m_Connected & 2)) {
692                     m_Connected |= 2;
693                 }
694                 m_WriteStatus = ok ? eIO_Success : eIO_Unknown;
695                 m_Flushed = false;
696                 break;
697             }
698             if ( !ok ) {
699                 DWORD error = ::GetLastError();
700                 if ((m_Connected & 2)  ||  error != ERROR_PIPE_NOT_CONNECTED) {
701                     if ( x_IsDisconnectError(error) ) {
702                         m_ReadStatus  = eIO_Closed;
703                         m_WriteStatus = eIO_Closed;
704                         status        = eIO_Closed;
705                     } else {
706                         m_WriteStatus = eIO_Unknown;
707                         // NB: status == eIO_Unknown
708                     }
709                     NAMEDPIPE_THROW(error,
710                                     "Named pipe \"" + m_PipeName
711                                     + "\" write failed");
712                 }
713             }
714 
715             if ( !x_timeout ) {
716                 m_WriteStatus = eIO_Timeout;
717                 return eIO_Timeout;
718             }
719             if (x_timeout != INFINITE) {
720                 if (x_sleep > x_timeout) {
721                     x_sleep = x_timeout;
722                 }
723                 x_timeout -= x_sleep;
724             }
725             SleepMilliSec(x_sleep);
726             x_sleep <<= 1;
727             if (x_sleep > kWaitPrecision) {
728                 x_sleep = kWaitPrecision;
729             }
730         }
731         _ASSERT(bytes_written);
732 
733         *n_written = bytes_written;
734         status     = eIO_Success;
735     }
736     catch (string& what) {
737         ERR_POST_X(15, s_FormatErrorMessage("Write", what));
738     }
739 
740     return status;
741 }
742 
743 
Wait(EIO_Event event,const STimeout * timeout)744 EIO_Status CNamedPipeHandle::Wait(EIO_Event event, const STimeout* timeout)
745 {
746     if (m_Pipe == INVALID_HANDLE_VALUE  ||  !m_Connected) {
747         ERR_POST_X(9, s_FormatErrorMessage("Wait",
748                                            "Named pipe \"" + m_PipeName + '"'
749                                            + string(m_Pipe == INVALID_HANDLE_VALUE
750                                                     ? " closed"
751                                                     : " not connected")));
752         return eIO_Unknown;
753     }
754     if (m_ReadStatus  == eIO_Closed) {
755         event = (EIO_Event)(event & ~eIO_Read);
756     }
757     if (m_WriteStatus == eIO_Closed) {
758         event = (EIO_Event)(event & ~eIO_Write);
759     }
760     if (!(event & eIO_Read)) {
761         return event ? eIO_Success : eIO_Closed;
762     }
763     DWORD x_avail;
764     EIO_Status status = x_WaitForRead(timeout, &x_avail);
765     return status == eIO_Closed ? eIO_Success : status;
766 }
767 
768 
Status(EIO_Event direction) const769 EIO_Status CNamedPipeHandle::Status(EIO_Event direction) const
770 {
771     _ASSERT(m_Connected  ||  (m_ReadStatus  == eIO_Closed  &&
772                               m_WriteStatus == eIO_Closed));
773     switch ( direction ) {
774     case eIO_Read:
775         return m_ReadStatus;
776     case eIO_Write:
777         return m_WriteStatus;
778     default:
779         _TROUBLE;
780         break;
781     }
782     return eIO_InvalidArg;
783 }
784 
785 
786 #elif defined(NCBI_OS_UNIX)
787 
788 
789 //////////////////////////////////////////////////////////////////////////////
790 //
791 // CNamedPipeHandle -- Unix version
792 //
793 
794 // The maximum length the queue of pending connections may grow to
795 static const int kListenQueueSize = 64;
796 
797 class CNamedPipeHandle
798 {
799 public:
800     CNamedPipeHandle(void);
801     ~CNamedPipeHandle();
802 
803     // client-side
804 
805     EIO_Status Open(const string& pipename, const STimeout* timeout,
806                     size_t pipesize, CNamedPipeClient::TFlags flags);
807 
808     // server-side
809 
810     EIO_Status Create(const string& pipename, size_t pipesize);
811     EIO_Status Listen(const STimeout* timeout);
812     EIO_Status Disconnect(void);
813 
814     // common
815 
816     EIO_Status Close(void);
817     EIO_Status Read (void* buf, size_t count, size_t* n_read,
818                      const STimeout* timeout);
819     EIO_Status Write(const void* buf, size_t count, size_t* n_written,
820                      const STimeout* timeout);
821     EIO_Status Wait(EIO_Event event, const STimeout* timeout);
822     EIO_Status Status(EIO_Event direction) const;
823 
824 private:
825     // Set socket I/O buffer size (dir: SO_SNDBUF, SO_RCVBUF)
826     bool x_SetSocketBufSize(int sock, size_t bufsize, int dir);
827     // Disconnect implementation
828     EIO_Status x_Disconnect(const char* where);
829 
830 private:
831     LSOCK      m_LSocket;   // listening socket
832     SOCK       m_IoSocket;  // I/O socket
833     size_t     m_PipeSize;  // pipe size
834     string     m_PipeName;  // pipe name
835 };
836 
837 
CNamedPipeHandle(void)838 CNamedPipeHandle::CNamedPipeHandle(void)
839     : m_LSocket(0), m_IoSocket(0), m_PipeSize(0)
840 {
841     return;
842 }
843 
844 
~CNamedPipeHandle()845 CNamedPipeHandle::~CNamedPipeHandle()
846 {
847     Close();
848 }
849 
850 
Open(const string & pipename,const STimeout * timeout,size_t pipesize,CNamedPipeClient::TFlags flags)851 EIO_Status CNamedPipeHandle::Open(const string&            pipename,
852                                   const STimeout*          timeout,
853                                   size_t                   pipesize,
854                                   CNamedPipeClient::TFlags flags)
855 {
856     EIO_Status status = eIO_Unknown;
857 
858     try {
859         if (m_LSocket  ||  m_IoSocket) {
860             NAMEDPIPE_THROW(0,
861                             "Named pipe \"" + m_PipeName
862                             + "\" already open");
863         }
864 
865         status = SOCK_CreateUNIX(pipename.c_str(), timeout, &m_IoSocket,
866                                  NULL, 0, 0/*flags*/);
867         if (status == eIO_Closed
868             &&  (flags & CNamedPipeClient::fNoLogIfClosed)) {
869             return status;
870         }
871         if (status != eIO_Success) {
872             NAMEDPIPE_THROW(0,
873                             "Named pipe \"" + pipename
874                             + "\" failed to open UNIX socket: "
875                             + string(IO_StatusStr(status)));
876         }
877         SOCK_SetTimeout(m_IoSocket, eIO_Close, timeout);
878 
879         // Set buffer size
880         if (pipesize) {
881             int fd;
882             if (SOCK_GetOSHandle(m_IoSocket, &fd, sizeof(fd)) == eIO_Success) {
883                 if (!x_SetSocketBufSize(fd, pipesize, SO_SNDBUF)  ||
884                     !x_SetSocketBufSize(fd, pipesize, SO_RCVBUF)) {
885                     int error = errno;
886                     _ASSERT(error);
887                     NAMEDPIPE_THROW(error,
888                                     "Named pipe \"" + pipename
889                                     + "\" failed to set"
890                                     " UNIX socket buffer size "
891                                     + NStr::NumericToString(pipesize));
892                 }
893             }
894         }
895 
896         m_PipeSize = 0/*not needed*/;
897         m_PipeName = pipename;
898         return eIO_Success;
899     }
900     catch (string& what) {
901         ERR_POST_X(10, s_FormatErrorMessage("Open", what));
902     }
903 
904     return status;
905 }
906 
907 
Create(const string & pipename,size_t pipesize)908 EIO_Status CNamedPipeHandle::Create(const string& pipename,
909                                     size_t        pipesize)
910 {
911     EIO_Status status = eIO_Unknown;
912 
913     try {
914         if (m_LSocket  ||  m_IoSocket) {
915             NAMEDPIPE_THROW(0,
916                             "Named pipe \"" + m_PipeName
917                             + "\" already exists");
918         }
919 
920         CDirEntry pipe(pipename);
921         switch (pipe.GetType()) {
922         case CDirEntry::eSocket:
923             pipe.Remove();
924             /*FALLTHRU*/
925         case CDirEntry::eUnknown:
926             // File does not exist
927             break;
928         default:
929             status = eIO_Unknown;
930             NAMEDPIPE_THROW(0,
931                             "Named pipe path \"" + pipename
932                             + "\" already exists");
933         }
934 
935         status = LSOCK_CreateUNIX(pipename.c_str(),
936                                   kListenQueueSize,
937                                   &m_LSocket, 0);
938         if (status != eIO_Success) {
939             NAMEDPIPE_THROW(0,
940                             "Named pipe \"" + pipename
941                             + "\" failed to create listening"
942                             " UNIX socket: " + string(IO_StatusStr(status)));
943         }
944 
945         m_PipeSize = pipesize;
946         m_PipeName = pipename;
947         return eIO_Success;
948     }
949     catch (string& what) {
950         ERR_POST_X(11, s_FormatErrorMessage("Create", what));
951     }
952 
953     return status;
954 }
955 
956 
Listen(const STimeout * timeout)957 EIO_Status CNamedPipeHandle::Listen(const STimeout* timeout)
958 {
959     EIO_Status status = eIO_Unknown;
960 
961     try {
962         if (!m_LSocket  ||  m_IoSocket) {
963             status = eIO_Closed;
964             NAMEDPIPE_THROW(0,
965                             "Named pipe \"" + m_PipeName
966                             + '"' + string(m_LSocket
967                                            ? " closed"
968                                            : " busy"));
969         }
970 
971         status = LSOCK_Accept(m_LSocket, timeout, &m_IoSocket);
972         if (status == eIO_Timeout) {
973             return status;
974         }
975         if (status != eIO_Success) {
976             NAMEDPIPE_THROW(0,
977                             "Named pipe \"" + m_PipeName
978                             + "\" failed to accept in UNIX socket: "
979                             + string(IO_StatusStr(status)));
980         }
981         _ASSERT(m_IoSocket);
982 
983         // Set buffer size
984         if (m_PipeSize) {
985             int fd;
986             if (SOCK_GetOSHandle(m_IoSocket, &fd, sizeof(fd)) == eIO_Success) {
987                 if (!x_SetSocketBufSize(fd, m_PipeSize, SO_SNDBUF)  ||
988                     !x_SetSocketBufSize(fd, m_PipeSize, SO_RCVBUF)) {
989                     NAMEDPIPE_THROW(errno,
990                                     "Named pipe \"" + m_PipeName
991                                     + "\" failed to set UNIX socket buffer "
992                                     "size "+NStr::NumericToString(m_PipeSize));
993                 }
994             }
995         }
996 
997         return eIO_Success;
998     }
999     catch (string& what) {
1000         ERR_POST_X(12, s_FormatErrorMessage("Listen", what));
1001     }
1002 
1003     return status;
1004 }
1005 
1006 
x_Disconnect(const char * where)1007 EIO_Status CNamedPipeHandle::x_Disconnect(const char* where)
1008 {
1009     // Close I/O socket
1010     _ASSERT(m_IoSocket);
1011     EIO_Status status = SOCK_Close(m_IoSocket);
1012     m_IoSocket = 0;
1013 
1014     if (status != eIO_Success) {
1015         string verb(where);
1016         ERR_POST_X(8, s_FormatErrorMessage(where,
1017                                            x_FormatError(0,
1018                                                          "Named pipe \""
1019                                                          + m_PipeName + "\""
1020                                                          " failed to "
1021                                                          + NStr::ToLower
1022                                                          (verb))));
1023     }
1024     return status;
1025 }
1026 
1027 
Disconnect(void)1028 EIO_Status CNamedPipeHandle::Disconnect(void)
1029 {
1030     if ( !m_IoSocket ) {
1031         ERR_POST_X(13, s_FormatErrorMessage("Disconnect",
1032                                             "Named pipe \"" + m_PipeName
1033                                             + "\" already disconnected"));
1034         return eIO_Closed;
1035     }
1036     return x_Disconnect("Disconnect");
1037 }
1038 
1039 
Close(void)1040 EIO_Status CNamedPipeHandle::Close(void)
1041 {
1042     if (!m_LSocket  &&  !m_IoSocket) {
1043         return eIO_Closed;
1044     }
1045     // Close listening socket
1046     if ( m_LSocket ) {
1047         (void) LSOCK_Close(m_LSocket);
1048         m_LSocket = 0;
1049     }
1050     // Disconnect if connected
1051     return m_IoSocket ? x_Disconnect("Close") : eIO_Success;
1052 }
1053 
1054 
Read(void * buf,size_t count,size_t * n_read,const STimeout * timeout)1055 EIO_Status CNamedPipeHandle::Read(void* buf, size_t count, size_t* n_read,
1056                                   const STimeout* timeout)
1057 {
1058     _ASSERT(n_read  &&  !*n_read);
1059 
1060     EIO_Status status = eIO_Unknown;
1061 
1062     try {
1063         if ( !m_IoSocket ) {
1064             NAMEDPIPE_THROW(0,
1065                             "Named pipe \"" + m_PipeName
1066                             + '"' + string(m_LSocket
1067                                            ? " not connected"
1068                                            : " closed"));
1069         }
1070         if ( !count ) {
1071             return eIO_Success;
1072         }
1073 
1074         _VERIFY(SOCK_SetTimeout(m_IoSocket, eIO_Read, timeout) == eIO_Success);
1075         status = SOCK_Read(m_IoSocket, buf, count, n_read,
1076                            eIO_ReadPlain);
1077         if (status != eIO_Success) {
1078             NAMEDPIPE_THROW(0,
1079                             "Named pipe \"" + m_PipeName
1080                             + "\" read failed: "
1081                             + string(IO_StatusStr(status)));
1082         }
1083     }
1084     catch (string& what) {
1085         ERR_POST_X(14, s_FormatErrorMessage("Read", what));
1086     }
1087 
1088     return status;
1089 }
1090 
1091 
Write(const void * buf,size_t count,size_t * n_written,const STimeout * timeout)1092 EIO_Status CNamedPipeHandle::Write(const void* buf, size_t count,
1093                                    size_t* n_written, const STimeout* timeout)
1094 
1095 {
1096     _ASSERT(n_written  &&  !*n_written);
1097 
1098     EIO_Status status  = eIO_Unknown;
1099 
1100     try {
1101         if ( !m_IoSocket ) {
1102             NAMEDPIPE_THROW(0,
1103                             "Named pipe \"" + m_PipeName
1104                             + '"' + string(m_LSocket
1105                                            ? " not connected"
1106                                            : " closed"));
1107         }
1108         if ( !count ) {
1109             return eIO_Success;
1110         }
1111 
1112         _VERIFY(SOCK_SetTimeout(m_IoSocket, eIO_Write, timeout)== eIO_Success);
1113         status = SOCK_Write(m_IoSocket, buf, count, n_written,
1114                             eIO_WritePlain);
1115         if (status != eIO_Success) {
1116             NAMEDPIPE_THROW(0,
1117                             "Named pipe \"" + m_PipeName
1118                             + "\" write failed: "
1119                             + string(IO_StatusStr(status)));
1120         }
1121     }
1122     catch (string& what) {
1123         ERR_POST_X(15, s_FormatErrorMessage("Write", what));
1124     }
1125 
1126     return status;
1127 }
1128 
1129 
Wait(EIO_Event event,const STimeout * timeout)1130 EIO_Status CNamedPipeHandle::Wait(EIO_Event event, const STimeout* timeout)
1131 {
1132     if ( !m_IoSocket ) {
1133         ERR_POST_X(9, s_FormatErrorMessage("Wait",
1134                                            "Named pipe \"" + m_PipeName + '"'
1135                                            + (m_LSocket
1136                                               ? " not connected"
1137                                               : " closed")));
1138         return eIO_Unknown;
1139     }
1140     return SOCK_Wait(m_IoSocket, event, timeout);
1141 }
1142 
1143 
Status(EIO_Event direction) const1144 EIO_Status CNamedPipeHandle::Status(EIO_Event direction) const
1145 {
1146     return !m_IoSocket ? eIO_Closed : SOCK_Status(m_IoSocket, direction);
1147 }
1148 
1149 
x_SetSocketBufSize(int sock,size_t bufsize,int dir)1150 bool CNamedPipeHandle::x_SetSocketBufSize(int sock, size_t bufsize, int dir)
1151 {
1152     int            bs_old = 0;
1153     int            bs_new = (int) bufsize;
1154     SOCK_socklen_t bs_len = (SOCK_socklen_t) sizeof(bs_old);
1155 
1156     if (::getsockopt(sock, SOL_SOCKET, dir, &bs_old, &bs_len) == 0
1157         &&  bs_new > bs_old) {
1158         if (::setsockopt(sock, SOL_SOCKET, dir, &bs_new, bs_len) != 0) {
1159             return false;
1160         }
1161     }
1162     return true;
1163 }
1164 
1165 
1166 #endif  /* NCBI_OS_UNIX | NCBI_OS_MSWIN */
1167 
1168 
1169 
1170 //////////////////////////////////////////////////////////////////////////////
1171 //
1172 // CNamedPipe
1173 //
1174 
1175 
CNamedPipe(size_t pipesize)1176 CNamedPipe::CNamedPipe(size_t pipesize)
1177     : m_PipeSize(pipesize),
1178       m_OpenTimeout(0), m_ReadTimeout(0), m_WriteTimeout(0)
1179 {
1180     m_NamedPipeHandle = new CNamedPipeHandle;
1181 }
1182 
1183 
~CNamedPipe()1184 CNamedPipe::~CNamedPipe()
1185 {
1186     Close();
1187     delete m_NamedPipeHandle;
1188 }
1189 
1190 
Close(void)1191 EIO_Status CNamedPipe::Close(void)
1192 {
1193     _ASSERT(m_NamedPipeHandle);
1194     return m_NamedPipeHandle->Close();
1195 }
1196 
1197 
Read(void * buf,size_t count,size_t * n_read)1198 EIO_Status CNamedPipe::Read(void* buf, size_t count, size_t* n_read)
1199 {
1200     _ASSERT(m_NamedPipeHandle);
1201     size_t x_read;
1202     if ( !n_read ) {
1203         n_read = &x_read;
1204     }
1205     *n_read = 0;
1206     if (count  &&  !buf) {
1207         return eIO_InvalidArg;
1208     }
1209     return m_NamedPipeHandle->Read(buf, count, n_read, m_ReadTimeout);
1210 }
1211 
1212 
Write(const void * buf,size_t count,size_t * n_written)1213 EIO_Status CNamedPipe::Write(const void* buf, size_t count, size_t* n_written)
1214 {
1215     _ASSERT(m_NamedPipeHandle);
1216     size_t x_written;
1217     if ( !n_written ) {
1218         n_written = &x_written;
1219     }
1220     *n_written = 0;
1221     if (count  &&  !buf) {
1222         return eIO_InvalidArg;
1223     }
1224     return m_NamedPipeHandle->Write(buf, count, n_written, m_WriteTimeout);
1225 }
1226 
1227 
Wait(EIO_Event event,const STimeout * timeout)1228 EIO_Status CNamedPipe::Wait(EIO_Event event, const STimeout* timeout)
1229 {
1230     _ASSERT(m_NamedPipeHandle);
1231     if (timeout == kDefaultTimeout) {
1232         _TROUBLE;
1233         return eIO_InvalidArg;
1234     }
1235     switch (event) {
1236     case eIO_Read:
1237     case eIO_Write:
1238     case eIO_ReadWrite:
1239         break;
1240     default:
1241         _TROUBLE;
1242         return eIO_InvalidArg;
1243     }
1244     return m_NamedPipeHandle->Wait(event, timeout);
1245 }
1246 
1247 
Status(EIO_Event direction) const1248 EIO_Status CNamedPipe::Status(EIO_Event direction) const
1249 {
1250     _ASSERT(m_NamedPipeHandle);
1251     switch (direction) {
1252     case eIO_Read:
1253     case eIO_Write:
1254         break;
1255     default:
1256         _TROUBLE;
1257         return eIO_InvalidArg;
1258     }
1259     return m_NamedPipeHandle->Status(direction);
1260 }
1261 
1262 
SetTimeout(EIO_Event event,const STimeout * timeout)1263 EIO_Status CNamedPipe::SetTimeout(EIO_Event event, const STimeout* timeout)
1264 {
1265     _ASSERT(m_NamedPipeHandle);
1266     if (timeout == kDefaultTimeout) {
1267         return eIO_Success;
1268     }
1269     switch ( event ) {
1270     case eIO_Open:
1271         m_OpenTimeout  = s_SetTimeout(timeout, &m_OpenTimeoutValue);
1272         break;
1273     case eIO_Read:
1274         m_ReadTimeout  = s_SetTimeout(timeout, &m_ReadTimeoutValue);
1275         break;
1276     case eIO_Write:
1277         m_WriteTimeout = s_SetTimeout(timeout, &m_WriteTimeoutValue);
1278         break;
1279     case eIO_ReadWrite:
1280         m_ReadTimeout  = s_SetTimeout(timeout, &m_ReadTimeoutValue);
1281         m_WriteTimeout = s_SetTimeout(timeout, &m_WriteTimeoutValue);
1282         break;
1283     default:
1284         _TROUBLE;
1285         return eIO_InvalidArg;
1286     }
1287     return eIO_Success;
1288 }
1289 
1290 
GetTimeout(EIO_Event event) const1291 const STimeout* CNamedPipe::GetTimeout(EIO_Event event) const
1292 {
1293     _ASSERT(m_NamedPipeHandle);
1294     switch ( event ) {
1295     case eIO_Open:
1296         return m_OpenTimeout;
1297     case eIO_Read:
1298         return m_ReadTimeout;
1299     case eIO_Write:
1300         return m_WriteTimeout;
1301     default:
1302         _TROUBLE;
1303         break;
1304     }
1305     return kDefaultTimeout;
1306 }
1307 
1308 
x_SetName(const string & pipename)1309 void CNamedPipe::x_SetName(const string& pipename)
1310 {
1311 #ifdef NCBI_OS_MSWIN
1312     static const char kSeparators[] = ":/\\";
1313 #else
1314     static const char kSeparators[] = "/";
1315 #endif
1316     if (pipename.find_first_of(kSeparators) != NPOS) {
1317         m_PipeName = pipename;
1318         return;
1319     }
1320 
1321 #if defined(NCBI_OS_MSWIN)
1322     m_PipeName = "\\\\.\\pipe\\" + pipename;
1323 #elif defined(NCBI_OS_UNIX)
1324     struct stat st;
1325 
1326     const char* pipedir = "/var/tmp";
1327     if (::stat(pipedir, &st) != 0  ||  !S_ISDIR(st.st_mode)
1328         ||  ::access(pipedir, W_OK) != 0) {
1329         pipedir = "/tmp";
1330         if (::stat(pipedir, &st) != 0  ||  !S_ISDIR(st.st_mode)
1331             ||  ::access(pipedir, W_OK) != 0) {
1332             pipedir = ".";
1333         }
1334     }
1335     m_PipeName = string(pipedir) + '/' + pipename;
1336 #else
1337     m_PipeName = pipename;
1338 #endif
1339 }
1340 
1341 
1342 
1343 //////////////////////////////////////////////////////////////////////////////
1344 //
1345 // CNamedPipeClient
1346 //
1347 
CNamedPipeClient(size_t pipesize)1348 CNamedPipeClient::CNamedPipeClient(size_t pipesize)
1349     : CNamedPipe(pipesize)
1350 {
1351     _ASSERT(m_NamedPipeHandle);
1352     m_IsClientSide = true;
1353 }
1354 
1355 
CNamedPipeClient(const string & pipename,const STimeout * timeout,size_t pipesize,CNamedPipeClient::TFlags flags)1356 CNamedPipeClient::CNamedPipeClient(const string&            pipename,
1357                                    const STimeout*          timeout,
1358                                    size_t                   pipesize,
1359                                    CNamedPipeClient::TFlags flags)
1360     : CNamedPipe(pipesize)
1361 {
1362     m_IsClientSide = true;
1363     Open(pipename, timeout, flags);
1364 }
1365 
1366 
Open(const string & pipename,const STimeout * timeout,size_t pipesize,CNamedPipeClient::TFlags flags)1367 EIO_Status CNamedPipeClient::Open(const string&            pipename,
1368                                   const STimeout*          timeout,
1369                                   size_t                   pipesize,
1370                                   CNamedPipeClient::TFlags flags)
1371 {
1372     _ASSERT(m_NamedPipeHandle  &&  m_IsClientSide);
1373     if (pipesize) {
1374         m_PipeSize = pipesize;
1375     }
1376     x_SetName(pipename);
1377 
1378     SetTimeout(eIO_Open, timeout);
1379     return m_NamedPipeHandle->Open(m_PipeName, m_OpenTimeout,
1380                                    m_PipeSize, flags);
1381 }
1382 
1383 
1384 
1385 //////////////////////////////////////////////////////////////////////////////
1386 //
1387 // CNamedPipeServer
1388 //
1389 
1390 
CNamedPipeServer(size_t pipesize)1391 CNamedPipeServer::CNamedPipeServer(size_t pipesize)
1392     : CNamedPipe(pipesize)
1393 {
1394     _ASSERT(m_NamedPipeHandle);
1395     m_IsClientSide = false;
1396 }
1397 
1398 
CNamedPipeServer(const string & pipename,const STimeout * timeout,size_t pipesize)1399 CNamedPipeServer::CNamedPipeServer(const string&   pipename,
1400                                    const STimeout* timeout,
1401                                    size_t          pipesize)
1402     : CNamedPipe(pipesize)
1403 {
1404     m_IsClientSide = false;
1405     Create(pipename, timeout);
1406 }
1407 
1408 
Create(const string & pipename,const STimeout * timeout,size_t pipesize)1409 EIO_Status CNamedPipeServer::Create(const string&   pipename,
1410                                     const STimeout* timeout,
1411                                     size_t          pipesize)
1412 {
1413     _ASSERT(m_NamedPipeHandle  &&  !m_IsClientSide);
1414     if (pipesize) {
1415         m_PipeSize = pipesize;
1416     }
1417     x_SetName(pipename);
1418 
1419     SetTimeout(eIO_Open, timeout);
1420     return m_NamedPipeHandle->Create(m_PipeName, m_PipeSize);
1421 }
1422 
1423 
Listen(void)1424 EIO_Status CNamedPipeServer::Listen(void)
1425 {
1426     _ASSERT(m_NamedPipeHandle  &&  !m_IsClientSide);
1427     return m_NamedPipeHandle->Listen(m_OpenTimeout);
1428 }
1429 
1430 
Disconnect(void)1431 EIO_Status CNamedPipeServer::Disconnect(void)
1432 {
1433     _ASSERT(m_NamedPipeHandle  &&  !m_IsClientSide);
1434     return m_NamedPipeHandle->Disconnect();
1435 }
1436 
1437 
1438 END_NCBI_SCOPE
1439