VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/localipc-win.cpp@ 81106

Last change on this file since 81106 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.1 KB
Line 
1/* $Id: localipc-win.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - Local IPC, Windows Implementation Using Named Pipes.
4 */
5
6/*
7 * Copyright (C) 2008-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_LOCALIPC
32/*
33 * We have to force NT 5.0 here because of
34 * ConvertStringSecurityDescriptorToSecurityDescriptor. Note that because of
35 * FILE_FLAG_FIRST_PIPE_INSTANCE this code actually requires W2K SP2+.
36 */
37#ifndef _WIN32_WINNT
38# define _WIN32_WINNT 0x0500 /* for ConvertStringSecurityDescriptorToSecurityDescriptor */
39#elif _WIN32_WINNT < 0x0500
40# undef _WIN32_WINNT
41# define _WIN32_WINNT 0x0500
42#endif
43#define UNICODE /* For the SDDL_ strings. */
44#include <iprt/win/windows.h>
45#include <sddl.h>
46
47#include "internal/iprt.h"
48#include <iprt/localipc.h>
49
50#include <iprt/asm.h>
51#include <iprt/assert.h>
52#include <iprt/critsect.h>
53#include <iprt/ctype.h>
54#include <iprt/err.h>
55#include <iprt/ldr.h>
56#include <iprt/log.h>
57#include <iprt/mem.h>
58#include <iprt/param.h>
59#include <iprt/string.h>
60#include <iprt/thread.h>
61#include <iprt/time.h>
62#include <iprt/utf16.h>
63
64#include "internal/magics.h"
65#include "internal-r3-win.h"
66
67
68
69/*********************************************************************************************************************************
70* Defined Constants And Macros *
71*********************************************************************************************************************************/
72/** Pipe prefix string. */
73#define RTLOCALIPC_WIN_PREFIX L"\\\\.\\pipe\\IPRT-"
74
75/** DACL for block all network access and local users other than the creator/owner.
76 *
77 * ACE format: (ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid)
78 *
79 * Note! FILE_GENERIC_WRITE (SDDL_FILE_WRITE) is evil here because it includes
80 * the FILE_CREATE_PIPE_INSTANCE(=FILE_APPEND_DATA) flag. Thus the hardcoded
81 * value 0x0012019b in the client ACE. The server-side still needs
82 * setting FILE_CREATE_PIPE_INSTANCE although.
83 * It expands to:
84 * 0x00000001 - FILE_READ_DATA
85 * 0x00000008 - FILE_READ_EA
86 * 0x00000080 - FILE_READ_ATTRIBUTES
87 * 0x00020000 - READ_CONTROL
88 * 0x00100000 - SYNCHRONIZE
89 * 0x00000002 - FILE_WRITE_DATA
90 * 0x00000010 - FILE_WRITE_EA
91 * 0x00000100 - FILE_WRITE_ATTRIBUTES
92 * = 0x0012019b (client)
93 * + (only for server):
94 * 0x00000004 - FILE_CREATE_PIPE_INSTANCE
95 * = 0x0012019f
96 *
97 * @todo Triple check this!
98 * @todo EVERYONE -> AUTHENTICATED USERS or something more appropriate?
99 * @todo Have trouble allowing the owner FILE_CREATE_PIPE_INSTANCE access, so for now I'm hacking
100 * it just to get progress - the service runs as local system.
101 * The CREATOR OWNER and PERSONAL SELF works (the former is only involved in inheriting
102 * it seems, which is why it won't work. The latter I've no idea about. Perhaps the solution
103 * is to go the annoying route of OpenProcessToken, QueryTokenInformation,
104 * ConvertSidToStringSid and then use the result... Suggestions are very welcome
105 */
106#define RTLOCALIPC_WIN_SDDL_BASE \
107 SDDL_DACL SDDL_DELIMINATOR \
108 SDDL_ACE_BEGIN SDDL_ACCESS_DENIED L";;" SDDL_GENERIC_ALL L";;;" SDDL_NETWORK SDDL_ACE_END \
109 SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" SDDL_FILE_ALL L";;;" SDDL_LOCAL_SYSTEM SDDL_ACE_END
110
111#define RTLOCALIPC_WIN_SDDL_SERVER \
112 RTLOCALIPC_WIN_SDDL_BASE \
113 SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019f" L";;;" SDDL_EVERYONE SDDL_ACE_END
114
115#define RTLOCALIPC_WIN_SDDL_CLIENT \
116 RTLOCALIPC_WIN_SDDL_BASE \
117 SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019b" L";;;" SDDL_EVERYONE SDDL_ACE_END
118
119// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" SDDL_GENERIC_ALL L";;;" SDDL_PERSONAL_SELF SDDL_ACE_END \
120// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";CIOI;" SDDL_GENERIC_ALL L";;;" SDDL_CREATOR_OWNER SDDL_ACE_END
121// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019b" L";;;" SDDL_EVERYONE SDDL_ACE_END
122// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" SDDL_FILE_ALL L";;;" SDDL_LOCAL_SYSTEM SDDL_ACE_END
123
124
125/*********************************************************************************************************************************
126* Structures and Typedefs *
127*********************************************************************************************************************************/
128/**
129 * Local IPC service instance, Windows.
130 */
131typedef struct RTLOCALIPCSERVERINT
132{
133 /** The magic (RTLOCALIPCSERVER_MAGIC). */
134 uint32_t u32Magic;
135 /** The creation flags. */
136 uint32_t fFlags;
137 /** Critical section protecting the structure. */
138 RTCRITSECT CritSect;
139 /** The number of references to the instance.
140 * @remarks The reference counting isn't race proof. */
141 uint32_t volatile cRefs;
142 /** Indicates that there is a pending cancel request. */
143 bool volatile fCancelled;
144 /** The named pipe handle. */
145 HANDLE hNmPipe;
146 /** The handle to the event object we're using for overlapped I/O. */
147 HANDLE hEvent;
148 /** The overlapped I/O structure. */
149 OVERLAPPED OverlappedIO;
150 /** The full pipe name (variable length). */
151 RTUTF16 wszName[1];
152} RTLOCALIPCSERVERINT;
153/** Pointer to a local IPC server instance (Windows). */
154typedef RTLOCALIPCSERVERINT *PRTLOCALIPCSERVERINT;
155
156
157/**
158 * Local IPC session instance, Windows.
159 *
160 * This is a named pipe and we should probably merge the pipe code with this to
161 * save work and code duplication.
162 */
163typedef struct RTLOCALIPCSESSIONINT
164{
165 /** The magic (RTLOCALIPCSESSION_MAGIC). */
166 uint32_t u32Magic;
167 /** Critical section protecting the structure. */
168 RTCRITSECT CritSect;
169 /** The number of references to the instance.
170 * @remarks The reference counting isn't race proof. */
171 uint32_t volatile cRefs;
172 /** Set if the zero byte read that the poll code using is pending. */
173 bool fZeroByteRead;
174 /** Indicates that there is a pending cancel request. */
175 bool volatile fCancelled;
176 /** Set if this is the server side, clear if the client. */
177 bool fServerSide;
178 /** The named pipe handle. */
179 HANDLE hNmPipe;
180 struct
181 {
182 RTTHREAD hActiveThread;
183 /** The handle to the event object we're using for overlapped I/O. */
184 HANDLE hEvent;
185 /** The overlapped I/O structure. */
186 OVERLAPPED OverlappedIO;
187 }
188 /** Overlapped reads. */
189 Read,
190 /** Overlapped writes. */
191 Write;
192#if 0 /* Non-blocking writes are not yet supported. */
193 /** Bounce buffer for writes. */
194 uint8_t *pbBounceBuf;
195 /** Amount of used buffer space. */
196 size_t cbBounceBufUsed;
197 /** Amount of allocated buffer space. */
198 size_t cbBounceBufAlloc;
199#endif
200 /** Buffer for the zero byte read.
201 * Used in RTLocalIpcSessionWaitForData(). */
202 uint8_t abBuf[8];
203} RTLOCALIPCSESSIONINT;
204/** Pointer to a local IPC session instance (Windows). */
205typedef RTLOCALIPCSESSIONINT *PRTLOCALIPCSESSIONINT;
206
207
208/*********************************************************************************************************************************
209* Internal Functions *
210*********************************************************************************************************************************/
211static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSIONINT *ppSession, HANDLE hNmPipeSession);
212
213
214/*********************************************************************************************************************************
215* Global Variables *
216*********************************************************************************************************************************/
217static bool volatile g_fResolvedApis = false;
218/** advapi32.dll API ConvertStringSecurityDescriptorToSecurityDescriptorW. */
219static decltype(ConvertStringSecurityDescriptorToSecurityDescriptorW) *g_pfnSSDLToSecDescW = NULL;
220
221
222/**
223 * Builds and allocates the security descriptor required for securing the local pipe.
224 *
225 * @return IPRT status code.
226 * @param ppDesc Where to store the allocated security descriptor on success.
227 * Must be free'd using LocalFree().
228 * @param fServer Whether it's for a server or client instance.
229 */
230static int rtLocalIpcServerWinAllocSecurityDescriptior(PSECURITY_DESCRIPTOR *ppDesc, bool fServer)
231{
232 /*
233 * Resolve the API the first time around.
234 */
235 if (!g_fResolvedApis)
236 {
237 g_pfnSSDLToSecDescW = (decltype(g_pfnSSDLToSecDescW))RTLdrGetSystemSymbol("advapi32.dll", "ConvertStringSecurityDescriptorToSecurityDescriptorW");
238 ASMCompilerBarrier();
239 g_fResolvedApis = true;
240 }
241
242 int rc;
243 PSECURITY_DESCRIPTOR pSecDesc = NULL;
244 if (g_pfnSSDLToSecDescW)
245 {
246 /*
247 * We'll create a security descriptor from a SDDL that denies
248 * access to network clients (this is local IPC after all), it
249 * makes some further restrictions to prevent non-authenticated
250 * users from screwing around.
251 */
252 PCRTUTF16 pwszSDDL = fServer ? RTLOCALIPC_WIN_SDDL_SERVER : RTLOCALIPC_WIN_SDDL_CLIENT;
253 if (g_pfnSSDLToSecDescW(pwszSDDL, SDDL_REVISION_1, &pSecDesc, NULL))
254 {
255 AssertPtr(pSecDesc);
256 *ppDesc = pSecDesc;
257 return VINF_SUCCESS;
258 }
259
260 rc = RTErrConvertFromWin32(GetLastError());
261 }
262 else
263 {
264 /* Windows OSes < W2K SP2 not supported for now, bail out. */
265 /** @todo Implement me! */
266 rc = VERR_NOT_SUPPORTED;
267 }
268 return rc;
269}
270
271
272/**
273 * Creates a named pipe instance.
274 *
275 * This is used by both RTLocalIpcServerCreate and RTLocalIpcServerListen.
276 *
277 * @return IPRT status code.
278 * @param phNmPipe Where to store the named pipe handle on success.
279 * This will be set to INVALID_HANDLE_VALUE on failure.
280 * @param pwszPipeName The named pipe name, full, UTF-16 encoded.
281 * @param fFirst Set on the first call (from RTLocalIpcServerCreate),
282 * otherwise clear. Governs the
283 * FILE_FLAG_FIRST_PIPE_INSTANCE flag.
284 */
285static int rtLocalIpcServerWinCreatePipeInstance(PHANDLE phNmPipe, PCRTUTF16 pwszPipeName, bool fFirst)
286{
287 *phNmPipe = INVALID_HANDLE_VALUE;
288
289 PSECURITY_DESCRIPTOR pSecDesc;
290 int rc = rtLocalIpcServerWinAllocSecurityDescriptior(&pSecDesc, fFirst /* Server? */);
291 if (RT_SUCCESS(rc))
292 {
293 SECURITY_ATTRIBUTES SecAttrs;
294 SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
295 SecAttrs.lpSecurityDescriptor = pSecDesc;
296 SecAttrs.bInheritHandle = FALSE;
297
298 DWORD fOpenMode = PIPE_ACCESS_DUPLEX
299 | PIPE_WAIT
300 | FILE_FLAG_OVERLAPPED;
301 if ( fFirst
302 && ( g_enmWinVer >= kRTWinOSType_XP
303 || ( g_enmWinVer == kRTWinOSType_2K
304 && g_WinOsInfoEx.wServicePackMajor >= 2) ) )
305 fOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE; /* Introduced with W2K SP2 */
306
307 HANDLE hNmPipe = CreateNamedPipeW(pwszPipeName, /* lpName */
308 fOpenMode, /* dwOpenMode */
309 PIPE_TYPE_BYTE, /* dwPipeMode */
310 PIPE_UNLIMITED_INSTANCES, /* nMaxInstances */
311 PAGE_SIZE, /* nOutBufferSize (advisory) */
312 PAGE_SIZE, /* nInBufferSize (ditto) */
313 30*1000, /* nDefaultTimeOut = 30 sec */
314 &SecAttrs); /* lpSecurityAttributes */
315 LocalFree(pSecDesc);
316 if (hNmPipe != INVALID_HANDLE_VALUE)
317 *phNmPipe = hNmPipe;
318 else
319 rc = RTErrConvertFromWin32(GetLastError());
320 }
321
322 return rc;
323}
324
325
326/**
327 * Validates the user specified name.
328 *
329 * @returns IPRT status code.
330 * @param pszName The name to validate.
331 * @param pcwcFullName Where to return the UTF-16 length of the full name.
332 * @param fNative Whether it's a native name or a portable name.
333 */
334static int rtLocalIpcWinValidateName(const char *pszName, size_t *pcwcFullName, bool fNative)
335{
336 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
337 AssertReturn(*pszName, VERR_INVALID_NAME);
338
339 if (!fNative)
340 {
341 size_t cwcName = RT_ELEMENTS(RTLOCALIPC_WIN_PREFIX) - 1;
342 for (;;)
343 {
344 char ch = *pszName++;
345 if (!ch)
346 break;
347 AssertReturn(!RT_C_IS_CNTRL(ch), VERR_INVALID_NAME);
348 AssertReturn((unsigned)ch < 0x80, VERR_INVALID_NAME);
349 AssertReturn(ch != '\\', VERR_INVALID_NAME);
350 AssertReturn(ch != '/', VERR_INVALID_NAME);
351 cwcName++;
352 }
353 *pcwcFullName = cwcName;
354 }
355 else
356 {
357 int rc = RTStrCalcUtf16LenEx(pszName, RTSTR_MAX, pcwcFullName);
358 AssertRCReturn(rc, rc);
359 }
360
361 return VINF_SUCCESS;
362}
363
364
365/**
366 * Constructs the full pipe name as UTF-16.
367 *
368 * @returns IPRT status code.
369 * @param pszName The user supplied name. ASSUMES reasonable length
370 * for now, so no long path prefixing needed.
371 * @param pwszFullName The output buffer.
372 * @param cwcFullName The output buffer size excluding the terminator.
373 * @param fNative Whether the user supplied name is a native or
374 * portable one.
375 */
376static int rtLocalIpcWinConstructName(const char *pszName, PRTUTF16 pwszFullName, size_t cwcFullName, bool fNative)
377{
378 if (!fNative)
379 {
380 static RTUTF16 const s_wszPrefix[] = RTLOCALIPC_WIN_PREFIX;
381 Assert(cwcFullName * sizeof(RTUTF16) > sizeof(s_wszPrefix));
382 memcpy(pwszFullName, s_wszPrefix, sizeof(s_wszPrefix));
383 cwcFullName -= RT_ELEMENTS(s_wszPrefix) - 1;
384 pwszFullName += RT_ELEMENTS(s_wszPrefix) - 1;
385 }
386 return RTStrToUtf16Ex(pszName, RTSTR_MAX, &pwszFullName, cwcFullName + 1, NULL);
387}
388
389
390RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszName, uint32_t fFlags)
391{
392 /*
393 * Validate parameters.
394 */
395 AssertPtrReturn(phServer, VERR_INVALID_POINTER);
396 *phServer = NIL_RTLOCALIPCSERVER;
397 AssertReturn(!(fFlags & ~RTLOCALIPC_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
398 size_t cwcFullName;
399 int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
400 if (RT_SUCCESS(rc))
401 {
402 /*
403 * Allocate and initialize the instance data.
404 */
405 size_t cbThis = RT_UOFFSETOF_DYN(RTLOCALIPCSERVERINT, wszName[cwcFullName + 1]);
406 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)RTMemAllocVar(cbThis);
407 AssertReturn(pThis, VERR_NO_MEMORY);
408
409 pThis->u32Magic = RTLOCALIPCSERVER_MAGIC;
410 pThis->cRefs = 1; /* the one we return */
411 pThis->fCancelled = false;
412
413 rc = rtLocalIpcWinConstructName(pszName, pThis->wszName, cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
414 if (RT_SUCCESS(rc))
415 {
416 rc = RTCritSectInit(&pThis->CritSect);
417 if (RT_SUCCESS(rc))
418 {
419 pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
420 FALSE /*bInitialState*/, NULL /*lpName*/);
421 if (pThis->hEvent != NULL)
422 {
423 RT_ZERO(pThis->OverlappedIO);
424 pThis->OverlappedIO.Internal = STATUS_PENDING;
425 pThis->OverlappedIO.hEvent = pThis->hEvent;
426
427 rc = rtLocalIpcServerWinCreatePipeInstance(&pThis->hNmPipe, pThis->wszName, true /* fFirst */);
428 if (RT_SUCCESS(rc))
429 {
430 *phServer = pThis;
431 return VINF_SUCCESS;
432 }
433
434 BOOL fRc = CloseHandle(pThis->hEvent);
435 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
436 }
437 else
438 rc = RTErrConvertFromWin32(GetLastError());
439
440 int rc2 = RTCritSectDelete(&pThis->CritSect);
441 AssertRC(rc2);
442 }
443 }
444 RTMemFree(pThis);
445 }
446 return rc;
447}
448
449
450/**
451 * Retains a reference to the server instance.
452 *
453 * @returns
454 * @param pThis The server instance.
455 */
456DECLINLINE(void) rtLocalIpcServerRetain(PRTLOCALIPCSERVERINT pThis)
457{
458 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
459 Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs);
460}
461
462
463/**
464 * Call when the reference count reaches 0.
465 *
466 * Caller owns the critsect.
467 *
468 * @returns VINF_OBJECT_DESTROYED
469 * @param pThis The instance to destroy.
470 */
471DECL_NO_INLINE(static, int) rtLocalIpcServerWinDestroy(PRTLOCALIPCSERVERINT pThis)
472{
473 Assert(pThis->u32Magic == ~RTLOCALIPCSERVER_MAGIC);
474 pThis->u32Magic = ~RTLOCALIPCSERVER_MAGIC;
475
476 BOOL fRc = CloseHandle(pThis->hNmPipe);
477 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
478 pThis->hNmPipe = INVALID_HANDLE_VALUE;
479
480 fRc = CloseHandle(pThis->hEvent);
481 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
482 pThis->hEvent = NULL;
483
484 RTCritSectLeave(&pThis->CritSect);
485 RTCritSectDelete(&pThis->CritSect);
486
487 RTMemFree(pThis);
488 return VINF_OBJECT_DESTROYED;
489}
490
491
492/**
493 * Server instance destructor.
494 *
495 * @returns VINF_OBJECT_DESTROYED
496 * @param pThis The server instance.
497 */
498DECL_NO_INLINE(static, int) rtLocalIpcServerDtor(PRTLOCALIPCSERVERINT pThis)
499{
500 RTCritSectEnter(&pThis->CritSect);
501 return rtLocalIpcServerWinDestroy(pThis);
502}
503
504
505/**
506 * Releases a reference to the server instance.
507 *
508 * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
509 * @param pThis The server instance.
510 */
511DECLINLINE(int) rtLocalIpcServerRelease(PRTLOCALIPCSERVERINT pThis)
512{
513 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
514 Assert(cRefs < UINT32_MAX / 2);
515 if (!cRefs)
516 return rtLocalIpcServerDtor(pThis);
517 return VINF_SUCCESS;
518}
519
520
521/**
522 * Releases a reference to the server instance and leaves the critsect.
523 *
524 * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
525 * @param pThis The server instance.
526 */
527DECLINLINE(int) rtLocalIpcServerReleaseAndUnlock(PRTLOCALIPCSERVERINT pThis)
528{
529 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
530 Assert(cRefs < UINT32_MAX / 2);
531 if (!cRefs)
532 return rtLocalIpcServerWinDestroy(pThis);
533 return RTCritSectLeave(&pThis->CritSect);
534}
535
536
537
538RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer)
539{
540 /*
541 * Validate input.
542 */
543 if (hServer == NIL_RTLOCALIPCSERVER)
544 return VINF_SUCCESS;
545 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
546 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
547 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
548
549 /*
550 * Cancel any thread currently busy using the server,
551 * leaving the cleanup to it.
552 */
553 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC, RTLOCALIPCSERVER_MAGIC), VERR_WRONG_ORDER);
554
555 RTCritSectEnter(&pThis->CritSect);
556
557 /* Cancel everything. */
558 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
559 if (pThis->cRefs > 1)
560 {
561 BOOL fRc = SetEvent(pThis->hEvent);
562 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
563 }
564
565 return rtLocalIpcServerReleaseAndUnlock(pThis);
566}
567
568
569RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION phClientSession)
570{
571 /*
572 * Validate input.
573 */
574 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
575 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
576 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
577 AssertPtrReturn(phClientSession, VERR_INVALID_POINTER);
578
579 /*
580 * Enter the critsect before inspecting the object further.
581 */
582 int rc = RTCritSectEnter(&pThis->CritSect);
583 AssertRCReturn(rc, rc);
584
585 rtLocalIpcServerRetain(pThis);
586 if (!pThis->fCancelled)
587 {
588 ResetEvent(pThis->hEvent);
589
590 RTCritSectLeave(&pThis->CritSect);
591
592 /*
593 * Try connect a client. We need to use overlapped I/O here because
594 * of the cancellation done by RTLocalIpcServerCancel and RTLocalIpcServerDestroy.
595 */
596 SetLastError(NO_ERROR);
597 BOOL fRc = ConnectNamedPipe(pThis->hNmPipe, &pThis->OverlappedIO);
598 DWORD dwErr = fRc ? NO_ERROR : GetLastError();
599 if ( !fRc
600 && dwErr == ERROR_IO_PENDING)
601 {
602 WaitForSingleObject(pThis->hEvent, INFINITE);
603 DWORD dwIgnored;
604 fRc = GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &dwIgnored, FALSE /* bWait*/);
605 dwErr = fRc ? NO_ERROR : GetLastError();
606 }
607
608 RTCritSectEnter(&pThis->CritSect);
609 if ( !pThis->fCancelled /* Event signalled but not cancelled? */
610 && pThis->u32Magic == RTLOCALIPCSERVER_MAGIC)
611 {
612 /*
613 * Still alive, some error or an actual client.
614 *
615 * If it's the latter we'll have to create a new pipe instance that
616 * replaces the current one for the server. The current pipe instance
617 * will be assigned to the client session.
618 */
619 if ( fRc
620 || dwErr == ERROR_PIPE_CONNECTED)
621 {
622 HANDLE hNmPipe;
623 rc = rtLocalIpcServerWinCreatePipeInstance(&hNmPipe, pThis->wszName, false /* fFirst */);
624 if (RT_SUCCESS(rc))
625 {
626 HANDLE hNmPipeSession = pThis->hNmPipe; /* consumed */
627 pThis->hNmPipe = hNmPipe;
628 rc = rtLocalIpcWinCreateSession(phClientSession, hNmPipeSession);
629 }
630 else
631 {
632 /*
633 * We failed to create a new instance for the server, disconnect
634 * the client and fail. Don't try service the client here.
635 */
636 fRc = DisconnectNamedPipe(pThis->hNmPipe);
637 AssertMsg(fRc, ("%d\n", GetLastError()));
638 }
639 }
640 else
641 rc = RTErrConvertFromWin32(dwErr);
642 }
643 else
644 {
645 /*
646 * Cancelled.
647 *
648 * Cancel the overlapped io if it didn't complete (must be done
649 * in the this thread) or disconnect the client.
650 */
651 Assert(pThis->fCancelled);
652 if ( fRc
653 || dwErr == ERROR_PIPE_CONNECTED)
654 fRc = DisconnectNamedPipe(pThis->hNmPipe);
655 else if (dwErr == ERROR_IO_PENDING)
656 fRc = CancelIo(pThis->hNmPipe);
657 else
658 fRc = TRUE;
659 AssertMsg(fRc, ("%d\n", GetLastError()));
660 rc = VERR_CANCELLED;
661 }
662 }
663 else
664 {
665 /*pThis->fCancelled = false; - Terrible interface idea. Add API to clear fCancelled if ever required. */
666 rc = VERR_CANCELLED;
667 }
668 rtLocalIpcServerReleaseAndUnlock(pThis);
669 return rc;
670}
671
672
673RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer)
674{
675 /*
676 * Validate input.
677 */
678 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
679 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
680 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
681
682 /*
683 * Enter the critical section, then set the cancellation flag
684 * and signal the event (to wake up anyone in/at WaitForSingleObject).
685 */
686 rtLocalIpcServerRetain(pThis);
687 int rc = RTCritSectEnter(&pThis->CritSect);
688 if (RT_SUCCESS(rc))
689 {
690 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
691
692 BOOL fRc = SetEvent(pThis->hEvent);
693 if (fRc)
694 rc = VINF_SUCCESS;
695 else
696 {
697 DWORD dwErr = GetLastError();
698 AssertMsgFailed(("dwErr=%u\n", dwErr));
699 rc = RTErrConvertFromWin32(dwErr);
700 }
701
702 rtLocalIpcServerReleaseAndUnlock(pThis);
703 }
704 else
705 rtLocalIpcServerRelease(pThis);
706 return rc;
707}
708
709
710/**
711 * Create a session instance for a new server client or a client connect.
712 *
713 * @returns IPRT status code.
714 *
715 * @param ppSession Where to store the session handle on success.
716 * @param hNmPipeSession The named pipe handle if server calling,
717 * INVALID_HANDLE_VALUE if client connect. This will
718 * be consumed by this session, meaning on failure to
719 * create the session it will be closed.
720 */
721static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSIONINT *ppSession, HANDLE hNmPipeSession)
722{
723 AssertPtr(ppSession);
724
725 /*
726 * Allocate and initialize the session instance data.
727 */
728 int rc;
729 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pThis));
730 if (pThis)
731 {
732 pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
733 pThis->cRefs = 1; /* our ref */
734 pThis->fCancelled = false;
735 pThis->fZeroByteRead = false;
736 pThis->fServerSide = hNmPipeSession != INVALID_HANDLE_VALUE;
737 pThis->hNmPipe = hNmPipeSession;
738#if 0 /* Non-blocking writes are not yet supported. */
739 pThis->pbBounceBuf = NULL;
740 pThis->cbBounceBufAlloc = 0;
741 pThis->cbBounceBufUsed = 0;
742#endif
743 rc = RTCritSectInit(&pThis->CritSect);
744 if (RT_SUCCESS(rc))
745 {
746 pThis->Read.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
747 FALSE /*bInitialState*/, NULL /*lpName*/);
748 if (pThis->Read.hEvent != NULL)
749 {
750 pThis->Read.OverlappedIO.Internal = STATUS_PENDING;
751 pThis->Read.OverlappedIO.hEvent = pThis->Read.hEvent;
752 pThis->Read.hActiveThread = NIL_RTTHREAD;
753
754 pThis->Write.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
755 FALSE /*bInitialState*/, NULL /*lpName*/);
756 if (pThis->Write.hEvent != NULL)
757 {
758 pThis->Write.OverlappedIO.Internal = STATUS_PENDING;
759 pThis->Write.OverlappedIO.hEvent = pThis->Write.hEvent;
760 pThis->Write.hActiveThread = NIL_RTTHREAD;
761
762 *ppSession = pThis;
763 return VINF_SUCCESS;
764 }
765
766 CloseHandle(pThis->Read.hEvent);
767 }
768
769 /* bail out */
770 rc = RTErrConvertFromWin32(GetLastError());
771 RTCritSectDelete(&pThis->CritSect);
772 }
773 RTMemFree(pThis);
774 }
775 else
776 rc = VERR_NO_MEMORY;
777
778 if (hNmPipeSession != INVALID_HANDLE_VALUE)
779 {
780 BOOL fRc = CloseHandle(hNmPipeSession);
781 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
782 }
783 return rc;
784}
785
786
787RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags)
788{
789 /*
790 * Validate input.
791 */
792 AssertPtrReturn(phSession, VERR_INVALID_POINTER);
793 AssertReturn(!(fFlags & ~RTLOCALIPC_C_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
794
795 size_t cwcFullName;
796 int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
797 if (RT_SUCCESS(rc))
798 {
799 /*
800 * Create a session (shared with server client session creation).
801 */
802 PRTLOCALIPCSESSIONINT pThis;
803 rc = rtLocalIpcWinCreateSession(&pThis, INVALID_HANDLE_VALUE);
804 if (RT_SUCCESS(rc))
805 {
806 /*
807 * Try open the pipe.
808 */
809 PSECURITY_DESCRIPTOR pSecDesc;
810 rc = rtLocalIpcServerWinAllocSecurityDescriptior(&pSecDesc, false /*fServer*/);
811 if (RT_SUCCESS(rc))
812 {
813 PRTUTF16 pwszFullName = RTUtf16Alloc((cwcFullName + 1) * sizeof(RTUTF16));
814 if (pwszFullName)
815 rc = rtLocalIpcWinConstructName(pszName, pwszFullName, cwcFullName,
816 RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
817 else
818 rc = VERR_NO_UTF16_MEMORY;
819 if (RT_SUCCESS(rc))
820 {
821 SECURITY_ATTRIBUTES SecAttrs;
822 SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
823 SecAttrs.lpSecurityDescriptor = pSecDesc;
824 SecAttrs.bInheritHandle = FALSE;
825
826 HANDLE hPipe = CreateFileW(pwszFullName,
827 GENERIC_READ | GENERIC_WRITE,
828 0 /*no sharing*/,
829 &SecAttrs,
830 OPEN_EXISTING,
831 FILE_FLAG_OVERLAPPED,
832 NULL /*no template handle*/);
833 if (hPipe != INVALID_HANDLE_VALUE)
834 {
835 pThis->hNmPipe = hPipe;
836
837 LocalFree(pSecDesc);
838 RTUtf16Free(pwszFullName);
839
840 /*
841 * We're done!
842 */
843 *phSession = pThis;
844 return VINF_SUCCESS;
845 }
846
847 rc = RTErrConvertFromWin32(GetLastError());
848 }
849
850 RTUtf16Free(pwszFullName);
851 LocalFree(pSecDesc);
852 }
853
854 /* destroy the session handle. */
855 CloseHandle(pThis->Read.hEvent);
856 CloseHandle(pThis->Write.hEvent);
857 RTCritSectDelete(&pThis->CritSect);
858
859 RTMemFree(pThis);
860 }
861 }
862 return rc;
863}
864
865
866/**
867 * Cancells all pending I/O operations, forcing the methods to return with
868 * VERR_CANCELLED (unless they've got actual data to return).
869 *
870 * Used by RTLocalIpcSessionCancel and RTLocalIpcSessionClose.
871 *
872 * @returns IPRT status code.
873 * @param pThis The client session instance.
874 */
875static int rtLocalIpcWinCancel(PRTLOCALIPCSESSIONINT pThis)
876{
877 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
878
879 /*
880 * Call CancelIo since this call cancels both read and write oriented operations.
881 */
882 if ( pThis->fZeroByteRead
883 || pThis->Read.hActiveThread != NIL_RTTHREAD
884 || pThis->Write.hActiveThread != NIL_RTTHREAD)
885 CancelIo(pThis->hNmPipe);
886
887 /*
888 * Set both event semaphores.
889 */
890 BOOL fRc = SetEvent(pThis->Read.hEvent);
891 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
892 fRc = SetEvent(pThis->Write.hEvent);
893 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
894
895 return VINF_SUCCESS;
896}
897
898
899/**
900 * Retains a reference to the session instance.
901 *
902 * @param pThis The client session instance.
903 */
904DECLINLINE(void) rtLocalIpcSessionRetain(PRTLOCALIPCSESSIONINT pThis)
905{
906 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
907 Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs);
908}
909
910
911RTDECL(uint32_t) RTLocalIpcSessionRetain(RTLOCALIPCSESSION hSession)
912{
913 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
914 AssertPtrReturn(pThis, UINT32_MAX);
915 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
916
917 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
918 Assert(cRefs < UINT32_MAX / 2 && cRefs);
919 return cRefs;
920}
921
922
923/**
924 * Call when the reference count reaches 0.
925 *
926 * Caller owns the critsect.
927 *
928 * @returns VINF_OBJECT_DESTROYED
929 * @param pThis The instance to destroy.
930 */
931DECL_NO_INLINE(static, int) rtLocalIpcSessionWinDestroy(PRTLOCALIPCSESSIONINT pThis)
932{
933 BOOL fRc = CloseHandle(pThis->hNmPipe);
934 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
935 pThis->hNmPipe = INVALID_HANDLE_VALUE;
936
937 fRc = CloseHandle(pThis->Write.hEvent);
938 AssertMsg(fRc, ("%d\n", GetLastError()));
939 pThis->Write.hEvent = NULL;
940
941 fRc = CloseHandle(pThis->Read.hEvent);
942 AssertMsg(fRc, ("%d\n", GetLastError()));
943 pThis->Read.hEvent = NULL;
944
945 int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2);
946 RTCritSectDelete(&pThis->CritSect);
947
948 RTMemFree(pThis);
949 return VINF_OBJECT_DESTROYED;
950}
951
952
953/**
954 * Releases a reference to the session instance and unlock it.
955 *
956 * @returns VINF_SUCCESS or VINF_OBJECT_DESTROYED as appropriate.
957 * @param pThis The session instance.
958 */
959DECLINLINE(int) rtLocalIpcSessionReleaseAndUnlock(PRTLOCALIPCSESSIONINT pThis)
960{
961 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
962 Assert(cRefs < UINT32_MAX / 2);
963 if (!cRefs)
964 return rtLocalIpcSessionWinDestroy(pThis);
965
966 int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2);
967 Log(("rtLocalIpcSessionReleaseAndUnlock: %u refs left\n", cRefs));
968 return VINF_SUCCESS;
969}
970
971
972RTDECL(uint32_t) RTLocalIpcSessionRelease(RTLOCALIPCSESSION hSession)
973{
974 if (hSession == NIL_RTLOCALIPCSESSION)
975 return 0;
976
977 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
978 AssertPtrReturn(pThis, UINT32_MAX);
979 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
980
981 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
982 Assert(cRefs < UINT32_MAX / 2);
983 if (cRefs)
984 Log(("RTLocalIpcSessionRelease: %u refs left\n", cRefs));
985 else
986 {
987 RTCritSectEnter(&pThis->CritSect);
988 rtLocalIpcSessionWinDestroy(pThis);
989 }
990 return cRefs;
991}
992
993
994RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession)
995{
996 /*
997 * Validate input.
998 */
999 if (hSession == NIL_RTLOCALIPCSESSION)
1000 return VINF_SUCCESS;
1001 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1002 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1003 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1004
1005 /*
1006 * Invalidate the instance, cancel all outstanding I/O and drop our reference.
1007 */
1008 RTCritSectEnter(&pThis->CritSect);
1009 rtLocalIpcWinCancel(pThis);
1010 return rtLocalIpcSessionReleaseAndUnlock(pThis);
1011}
1012
1013
1014/**
1015 * Handles WaitForSingleObject return value when waiting for a zero byte read.
1016 *
1017 * The zero byte read is started by the RTLocalIpcSessionWaitForData method and
1018 * left pending when the function times out. This saves us the problem of
1019 * CancelIo messing with all active I/O operations and the trouble of restarting
1020 * the zero byte read the next time the method is called. However should
1021 * RTLocalIpcSessionRead be called after a failed RTLocalIpcSessionWaitForData
1022 * call, the zero byte read will still be pending and it must wait for it to
1023 * complete before the OVERLAPPEDIO structure can be reused.
1024 *
1025 * Thus, both functions will do WaitForSingleObject and share this routine to
1026 * handle the outcome.
1027 *
1028 * @returns IPRT status code.
1029 * @param pThis The session instance.
1030 * @param rcWait The WaitForSingleObject return code.
1031 */
1032static int rtLocalIpcWinGetZeroReadResult(PRTLOCALIPCSESSIONINT pThis, DWORD rcWait)
1033{
1034 int rc;
1035 DWORD cbRead = 42;
1036 if (rcWait == WAIT_OBJECT_0)
1037 {
1038 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, !pThis->fCancelled /*fWait*/))
1039 {
1040 Assert(cbRead == 0);
1041 rc = VINF_SUCCESS;
1042 pThis->fZeroByteRead = false;
1043 }
1044 else if (pThis->fCancelled)
1045 rc = VERR_CANCELLED;
1046 else
1047 rc = RTErrConvertFromWin32(GetLastError());
1048 }
1049 else
1050 {
1051 /* We try get the result here too, just in case we're lucky, but no waiting. */
1052 DWORD dwErr = GetLastError();
1053 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, FALSE /*fWait*/))
1054 {
1055 Assert(cbRead == 0);
1056 rc = VINF_SUCCESS;
1057 pThis->fZeroByteRead = false;
1058 }
1059 else if (rcWait == WAIT_TIMEOUT)
1060 rc = VERR_TIMEOUT;
1061 else if (rcWait == WAIT_ABANDONED)
1062 rc = VERR_INVALID_HANDLE;
1063 else
1064 rc = RTErrConvertFromWin32(dwErr);
1065 }
1066 return rc;
1067}
1068
1069
1070RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
1071{
1072 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1073 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1074 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1075 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1076 /* pcbRead is optional. */
1077
1078 int rc = RTCritSectEnter(&pThis->CritSect);
1079 if (RT_SUCCESS(rc))
1080 {
1081 rtLocalIpcSessionRetain(pThis);
1082 if (pThis->Read.hActiveThread == NIL_RTTHREAD)
1083 {
1084 pThis->Read.hActiveThread = RTThreadSelf();
1085
1086 size_t cbTotalRead = 0;
1087 while (cbToRead > 0)
1088 {
1089 DWORD cbRead = 0;
1090 if (!pThis->fCancelled)
1091 {
1092 /*
1093 * Wait for pending zero byte read, if necessary.
1094 * Note! It cannot easily be cancelled due to concurrent current writes.
1095 */
1096 if (!pThis->fZeroByteRead)
1097 { /* likely */ }
1098 else
1099 {
1100 RTCritSectLeave(&pThis->CritSect);
1101 DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, RT_MS_1MIN);
1102 RTCritSectEnter(&pThis->CritSect);
1103
1104 rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
1105 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
1106 continue;
1107 break;
1108 }
1109
1110 /*
1111 * Kick of a an overlapped read. It should return immediately if
1112 * there is bytes in the buffer. If not, we'll cancel it and see
1113 * what we get back.
1114 */
1115 rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE);
1116 RTCritSectLeave(&pThis->CritSect);
1117
1118 if (ReadFile(pThis->hNmPipe, pvBuf,
1119 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
1120 &cbRead, &pThis->Read.OverlappedIO))
1121 {
1122 RTCritSectEnter(&pThis->CritSect);
1123 rc = VINF_SUCCESS;
1124 }
1125 else if (GetLastError() == ERROR_IO_PENDING)
1126 {
1127 WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE);
1128
1129 RTCritSectEnter(&pThis->CritSect);
1130 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/))
1131 rc = VINF_SUCCESS;
1132 else
1133 {
1134 if (pThis->fCancelled)
1135 rc = VERR_CANCELLED;
1136 else
1137 rc = RTErrConvertFromWin32(GetLastError());
1138 break;
1139 }
1140 }
1141 else
1142 {
1143 rc = RTErrConvertFromWin32(GetLastError());
1144 AssertMsgFailedBreak(("%Rrc\n", rc));
1145 }
1146 }
1147 else
1148 {
1149 rc = VERR_CANCELLED;
1150 break;
1151 }
1152
1153 /* Advance. */
1154 cbToRead -= cbRead;
1155 cbTotalRead += cbRead;
1156 pvBuf = (uint8_t *)pvBuf + cbRead;
1157 }
1158
1159 if (pcbRead)
1160 {
1161 *pcbRead = cbTotalRead;
1162 if ( RT_FAILURE(rc)
1163 && cbTotalRead
1164 && rc != VERR_INVALID_POINTER)
1165 rc = VINF_SUCCESS;
1166 }
1167
1168 pThis->Read.hActiveThread = NIL_RTTHREAD;
1169 }
1170 else
1171 rc = VERR_WRONG_ORDER;
1172 rtLocalIpcSessionReleaseAndUnlock(pThis);
1173 }
1174
1175 return rc;
1176}
1177
1178
1179RTDECL(int) RTLocalIpcSessionReadNB(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
1180{
1181 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1182 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1183 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1184 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1185 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
1186 *pcbRead = 0;
1187
1188 int rc = RTCritSectEnter(&pThis->CritSect);
1189 if (RT_SUCCESS(rc))
1190 {
1191 rtLocalIpcSessionRetain(pThis);
1192 if (pThis->Read.hActiveThread == NIL_RTTHREAD)
1193 {
1194 pThis->Read.hActiveThread = RTThreadSelf();
1195
1196 for (;;)
1197 {
1198 DWORD cbRead = 0;
1199 if (!pThis->fCancelled)
1200 {
1201 /*
1202 * Wait for pending zero byte read, if necessary.
1203 * Note! It cannot easily be cancelled due to concurrent current writes.
1204 */
1205 if (!pThis->fZeroByteRead)
1206 { /* likely */ }
1207 else
1208 {
1209 RTCritSectLeave(&pThis->CritSect);
1210 DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0);
1211 RTCritSectEnter(&pThis->CritSect);
1212
1213 rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
1214 if (RT_SUCCESS(rc))
1215 continue;
1216
1217 if (rc == VERR_TIMEOUT)
1218 rc = VINF_TRY_AGAIN;
1219 break;
1220 }
1221
1222 /*
1223 * Figure out how much we can read (cannot try and cancel here
1224 * like in the anonymous pipe code).
1225 */
1226 DWORD cbAvailable;
1227 if (PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL))
1228 {
1229 if (cbAvailable == 0 || cbToRead == 0)
1230 {
1231 *pcbRead = 0;
1232 rc = VINF_TRY_AGAIN;
1233 break;
1234 }
1235 }
1236 else
1237 {
1238 rc = RTErrConvertFromWin32(GetLastError());
1239 break;
1240 }
1241 if (cbAvailable > cbToRead)
1242 cbAvailable = (DWORD)cbToRead;
1243
1244 /*
1245 * Kick of a an overlapped read. It should return immediately, so we
1246 * don't really need to leave the critsect here.
1247 */
1248 rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE);
1249 if (ReadFile(pThis->hNmPipe, pvBuf, cbAvailable, &cbRead, &pThis->Read.OverlappedIO))
1250 {
1251 *pcbRead = cbRead;
1252 rc = VINF_SUCCESS;
1253 }
1254 else if (GetLastError() == ERROR_IO_PENDING)
1255 {
1256 DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0);
1257 if (rcWait == WAIT_TIMEOUT)
1258 {
1259 RTCritSectLeave(&pThis->CritSect);
1260 rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE);
1261 RTCritSectEnter(&pThis->CritSect);
1262 }
1263 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/))
1264 {
1265 *pcbRead = cbRead;
1266 rc = VINF_SUCCESS;
1267 }
1268 else
1269 {
1270 if (pThis->fCancelled)
1271 rc = VERR_CANCELLED;
1272 else
1273 rc = RTErrConvertFromWin32(GetLastError());
1274 }
1275 }
1276 else
1277 {
1278 rc = RTErrConvertFromWin32(GetLastError());
1279 AssertMsgFailedBreak(("%Rrc\n", rc));
1280 }
1281 }
1282 else
1283 rc = VERR_CANCELLED;
1284 break;
1285 }
1286
1287 pThis->Read.hActiveThread = NIL_RTTHREAD;
1288 }
1289 else
1290 rc = VERR_WRONG_ORDER;
1291 rtLocalIpcSessionReleaseAndUnlock(pThis);
1292 }
1293
1294 return rc;
1295}
1296
1297
1298#if 0 /* Non-blocking writes are not yet supported. */
1299/**
1300 * Common worker for handling I/O completion.
1301 *
1302 * This is used by RTLocalIpcSessionClose and RTLocalIpcSessionWrite.
1303 *
1304 * @returns IPRT status code.
1305 * @param pThis The pipe instance handle.
1306 */
1307static int rtLocalIpcSessionWriteCheckCompletion(PRTLOCALIPCSESSIONINT pThis)
1308{
1309 int rc;
1310 DWORD rcWait = WaitForSingleObject(pThis->OverlappedIO.hEvent, 0);
1311 if (rcWait == WAIT_OBJECT_0)
1312 {
1313 DWORD cbWritten = 0;
1314 if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE))
1315 {
1316 for (;;)
1317 {
1318 if (cbWritten >= pThis->cbBounceBufUsed)
1319 {
1320 pThis->fIOPending = false;
1321 rc = VINF_SUCCESS;
1322 break;
1323 }
1324
1325 /* resubmit the remainder of the buffer - can this actually happen? */
1326 memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
1327 rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE);
1328 if (!WriteFile(pThis->hNmPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
1329 &cbWritten, &pThis->OverlappedIO))
1330 {
1331 DWORD dwErr = GetLastError();
1332 if (dwErr == ERROR_IO_PENDING)
1333 rc = VINF_TRY_AGAIN;
1334 else
1335 {
1336 pThis->fIOPending = false;
1337 if (dwErr == ERROR_NO_DATA)
1338 rc = VERR_BROKEN_PIPE;
1339 else
1340 rc = RTErrConvertFromWin32(dwErr);
1341 }
1342 break;
1343 }
1344 Assert(cbWritten > 0);
1345 }
1346 }
1347 else
1348 {
1349 pThis->fIOPending = false;
1350 rc = RTErrConvertFromWin32(GetLastError());
1351 }
1352 }
1353 else if (rcWait == WAIT_TIMEOUT)
1354 rc = VINF_TRY_AGAIN;
1355 else
1356 {
1357 pThis->fIOPending = false;
1358 if (rcWait == WAIT_ABANDONED)
1359 rc = VERR_INVALID_HANDLE;
1360 else
1361 rc = RTErrConvertFromWin32(GetLastError());
1362 }
1363 return rc;
1364}
1365#endif
1366
1367
1368RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuf, size_t cbToWrite)
1369{
1370 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1371 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1372 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1373 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1374 AssertReturn(cbToWrite, VERR_INVALID_PARAMETER);
1375
1376 int rc = RTCritSectEnter(&pThis->CritSect);
1377 if (RT_SUCCESS(rc))
1378 {
1379 rtLocalIpcSessionRetain(pThis);
1380 if (pThis->Write.hActiveThread == NIL_RTTHREAD)
1381 {
1382 pThis->Write.hActiveThread = RTThreadSelf();
1383
1384 /*
1385 * Try write everything. No bounce buffering necessary.
1386 */
1387 size_t cbTotalWritten = 0;
1388 while (cbToWrite > 0)
1389 {
1390 DWORD cbWritten = 0;
1391 if (!pThis->fCancelled)
1392 {
1393 BOOL fRc = ResetEvent(pThis->Write.OverlappedIO.hEvent); Assert(fRc == TRUE);
1394 RTCritSectLeave(&pThis->CritSect);
1395
1396 DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
1397 fRc = WriteFile(pThis->hNmPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Write.OverlappedIO);
1398 if (fRc)
1399 rc = VINF_SUCCESS;
1400 else
1401 {
1402 DWORD dwErr = GetLastError();
1403 if (dwErr == ERROR_IO_PENDING)
1404 {
1405 DWORD rcWait = WaitForSingleObject(pThis->Write.OverlappedIO.hEvent, INFINITE);
1406 if (rcWait == WAIT_OBJECT_0)
1407 {
1408 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Write.OverlappedIO, &cbWritten, TRUE /*fWait*/))
1409 rc = VINF_SUCCESS;
1410 else
1411 rc = RTErrConvertFromWin32(GetLastError());
1412 }
1413 else if (rcWait == WAIT_TIMEOUT)
1414 rc = VERR_TIMEOUT;
1415 else if (rcWait == WAIT_ABANDONED)
1416 rc = VERR_INVALID_HANDLE;
1417 else
1418 rc = RTErrConvertFromWin32(GetLastError());
1419 }
1420 else if (dwErr == ERROR_NO_DATA)
1421 rc = VERR_BROKEN_PIPE;
1422 else
1423 rc = RTErrConvertFromWin32(dwErr);
1424 }
1425
1426 if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
1427 cbWritten = cbToWriteInThisIteration;
1428
1429 RTCritSectEnter(&pThis->CritSect);
1430 if (RT_FAILURE(rc))
1431 break;
1432 }
1433 else
1434 {
1435 rc = VERR_CANCELLED;
1436 break;
1437 }
1438
1439 /* Advance. */
1440 pvBuf = (char const *)pvBuf + cbWritten;
1441 cbTotalWritten += cbWritten;
1442 cbToWrite -= cbWritten;
1443 }
1444
1445 pThis->Write.hActiveThread = NIL_RTTHREAD;
1446 }
1447 else
1448 rc = VERR_WRONG_ORDER;
1449 rtLocalIpcSessionReleaseAndUnlock(pThis);
1450 }
1451
1452 return rc;
1453}
1454
1455
1456RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession)
1457{
1458 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1459 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1460 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1461
1462 int rc = RTCritSectEnter(&pThis->CritSect);
1463 if (RT_SUCCESS(rc))
1464 {
1465 if (pThis->Write.hActiveThread == NIL_RTTHREAD)
1466 {
1467 /* No flushing on Windows needed since RTLocalIpcSessionWrite will block until
1468 * all data was written (or an error occurred). */
1469 /** @todo r=bird: above comment is misinformed.
1470 * Implement this as soon as we want an explicit asynchronous version of
1471 * RTLocalIpcSessionWrite on Windows. */
1472 rc = VINF_SUCCESS;
1473 }
1474 else
1475 rc = VERR_WRONG_ORDER;
1476 RTCritSectLeave(&pThis->CritSect);
1477 }
1478 return rc;
1479}
1480
1481
1482RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies)
1483{
1484 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1485 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1486 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1487
1488 uint64_t const msStart = RTTimeMilliTS();
1489
1490 int rc = RTCritSectEnter(&pThis->CritSect);
1491 if (RT_SUCCESS(rc))
1492 {
1493 rtLocalIpcSessionRetain(pThis);
1494 if (pThis->Read.hActiveThread == NIL_RTTHREAD)
1495 {
1496 pThis->Read.hActiveThread = RTThreadSelf();
1497
1498 /*
1499 * Wait loop.
1500 */
1501 for (unsigned iLoop = 0;; iLoop++)
1502 {
1503 /*
1504 * Check for cancellation before we continue.
1505 */
1506 if (!pThis->fCancelled)
1507 { /* likely */ }
1508 else
1509 {
1510 rc = VERR_CANCELLED;
1511 break;
1512 }
1513
1514 /*
1515 * Prep something we can wait on.
1516 */
1517 HANDLE hWait = INVALID_HANDLE_VALUE;
1518 if (pThis->fZeroByteRead)
1519 hWait = pThis->Read.OverlappedIO.hEvent;
1520 else
1521 {
1522 /* Peek at the pipe buffer and see how many bytes it contains. */
1523 DWORD cbAvailable;
1524 if ( PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL)
1525 && cbAvailable)
1526 {
1527 rc = VINF_SUCCESS;
1528 break;
1529 }
1530
1531 /* Start a zero byte read operation that we can wait on. */
1532 if (cMillies == 0)
1533 {
1534 rc = VERR_TIMEOUT;
1535 break;
1536 }
1537 BOOL fRc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(fRc == TRUE); NOREF(fRc);
1538 DWORD cbRead = 0;
1539 if (ReadFile(pThis->hNmPipe, pThis->abBuf, 0 /*cbToRead*/, &cbRead, &pThis->Read.OverlappedIO))
1540 {
1541 rc = VINF_SUCCESS;
1542 if (iLoop > 10)
1543 RTThreadYield();
1544 }
1545 else if (GetLastError() == ERROR_IO_PENDING)
1546 {
1547 pThis->fZeroByteRead = true;
1548 hWait = pThis->Read.OverlappedIO.hEvent;
1549 }
1550 else
1551 rc = RTErrConvertFromWin32(GetLastError());
1552 if (RT_FAILURE(rc))
1553 break;
1554 }
1555
1556 /*
1557 * Check for timeout.
1558 */
1559 DWORD cMsMaxWait = INFINITE; /* (MSC maybe used uninitialized) */
1560 if (cMillies == RT_INDEFINITE_WAIT)
1561 cMsMaxWait = INFINITE;
1562 else if ( hWait != INVALID_HANDLE_VALUE
1563 || iLoop > 10)
1564 {
1565 uint64_t cMsElapsed = RTTimeMilliTS() - msStart;
1566 if (cMsElapsed <= cMillies)
1567 cMsMaxWait = cMillies - (uint32_t)cMsElapsed;
1568 else if (iLoop == 0)
1569 cMsMaxWait = cMillies ? 1 : 0;
1570 else
1571 {
1572 rc = VERR_TIMEOUT;
1573 break;
1574 }
1575 }
1576
1577 /*
1578 * Wait and collect the result.
1579 */
1580 if (hWait != INVALID_HANDLE_VALUE)
1581 {
1582 RTCritSectLeave(&pThis->CritSect);
1583
1584 DWORD rcWait = WaitForSingleObject(hWait, cMsMaxWait);
1585
1586 int rc2 = RTCritSectEnter(&pThis->CritSect);
1587 AssertRC(rc2);
1588
1589 rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
1590 break;
1591 }
1592 }
1593
1594 pThis->Read.hActiveThread = NIL_RTTHREAD;
1595 }
1596
1597 rtLocalIpcSessionReleaseAndUnlock(pThis);
1598 }
1599
1600 return rc;
1601}
1602
1603
1604RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession)
1605{
1606 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1607 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1608 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1609
1610 /*
1611 * Enter the critical section, then set the cancellation flag
1612 * and signal the event (to wake up anyone in/at WaitForSingleObject).
1613 */
1614 int rc = RTCritSectEnter(&pThis->CritSect);
1615 if (RT_SUCCESS(rc))
1616 {
1617 rtLocalIpcSessionRetain(pThis);
1618 rc = rtLocalIpcWinCancel(pThis);
1619 rtLocalIpcSessionReleaseAndUnlock(pThis);
1620 }
1621
1622 return rc;
1623}
1624
1625
1626RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess)
1627{
1628 RT_NOREF_PV(hSession); RT_NOREF_PV(pProcess);
1629 return VERR_NOT_SUPPORTED;
1630}
1631
1632
1633RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid)
1634{
1635 RT_NOREF_PV(hSession); RT_NOREF_PV(pUid);
1636 return VERR_NOT_SUPPORTED;
1637}
1638
1639
1640RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTGID pGid)
1641{
1642 RT_NOREF_PV(hSession); RT_NOREF_PV(pGid);
1643 return VERR_NOT_SUPPORTED;
1644}
1645
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette