VirtualBox

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

Last change on this file since 47051 was 47051, checked in by vboxsync, 12 years ago

IPRT/localipc-win.cpp: Update on implementation (untested so far), also now should deal with NT4 + SDDL creation more dynamically.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.0 KB
Line 
1/* $Id: localipc-win.cpp 47051 2013-07-09 12:15:44Z vboxsync $ */
2/** @file
3 * IPRT - Local IPC, Windows Implementation Using Named Pipes.
4 */
5
6/*
7 * Copyright (C) 2008-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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* Header Files *
29*******************************************************************************/
30/*
31 * We have to force NT 5.0 here because of
32 * ConvertStringSecurityDescriptorToSecurityDescriptor. Note that because of
33 * FILE_FLAG_FIRST_PIPE_INSTANCE this code actually requires W2K SP2+.
34 */
35#ifndef _WIN32_WINNT
36# define _WIN32_WINNT 0x0500 /* for ConvertStringSecurityDescriptorToSecurityDescriptor */
37#elif _WIN32_WINNT < 0x0500
38# undef _WIN32_WINNT
39# define _WIN32_WINNT 0x0500
40#endif
41#include <Windows.h>
42#include <sddl.h>
43
44#include <iprt/alloc.h>
45#include <iprt/asm.h>
46#include <iprt/assert.h>
47#include <iprt/critsect.h>
48#include <iprt/err.h>
49#include <iprt/ldr.h>
50#include <iprt/localipc.h>
51#include <iprt/param.h>
52#include <iprt/string.h>
53#include <iprt/thread.h>
54
55#include "internal/magics.h"
56
57
58/*******************************************************************************
59* Defined Constants And Macros *
60*******************************************************************************/
61/** Pipe prefix string. */
62#define RTLOCALIPC_WIN_PREFIX "\\\\.\\pipe\\IPRT-"
63
64/** DACL for block all network access and local users other than the creator/owner.
65 *
66 * ACE format: (ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid)
67 *
68 * Note! FILE_GENERIC_WRITE (SDDL_FILE_WRITE) is evil here because it includes
69 * the FILE_CREATE_PIPE_INSTANCE(=FILE_APPEND_DATA) flag. Thus the hardcoded
70 * value 0x0012019b in the 2nd ACE. It expands to:
71 * 0x00000001 - FILE_READ_DATA
72 * 0x00000008 - FILE_READ_EA
73 * 0x00000080 - FILE_READ_ATTRIBUTES
74 * 0x00020000 - READ_CONTROL
75 * 0x00100000 - SYNCHRONIZE
76 * 0x00000002 - FILE_WRITE_DATA
77 * 0x00000010 - FILE_WRITE_EA
78 * 0x00000100 - FILE_WRITE_ATTRIBUTES
79 * 0x0012019b
80 * or FILE_GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_CREATE_PIPE_INSTANCE)
81 *
82 * @todo Double check this!
83 * @todo Drop the EA rights too? Since they doesn't mean anything to PIPS according to the docs.
84 * @todo EVERYONE -> AUTHENTICATED USERS or something more appropriate?
85 * @todo Have trouble allowing the owner FILE_CREATE_PIPE_INSTANCE access, so for now I'm hacking
86 * it just to get progress - the service runs as local system.
87 * The CREATOR OWNER and PERSONAL SELF works (the former is only involved in inheriting
88 * it seems, which is why it won't work. The latter I've no idea about. Perhaps the solution
89 * is to go the annoying route of OpenProcessToken, QueryTokenInformation,
90 * ConvertSidToStringSid and then use the result... Suggestions are very welcome
91 */
92#define RTLOCALIPC_WIN_SDDL \
93 SDDL_DACL SDDL_DELIMINATOR \
94 SDDL_ACE_BEGIN SDDL_ACCESS_DENIED ";;" SDDL_GENERIC_ALL ";;;" SDDL_NETWORK SDDL_ACE_END \
95 SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";;" "0x0012019b" ";;;" SDDL_EVERYONE SDDL_ACE_END \
96 SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";;" SDDL_FILE_ALL ";;;" SDDL_LOCAL_SYSTEM SDDL_ACE_END
97
98// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";;" SDDL_GENERIC_ALL ";;;" SDDL_PERSONAL_SELF SDDL_ACE_END \
99// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";CIOI;" SDDL_GENERIC_ALL ";;;" SDDL_CREATOR_OWNER SDDL_ACE_END
100// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";;" "0x0012019b" ";;;" SDDL_EVERYONE SDDL_ACE_END
101// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";;" SDDL_FILE_ALL ";;;" SDDL_LOCAL_SYSTEM SDDL_ACE_END
102
103
104/*******************************************************************************
105* Structures and Typedefs *
106*******************************************************************************/
107/**
108 * Local IPC service instance, Windows.
109 */
110typedef struct RTLOCALIPCSERVERINT
111{
112 /** The magic (RTLOCALIPCSERVER_MAGIC). */
113 uint32_t u32Magic;
114 /** The creation flags. */
115 uint32_t fFlags;
116 /** Critical section protecting the structure. */
117 RTCRITSECT CritSect;
118 /** The number of references to the instance.
119 * @remarks The reference counting isn't race proof. */
120 uint32_t volatile cRefs;
121 /** Indicates that there is a pending cancel request. */
122 bool volatile fCancelled;
123 /** The name pipe handle. */
124 HANDLE hNmPipe;
125 /** The handle to the event object we're using for overlapped I/O. */
126 HANDLE hEvent;
127 /** The overlapped I/O structure. */
128 OVERLAPPED OverlappedIO;
129 /** The pipe name. */
130 char szName[1];
131} RTLOCALIPCSERVERINT;
132/** Pointer to a local IPC server instance (Windows). */
133typedef RTLOCALIPCSERVERINT *PRTLOCALIPCSERVERINT;
134
135
136/**
137 * Local IPC session instance, Windows.
138 */
139typedef struct RTLOCALIPCSESSIONINT
140{
141 /** The magic (RTLOCALIPCSESSION_MAGIC). */
142 uint32_t u32Magic;
143 /** Critical section protecting the structure. */
144 RTCRITSECT CritSect;
145 /** The number of references to the instance.
146 * @remarks The reference counting isn't race proof. */
147 uint32_t volatile cRefs;
148 /** Set if there is already pending I/O. */
149 bool fIOPending;
150 /** Indicates that there is a pending cancel request. */
151 bool volatile fCancelled;
152 /** The name pipe handle. */
153 HANDLE hNmPipe;
154 /** The handle to the event object we're using for overlapped I/O. */
155 HANDLE hEvent;
156 /** The overlapped I/O structure. */
157 OVERLAPPED OverlappedIO;
158 /** Bounce buffer for writes. */
159 uint8_t *pbBounceBuf;
160 /** Amount of used buffer space. */
161 size_t cbBounceBufUsed;
162 /** Amount of allocated buffer space. */
163 size_t cbBounceBufAlloc;
164} RTLOCALIPCSESSIONINT;
165/** Pointer to a local IPC session instance (Windows). */
166typedef RTLOCALIPCSESSIONINT *PRTLOCALIPCSESSIONINT;
167
168typedef BOOL WINAPI FNCONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTOR(LPCTSTR, DWORD, PSECURITY_DESCRIPTOR, PULONG);
169typedef FNCONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTOR
170 *PFNCONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTOR; /* No, nobody fell on the keyboard, really! */
171
172
173/*******************************************************************************
174* Internal Functions *
175*******************************************************************************/
176static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSION phClientSession, HANDLE hNmPipeSession);
177
178
179/**
180 * Creates a named pipe instance.
181 *
182 * This is used by both RTLocalIpcServerCreate and RTLocalIpcServerListen.
183 *
184 * @return IPRT status code.
185 * @param phNmPipe Where to store the named pipe handle on success. This
186 * will be set to INVALID_HANDLE_VALUE on failure.
187 * @param pszFullPipeName The full named pipe name.
188 * @param fFirst Set on the first call (from RTLocalIpcServerCreate), otherwise clear.
189 * Governs the FILE_FLAG_FIRST_PIPE_INSTANCE flag.
190 */
191static int rtLocalIpcServerWinCreatePipeInstance(PHANDLE phNmPipe, const char *pszFullPipeName, bool fFirst)
192{
193 *phNmPipe = INVALID_HANDLE_VALUE;
194
195 /** @todo Stuff this into RTInitOnce. Later. */
196 PFNCONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTOR
197 pfnConvertStringSecurityDescriptorToSecurityDescriptor = NULL;
198
199 RTLDRMOD hAdvApi32 = NIL_RTLDRMOD;
200 int rc = RTLdrLoadSystem("Advapi32.lib", true /*fNoUnload*/, &hAdvApi32);
201 if (RT_SUCCESS(rc))
202 rc = RTLdrGetSymbol(hAdvApi32, "ConvertStringSecurityDescriptorToSecurityDescriptor",
203 (void**)&pfnConvertStringSecurityDescriptorToSecurityDescriptor);
204
205 PSECURITY_DESCRIPTOR pSecDesc = NULL;
206 if (RT_SUCCESS(rc))
207 {
208 AssertPtr(pfnConvertStringSecurityDescriptorToSecurityDescriptor);
209
210 /*
211 * We'll create a security descriptor from a SDDL that denies
212 * access to network clients (this is local IPC after all), it
213 * makes some further restrictions to prevent non-authenticated
214 * users from screwing around.
215 */
216 if (!pfnConvertStringSecurityDescriptorToSecurityDescriptor(RTLOCALIPC_WIN_SDDL,
217 SDDL_REVISION_1,
218 &pSecDesc,
219 NULL))
220 {
221 rc = RTErrConvertFromWin32(GetLastError());
222 }
223 }
224 else
225 {
226 /* Windows OSes < W2K SP2 not supported for now, bail out. */
227 /** @todo Implement me! */
228 rc = VERR_NOT_SUPPORTED;
229 }
230
231 if (hAdvApi32 != NIL_RTLDRMOD)
232 RTLdrClose(hAdvApi32);
233
234 if (RT_SUCCESS(rc))
235 {
236 SECURITY_ATTRIBUTES SecAttrs;
237 SecAttrs.nLength = sizeof(SecAttrs);
238 SecAttrs.lpSecurityDescriptor = pSecDesc;
239 SecAttrs.bInheritHandle = FALSE;
240
241 DWORD fOpenMode = PIPE_ACCESS_DUPLEX
242 | PIPE_WAIT
243 | FILE_FLAG_OVERLAPPED;
244
245 bool fSupportsFirstInstance = false;
246
247 OSVERSIONINFOEX OSInfoEx;
248 RT_ZERO(OSInfoEx);
249 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
250 if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
251 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT)
252 {
253 if ( /* Vista+. */
254 OSInfoEx.dwMajorVersion >= 6
255 /* Windows XP+. */
256 || ( OSInfoEx.dwMajorVersion == 5
257 && OSInfoEx.dwMinorVersion > 0)
258 /* Windows 2000. */
259 || ( OSInfoEx.dwMajorVersion == 5
260 && OSInfoEx.dwMinorVersion == 0
261 && OSInfoEx.wServicePackMajor >= 2))
262 {
263 /* Requires at least W2K (5.0) SP2+. This is non-fatal. */
264 fSupportsFirstInstance = true;
265 }
266 }
267
268 if (fFirst && fSupportsFirstInstance)
269 fOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
270
271 HANDLE hNmPipe = CreateNamedPipe(pszFullPipeName, /* lpName */
272 fOpenMode, /* dwOpenMode */
273 PIPE_TYPE_BYTE, /* dwPipeMode */
274 PIPE_UNLIMITED_INSTANCES, /* nMaxInstances */
275 PAGE_SIZE, /* nOutBufferSize (advisory) */
276 PAGE_SIZE, /* nInBufferSize (ditto) */
277 30*1000, /* nDefaultTimeOut = 30 sec */
278 &SecAttrs); /* lpSecurityAttributes */
279
280 LocalFree(pSecDesc);
281 if (hNmPipe != INVALID_HANDLE_VALUE)
282 {
283 *phNmPipe = hNmPipe;
284 }
285 else
286 rc = RTErrConvertFromWin32(GetLastError());
287 }
288
289 return rc;
290}
291
292
293RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszName, uint32_t fFlags)
294{
295 /*
296 * Basic parameter validation.
297 */
298 AssertPtrReturn(phServer, VERR_INVALID_POINTER);
299 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
300 AssertReturn(*pszName, VERR_INVALID_PARAMETER);
301 AssertReturn(!(fFlags & ~(RTLOCALIPC_FLAGS_VALID_MASK)), VERR_INVALID_PARAMETER);
302
303 AssertReturn(fFlags & RTLOCALIPC_FLAGS_MULTI_SESSION, VERR_NOT_IMPLEMENTED); /** @todo implement !RTLOCALIPC_FLAGS_MULTI_SESSION */
304
305 /*
306 * Allocate and initialize the instance data.
307 */
308 size_t cchName = strlen(pszName);
309 size_t cch = RT_OFFSETOF(RTLOCALIPCSERVERINT, szName[cchName + sizeof(RTLOCALIPC_WIN_PREFIX)]);
310 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)RTMemAlloc(cch);
311 if (!pThis)
312 return VERR_NO_MEMORY;
313 pThis->u32Magic = RTLOCALIPCSERVER_MAGIC;
314 pThis->cRefs = 1; /* the one we return */
315 pThis->fCancelled = false;
316 memcpy(pThis->szName, RTLOCALIPC_WIN_PREFIX, sizeof(RTLOCALIPC_WIN_PREFIX) - 1);
317 memcpy(&pThis->szName[sizeof(RTLOCALIPC_WIN_PREFIX) - 1], pszName, cchName + 1);
318 int rc = RTCritSectInit(&pThis->CritSect);
319 if (RT_SUCCESS(rc))
320 {
321 pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
322 FALSE /*bInitialState*/, NULL /*lpName*/);
323 if (pThis->hEvent != NULL)
324 {
325 RT_ZERO(pThis->OverlappedIO);
326 pThis->OverlappedIO.Internal = STATUS_PENDING;
327 pThis->OverlappedIO.hEvent = pThis->hEvent;
328
329 rc = rtLocalIpcServerWinCreatePipeInstance(&pThis->hNmPipe,
330 pThis->szName, true /* fFirst */);
331 if (RT_SUCCESS(rc))
332 {
333 *phServer = pThis;
334 return VINF_SUCCESS;
335 }
336
337 BOOL fRc = CloseHandle(pThis->hEvent);
338 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
339 }
340 else
341 rc = RTErrConvertFromWin32(GetLastError());
342
343 int rc2 = RTCritSectDelete(&pThis->CritSect);
344 AssertRC(rc2);
345 }
346 RTMemFree(pThis);
347 return rc;
348}
349
350
351/**
352 * Call when the reference count reaches 0.
353 * Caller owns the critsect.
354 * @param pThis The instance to destroy.
355 */
356static void rtLocalIpcServerWinDestroy(PRTLOCALIPCSERVERINT pThis)
357{
358 BOOL fRc = CloseHandle(pThis->hNmPipe);
359 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
360 pThis->hNmPipe = INVALID_HANDLE_VALUE;
361
362 fRc = CloseHandle(pThis->hEvent);
363 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
364 pThis->hEvent = NULL;
365
366 RTCritSectLeave(&pThis->CritSect);
367 RTCritSectDelete(&pThis->CritSect);
368
369 RTMemFree(pThis);
370}
371
372
373RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer)
374{
375 /*
376 * Validate input.
377 */
378 if (hServer == NIL_RTLOCALIPCSERVER)
379 return VINF_SUCCESS;
380 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
381 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
382 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_MAGIC);
383
384 /*
385 * Cancel any thread currently busy using the server,
386 * leaving the cleanup to it.
387 */
388 RTCritSectEnter(&pThis->CritSect);
389 ASMAtomicUoWriteU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC);
390 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
391 pThis->cRefs--;
392
393 if (pThis->cRefs > 0)
394 {
395 BOOL fRc = SetEvent(pThis->hEvent);
396 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
397
398 RTCritSectLeave(&pThis->CritSect);
399 }
400 else
401 rtLocalIpcServerWinDestroy(pThis);
402
403 return VINF_SUCCESS;
404}
405
406
407RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION phClientSession)
408{
409 /*
410 * Validate input.
411 */
412 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
413 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
414 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_MAGIC);
415
416 /*
417 * Enter the critsect before inspecting the object further.
418 */
419 int rc;
420 RTCritSectEnter(&pThis->CritSect);
421 if (pThis->fCancelled)
422 {
423 pThis->fCancelled = false;
424 rc = VERR_CANCELLED;
425 RTCritSectLeave(&pThis->CritSect);
426 }
427 else
428 {
429 pThis->cRefs++;
430 ResetEvent(pThis->hEvent);
431 RTCritSectLeave(&pThis->CritSect);
432
433 /*
434 * Try connect a client. We need to use overlapped I/O here because
435 * of the cancellation done by RTLocalIpcServerCancel and RTLocalIpcServerDestroy.
436 */
437 SetLastError(NO_ERROR);
438 BOOL fRc = ConnectNamedPipe(pThis->hNmPipe, &pThis->OverlappedIO);
439 DWORD err = fRc ? NO_ERROR : GetLastError();
440 if ( !fRc
441 && err == ERROR_IO_PENDING)
442 {
443 WaitForSingleObject(pThis->hEvent, INFINITE);
444 DWORD dwIgnored;
445 fRc = GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &dwIgnored, FALSE /* bWait*/);
446 err = fRc ? NO_ERROR : GetLastError();
447 }
448
449 RTCritSectEnter(&pThis->CritSect);
450 if ( !pThis->fCancelled /* Event signalled but not cancelled? */
451 && pThis->u32Magic == RTLOCALIPCSERVER_MAGIC)
452 {
453 /*
454 * Still alive, some error or an actual client.
455 *
456 * If it's the latter we'll have to create a new pipe instance that
457 * replaces the current one for the server. The current pipe instance
458 * will be assigned to the client session.
459 */
460 if ( fRc
461 || err == ERROR_PIPE_CONNECTED)
462 {
463 HANDLE hNmPipe;
464 DWORD err = rtLocalIpcServerWinCreatePipeInstance(&hNmPipe, pThis->szName, false /* fFirst */);
465 if (err == NO_ERROR)
466 {
467 HANDLE hNmPipeSession = pThis->hNmPipe; /* consumed */
468 pThis->hNmPipe = hNmPipe;
469 rc = rtLocalIpcWinCreateSession(phClientSession, hNmPipeSession);
470 }
471 else
472 {
473 /*
474 * We failed to create a new instance for the server, disconnect
475 * the client and fail. Don't try service the client here.
476 */
477 rc = RTErrConvertFromWin32(err);
478 fRc = DisconnectNamedPipe(pThis->hNmPipe);
479 AssertMsg(fRc, ("%d\n", GetLastError()));
480 }
481 }
482 else
483 rc = RTErrConvertFromWin32(err);
484 }
485 else
486 {
487 /*
488 * Cancelled or destroyed.
489 *
490 * Cancel the overlapped io if it didn't complete (must be done
491 * in the this thread) or disconnect the client.
492 */
493 if ( fRc
494 || err == ERROR_PIPE_CONNECTED)
495 fRc = DisconnectNamedPipe(pThis->hNmPipe);
496 else if (err == ERROR_IO_PENDING)
497 fRc = CancelIo(pThis->hNmPipe);
498 else
499 fRc = TRUE;
500 AssertMsg(fRc, ("%d\n", GetLastError()));
501 rc = VERR_CANCELLED;
502 }
503
504 pThis->cRefs--;
505 if (pThis->cRefs)
506 RTCritSectLeave(&pThis->CritSect);
507 else
508 rtLocalIpcServerWinDestroy(pThis);
509 }
510
511 return rc;
512}
513
514
515RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer)
516{
517 /*
518 * Validate input.
519 */
520 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
521 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
522 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_MAGIC);
523
524 /*
525 * Enter the critical section, then set the cancellation flag
526 * and signal the event (to wake up anyone in/at WaitForSingleObject).
527 */
528 RTCritSectEnter(&pThis->CritSect);
529
530 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
531 BOOL fRc = SetEvent(pThis->hEvent);
532 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
533
534 RTCritSectLeave(&pThis->CritSect);
535
536 return VINF_SUCCESS;
537}
538
539
540/**
541 * Create a session instance.
542 *
543 * @returns IPRT status code.
544 *
545 * @param phClientSession Where to store the session handle on success.
546 * @param hNmPipeSession The named pipe handle. This will be consumed by this session, meaning on failure
547 * to create the session it will be closed.
548 */
549static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSION phClientSession, HANDLE hNmPipeSession)
550{
551 AssertPtrReturn(phClientSession, VERR_INVALID_POINTER);
552 AssertReturn(hNmPipeSession != INVALID_HANDLE_VALUE, VERR_INVALID_PARAMETER);
553
554 int rc;
555
556 /*
557 * Allocate and initialize the session instance data.
558 */
559 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAlloc(sizeof(*pThis));
560 if (pThis)
561 {
562 pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
563 pThis->cRefs = 1; /* our ref */
564 pThis->fCancelled = false;
565 pThis->hNmPipe = hNmPipeSession;
566
567 rc = RTCritSectInit(&pThis->CritSect);
568 if (RT_SUCCESS(rc))
569 {
570 pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
571 FALSE /*bInitialState*/, NULL /*lpName*/);
572 if (pThis->hEvent != NULL)
573 {
574 RT_ZERO(pThis->OverlappedIO);
575 pThis->OverlappedIO.Internal = STATUS_PENDING;
576 pThis->OverlappedIO.hEvent = pThis->hEvent;
577
578 *phClientSession = pThis;
579 return VINF_SUCCESS;
580 }
581
582 /* bail out */
583 rc = RTErrConvertFromWin32(GetLastError());
584 RTCritSectDelete(&pThis->CritSect);
585 }
586 RTMemFree(pThis);
587 }
588 else
589 rc = VERR_NO_MEMORY;
590
591 BOOL fRc = CloseHandle(hNmPipeSession);
592 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
593 return rc;
594}
595
596RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags)
597{
598 AssertPtrReturn(phSession, VERR_INVALID_POINTER);
599 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
600 AssertReturn(!fFlags, VERR_INVALID_PARAMETER); /* Flags currently unused, must be 0. */
601
602 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAlloc(sizeof(*pThis));
603 if (!pThis)
604 return VERR_NO_MEMORY;
605
606 pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
607 pThis->cRefs = 1; /* The one we return. */
608 pThis->fIOPending = false;
609 pThis->fCancelled = false;
610 pThis->pbBounceBuf = NULL;
611 pThis->cbBounceBufAlloc = 0;
612 pThis->cbBounceBufUsed = 0;
613
614 int rc = RTCritSectInit(&pThis->CritSect);
615 if (RT_SUCCESS(rc))
616 {
617 pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
618 FALSE /*bInitialState*/, NULL /*lpName*/);
619 if (pThis->hEvent != NULL)
620 {
621 RT_ZERO(pThis->OverlappedIO);
622 pThis->OverlappedIO.Internal = STATUS_PENDING;
623 pThis->OverlappedIO.hEvent = pThis->hEvent;
624
625 char *pszPipe;
626 if (RTStrAPrintf(&pszPipe, "%s%s", RTLOCALIPC_WIN_PREFIX, pszName))
627 {
628 HANDLE hPipe = CreateFile(pszPipe, /* pipe name */
629 GENERIC_READ | /* read and write access */
630 GENERIC_WRITE,
631 0, /* no sharing */
632 NULL, /* default security attributes */
633 OPEN_EXISTING, /* opens existing pipe */
634 FILE_FLAG_OVERLAPPED, /* default attributes */
635 NULL); /* no template file */
636 RTStrFree(pszPipe);
637 if (hPipe != INVALID_HANDLE_VALUE)
638 {
639 pThis->hNmPipe = hPipe;
640 return VINF_SUCCESS;
641 }
642 else
643 rc = RTErrConvertFromWin32(GetLastError());
644 }
645 else
646 rc = VERR_NO_MEMORY;
647
648 BOOL fRc = CloseHandle(pThis->hEvent);
649 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
650 }
651 else
652 rc = RTErrConvertFromWin32(GetLastError());
653
654 int rc2 = RTCritSectDelete(&pThis->CritSect);
655 AssertRC(rc2);
656 }
657
658 RTMemFree(pThis);
659 return rc;
660}
661
662
663/**
664 * Call when the reference count reaches 0.
665 * Caller owns the critsect.
666 * @param pThis The instance to destroy.
667 */
668static void rtLocalIpcSessionWinDestroy(PRTLOCALIPCSESSIONINT pThis)
669{
670 BOOL fRc = CloseHandle(pThis->hNmPipe);
671 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
672 pThis->hNmPipe = INVALID_HANDLE_VALUE;
673
674 fRc = CloseHandle(pThis->hEvent);
675 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
676 pThis->hEvent = NULL;
677
678 RTCritSectLeave(&pThis->CritSect);
679 RTCritSectDelete(&pThis->CritSect);
680
681 RTMemFree(pThis);
682}
683
684
685RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession)
686{
687 /*
688 * Validate input.
689 */
690 if (hSession == NIL_RTLOCALIPCSESSION)
691 return VINF_SUCCESS;
692 PRTLOCALIPCSESSIONINT pThis = (RTLOCALIPCSESSION)hSession;
693 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
694 AssertReturn(pThis->u32Magic != RTLOCALIPCSESSION_MAGIC, VERR_INVALID_MAGIC);
695
696 /*
697 * Cancel any thread currently busy using the session,
698 * leaving the cleanup to it.
699 */
700 RTCritSectEnter(&pThis->CritSect);
701 ASMAtomicUoWriteU32(&pThis->u32Magic, ~RTLOCALIPCSESSION_MAGIC);
702 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
703 pThis->cRefs--;
704
705 if (pThis->cRefs > 0)
706 {
707 BOOL fRc = SetEvent(pThis->hEvent);
708 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
709
710 RTCritSectLeave(&pThis->CritSect);
711 }
712 else
713 rtLocalIpcSessionWinDestroy(pThis);
714
715 return VINF_SUCCESS;
716}
717
718
719RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
720{
721 PRTLOCALIPCSESSIONINT pThis = hSession;
722 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
723 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
724 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
725 /* pcbRead is optional. */
726
727 int rc = RTCritSectEnter(&pThis->CritSect);
728 if (RT_SUCCESS(rc))
729 {
730 /* No concurrent readers, sorry. */
731 if (pThis->cRefs == 0)
732 {
733 pThis->cRefs++;
734
735 /*
736 * If pcbRead is non-NULL this indicates the maximum number of bytes to read.
737 * If pcbRead is NULL the this is the exact number of bytes to read.
738 */
739 size_t cbToRead = pcbRead ? *pcbRead : cbBuffer;
740 size_t cbTotalRead = 0;
741 while (cbToRead > 0)
742 {
743 /*
744 * Kick of a an overlapped read. It should return immediately if
745 * there is bytes in the buffer. If not, we'll cancel it and see
746 * what we get back.
747 */
748 rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE);
749 DWORD cbRead = 0;
750 pThis->fIOPending = true;
751 RTCritSectLeave(&pThis->CritSect);
752
753 if (ReadFile(pThis->hNmPipe, pvBuffer,
754 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
755 &cbRead, &pThis->OverlappedIO))
756 rc = VINF_SUCCESS;
757 else if (GetLastError() == ERROR_IO_PENDING)
758 {
759 WaitForSingleObject(pThis->OverlappedIO.hEvent, INFINITE);
760 if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO,
761 &cbRead, TRUE /*fWait*/))
762 rc = VINF_SUCCESS;
763 else
764 rc = RTErrConvertFromWin32(GetLastError());
765 }
766 else
767 rc = RTErrConvertFromWin32(GetLastError());
768
769 RTCritSectEnter(&pThis->CritSect);
770 pThis->fIOPending = false;
771 if (RT_FAILURE(rc))
772 break;
773
774 /* Advance. */
775 cbToRead -= cbRead;
776 cbTotalRead += cbRead;
777 pvBuffer = (uint8_t *)pvBuffer + cbRead;
778 }
779
780 if (pcbRead)
781 {
782 *pcbRead = cbTotalRead;
783 if ( RT_FAILURE(rc)
784 && cbTotalRead
785 && rc != VERR_INVALID_POINTER)
786 rc = VINF_SUCCESS;
787 }
788
789 pThis->cRefs--;
790 }
791 else
792 rc = VERR_WRONG_ORDER;
793 RTCritSectLeave(&pThis->CritSect);
794 }
795
796 return rc;
797}
798
799
800/**
801 * Common worker for handling I/O completion.
802 *
803 * This is used by RTLocalIpcSessionClose and RTLocalIpcSessionWrite.
804 *
805 * @returns IPRT status code.
806 * @param pThis The pipe instance handle.
807 */
808static int rtLocalIpcSessionWriteCheckCompletion(PRTLOCALIPCSESSIONINT pThis)
809{
810 int rc;
811 DWORD dwRc = WaitForSingleObject(pThis->OverlappedIO.hEvent, 0);
812 if (dwRc == WAIT_OBJECT_0)
813 {
814 DWORD cbWritten = 0;
815 if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE))
816 {
817 for (;;)
818 {
819 if (cbWritten >= pThis->cbBounceBufUsed)
820 {
821 pThis->fIOPending = false;
822 rc = VINF_SUCCESS;
823 break;
824 }
825
826 /* resubmit the remainder of the buffer - can this actually happen? */
827 memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
828 rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE);
829 if (!WriteFile(pThis->hNmPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
830 &cbWritten, &pThis->OverlappedIO))
831 {
832 if (GetLastError() == ERROR_IO_PENDING)
833 rc = VINF_TRY_AGAIN;
834 else
835 {
836 pThis->fIOPending = false;
837 if (GetLastError() == ERROR_NO_DATA)
838 rc = VERR_BROKEN_PIPE;
839 else
840 rc = RTErrConvertFromWin32(GetLastError());
841 }
842 break;
843 }
844 Assert(cbWritten > 0);
845 }
846 }
847 else
848 {
849 pThis->fIOPending = false;
850 rc = RTErrConvertFromWin32(GetLastError());
851 }
852 }
853 else if (dwRc == WAIT_TIMEOUT)
854 rc = VINF_TRY_AGAIN;
855 else
856 {
857 pThis->fIOPending = false;
858 if (dwRc == WAIT_ABANDONED)
859 rc = VERR_INVALID_HANDLE;
860 else
861 rc = RTErrConvertFromWin32(GetLastError());
862 }
863 return rc;
864}
865
866
867RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuffer, size_t cbBuffer)
868{
869 PRTLOCALIPCSESSIONINT pThis = hSession;
870 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
871 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
872 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
873 AssertReturn(cbBuffer, VERR_INVALID_PARAMETER);
874
875 int rc = RTCritSectEnter(&pThis->CritSect);
876 if (RT_SUCCESS(rc))
877 {
878 /* No concurrent writers, sorry. */
879 if (pThis->cRefs == 0)
880 {
881 pThis->cRefs++;
882
883 /*
884 * If I/O is pending, wait for it to complete.
885 */
886 if (pThis->fIOPending)
887 {
888 rc = rtLocalIpcSessionWriteCheckCompletion(pThis);
889 while (rc == VINF_TRY_AGAIN)
890 {
891 Assert(pThis->fIOPending);
892 HANDLE hEvent = pThis->OverlappedIO.hEvent;
893 RTCritSectLeave(&pThis->CritSect);
894 WaitForSingleObject(pThis->OverlappedIO.hEvent, INFINITE);
895 RTCritSectEnter(&pThis->CritSect);
896 }
897 }
898 if (RT_SUCCESS(rc))
899 {
900 Assert(!pThis->fIOPending);
901
902 /*
903 * Try write everything.
904 * No bounce buffering, cUsers protects us.
905 */
906 size_t cbTotalWritten = 0;
907 while (cbBuffer > 0)
908 {
909 rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE);
910 pThis->fIOPending = true;
911 RTCritSectLeave(&pThis->CritSect);
912
913 DWORD cbWritten = 0;
914 if (WriteFile(pThis->hNmPipe, pvBuffer,
915 cbBuffer <= ~(DWORD)0 ? (DWORD)cbBuffer : ~(DWORD)0,
916 &cbWritten, &pThis->OverlappedIO))
917 rc = VINF_SUCCESS;
918 else if (GetLastError() == ERROR_IO_PENDING)
919 {
920 WaitForSingleObject(pThis->OverlappedIO.hEvent, INFINITE);
921 if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE /*fWait*/))
922 rc = VINF_SUCCESS;
923 else
924 rc = RTErrConvertFromWin32(GetLastError());
925 }
926 else if (GetLastError() == ERROR_NO_DATA)
927 rc = VERR_BROKEN_PIPE;
928 else
929 rc = RTErrConvertFromWin32(GetLastError());
930
931 RTCritSectEnter(&pThis->CritSect);
932 pThis->fIOPending = false;
933 if (RT_FAILURE(rc))
934 break;
935
936 /* Advance. */
937 pvBuffer = (char const *)pvBuffer + cbWritten;
938 cbTotalWritten += cbWritten;
939 cbBuffer -= cbWritten;
940 }
941 }
942
943 pThis->cRefs--;
944 }
945 else
946 rc = VERR_WRONG_ORDER;
947 RTCritSectLeave(&pThis->CritSect);
948 }
949
950 return rc;
951}
952
953
954RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession)
955{
956 /* No flushing on Windows needed since RTLocalIpcSessionWrite will block until
957 * all data was written (or an error occurred). */
958 /** @todo Implement this as soon as we want an explicit asynchronous version of
959 * RTLocalIpcSessionWrite on Windows. */
960 return VINF_SUCCESS;
961}
962
963
964RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies)
965{
966 PRTLOCALIPCSESSIONINT pThis = hSession;
967 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
968 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
969
970 int rc = RTCritSectEnter(&pThis->CritSect);
971 if (RT_SUCCESS(rc))
972 {
973 /* No concurrent waiters, sorry. */
974 if (pThis->cRefs == 0)
975 {
976 pThis->cRefs++;
977 RTCritSectLeave(&pThis->CritSect);
978
979 DWORD dwTimeout = cMillies == RT_INDEFINITE_WAIT
980 ? INFINITE : cMillies;
981 DWORD dwRc = WaitForSingleObject(pThis->OverlappedIO.hEvent, dwTimeout);
982
983 RTCritSectEnter(&pThis->CritSect);
984 if (dwRc == WAIT_TIMEOUT)
985 {
986 rc = VERR_TIMEOUT;
987 }
988 else if (dwRc == WAIT_ABANDONED)
989 {
990 rc = VERR_BROKEN_PIPE;
991 }
992 else if (dwRc == WAIT_FAILED)
993 rc = RTErrConvertFromWin32(GetLastError());
994
995 pThis->cRefs--;
996 }
997 else
998 rc = VERR_WRONG_ORDER;
999 RTCritSectLeave(&pThis->CritSect);
1000 }
1001
1002 return rc;
1003}
1004
1005
1006RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession)
1007{
1008 return VINF_SUCCESS;
1009}
1010
1011
1012RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess)
1013{
1014 return VERR_NOT_SUPPORTED;
1015}
1016
1017
1018RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid)
1019{
1020 return VERR_NOT_SUPPORTED;
1021}
1022
1023
1024RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTUID pUid)
1025{
1026 return VERR_NOT_SUPPORTED;
1027}
1028
Note: See TracBrowser for help on using the repository browser.

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