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