VirtualBox

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

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

IPRT/localipc-win.cpp: Update, added testcase (work in progress).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.2 KB
Line 
1/* $Id: localipc-win.cpp 47083 2013-07-10 15:38:57Z 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 * Builds and allocates the security descriptor required for securing the local pipe.
181 *
182 * @return IPRT status code.
183 * @param ppDesc Where to store the allocated security descriptor on success.
184 * Must be free'd using LocalFree().
185 */
186static int rtLocalIpcServerWinAllocSecurityDescriptior(PSECURITY_DESCRIPTOR *ppDesc)
187{
188 /** @todo Stuff this into RTInitOnce? Later. */
189 PFNCONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTOR
190 pfnConvertStringSecurityDescriptorToSecurityDescriptor = NULL;
191
192 RTLDRMOD hAdvApi32 = NIL_RTLDRMOD;
193 int rc = RTLdrLoadSystem("Advapi32.dll", true /*fNoUnload*/, &hAdvApi32);
194 if (RT_SUCCESS(rc))
195 rc = RTLdrGetSymbol(hAdvApi32, "ConvertStringSecurityDescriptorToSecurityDescriptorW",
196 (void**)&pfnConvertStringSecurityDescriptorToSecurityDescriptor);
197
198 PSECURITY_DESCRIPTOR pSecDesc = NULL;
199 if (RT_SUCCESS(rc))
200 {
201 AssertPtr(pfnConvertStringSecurityDescriptorToSecurityDescriptor);
202
203 /*
204 * We'll create a security descriptor from a SDDL that denies
205 * access to network clients (this is local IPC after all), it
206 * makes some further restrictions to prevent non-authenticated
207 * users from screwing around.
208 */
209 PRTUTF16 pwszSDDL;
210 rc = RTStrToUtf16(RTLOCALIPC_WIN_SDDL, &pwszSDDL);
211 if (RT_SUCCESS(rc))
212 {
213 if (!pfnConvertStringSecurityDescriptorToSecurityDescriptor((LPCTSTR)pwszSDDL,
214 SDDL_REVISION_1,
215 &pSecDesc,
216 NULL))
217 {
218 rc = RTErrConvertFromWin32(GetLastError());
219 }
220
221 RTUtf16Free(pwszSDDL);
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 AssertPtr(pSecDesc);
237 *ppDesc = pSecDesc;
238 }
239
240 return rc;
241}
242
243/**
244 * Creates a named pipe instance.
245 *
246 * This is used by both RTLocalIpcServerCreate and RTLocalIpcServerListen.
247 *
248 * @return IPRT status code.
249 * @param phNmPipe Where to store the named pipe handle on success. This
250 * will be set to INVALID_HANDLE_VALUE on failure.
251 * @param pszFullPipeName The full named pipe name.
252 * @param fFirst Set on the first call (from RTLocalIpcServerCreate), otherwise clear.
253 * Governs the FILE_FLAG_FIRST_PIPE_INSTANCE flag.
254 */
255static int rtLocalIpcServerWinCreatePipeInstance(PHANDLE phNmPipe, const char *pszFullPipeName, bool fFirst)
256{
257 *phNmPipe = INVALID_HANDLE_VALUE;
258
259 PSECURITY_DESCRIPTOR pSecDesc;
260 int rc = rtLocalIpcServerWinAllocSecurityDescriptior(&pSecDesc);
261 if (RT_SUCCESS(rc))
262 {
263 SECURITY_ATTRIBUTES SecAttrs;
264 SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
265 SecAttrs.lpSecurityDescriptor = pSecDesc;
266 SecAttrs.bInheritHandle = FALSE;
267
268 DWORD fOpenMode = PIPE_ACCESS_DUPLEX
269 | PIPE_WAIT
270 | FILE_FLAG_OVERLAPPED;
271
272 bool fSupportsFirstInstance = false;
273
274 OSVERSIONINFOEX OSInfoEx;
275 RT_ZERO(OSInfoEx);
276 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
277 if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
278 && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT)
279 {
280 if ( /* Vista+. */
281 OSInfoEx.dwMajorVersion >= 6
282 /* Windows XP+. */
283 || ( OSInfoEx.dwMajorVersion == 5
284 && OSInfoEx.dwMinorVersion > 0)
285 /* Windows 2000. */
286 || ( OSInfoEx.dwMajorVersion == 5
287 && OSInfoEx.dwMinorVersion == 0
288 && OSInfoEx.wServicePackMajor >= 2))
289 {
290 /* Requires at least W2K (5.0) SP2+. This is non-fatal. */
291 fSupportsFirstInstance = true;
292 }
293 }
294
295 if (fFirst && fSupportsFirstInstance)
296 fOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
297
298 HANDLE hNmPipe = CreateNamedPipe(pszFullPipeName, /* lpName */
299 fOpenMode, /* dwOpenMode */
300 PIPE_TYPE_BYTE, /* dwPipeMode */
301 PIPE_UNLIMITED_INSTANCES, /* nMaxInstances */
302 PAGE_SIZE, /* nOutBufferSize (advisory) */
303 PAGE_SIZE, /* nInBufferSize (ditto) */
304 30*1000, /* nDefaultTimeOut = 30 sec */
305 NULL /** @todo !!! Fix this !!! &SecAttrs */); /* lpSecurityAttributes */
306 LocalFree(pSecDesc);
307 if (hNmPipe != INVALID_HANDLE_VALUE)
308 {
309 *phNmPipe = hNmPipe;
310 }
311 else
312 rc = RTErrConvertFromWin32(GetLastError());
313 }
314
315 return rc;
316}
317
318
319RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszName, uint32_t fFlags)
320{
321 /*
322 * Basic parameter validation.
323 */
324 AssertPtrReturn(phServer, VERR_INVALID_POINTER);
325 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
326 AssertReturn(*pszName, VERR_INVALID_PARAMETER);
327 AssertReturn(!(fFlags & ~(RTLOCALIPC_FLAGS_VALID_MASK)), VERR_INVALID_PARAMETER);
328 AssertReturn((fFlags & RTLOCALIPC_FLAGS_MULTI_SESSION), VERR_INVALID_PARAMETER); /** @todo Implement !RTLOCALIPC_FLAGS_MULTI_SESSION */
329
330 /*
331 * Allocate and initialize the instance data.
332 */
333 size_t cchName = strlen(pszName);
334 size_t cch = RT_OFFSETOF(RTLOCALIPCSERVERINT, szName[cchName + sizeof(RTLOCALIPC_WIN_PREFIX)]);
335 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)RTMemAlloc(cch);
336 if (!pThis)
337 return VERR_NO_MEMORY;
338 pThis->u32Magic = RTLOCALIPCSERVER_MAGIC;
339 pThis->cRefs = 1; /* the one we return */
340 pThis->fCancelled = false;
341 memcpy(pThis->szName, RTLOCALIPC_WIN_PREFIX, sizeof(RTLOCALIPC_WIN_PREFIX) - 1);
342 memcpy(&pThis->szName[sizeof(RTLOCALIPC_WIN_PREFIX) - 1], pszName, cchName + 1);
343 int rc = RTCritSectInit(&pThis->CritSect);
344 if (RT_SUCCESS(rc))
345 {
346 pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
347 FALSE /*bInitialState*/, NULL /*lpName*/);
348 if (pThis->hEvent != NULL)
349 {
350 RT_ZERO(pThis->OverlappedIO);
351 pThis->OverlappedIO.Internal = STATUS_PENDING;
352 pThis->OverlappedIO.hEvent = pThis->hEvent;
353
354 rc = rtLocalIpcServerWinCreatePipeInstance(&pThis->hNmPipe,
355 pThis->szName, true /* fFirst */);
356 if (RT_SUCCESS(rc))
357 {
358 *phServer = pThis;
359 return VINF_SUCCESS;
360 }
361
362 BOOL fRc = CloseHandle(pThis->hEvent);
363 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
364 }
365 else
366 rc = RTErrConvertFromWin32(GetLastError());
367
368 int rc2 = RTCritSectDelete(&pThis->CritSect);
369 AssertRC(rc2);
370 }
371 RTMemFree(pThis);
372 return rc;
373}
374
375
376/**
377 * Call when the reference count reaches 0.
378 * Caller owns the critsect.
379 * @param pThis The instance to destroy.
380 */
381static void rtLocalIpcServerWinDestroy(PRTLOCALIPCSERVERINT pThis)
382{
383 BOOL fRc = CloseHandle(pThis->hNmPipe);
384 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
385 pThis->hNmPipe = INVALID_HANDLE_VALUE;
386
387 fRc = CloseHandle(pThis->hEvent);
388 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
389 pThis->hEvent = NULL;
390
391 RTCritSectLeave(&pThis->CritSect);
392 RTCritSectDelete(&pThis->CritSect);
393
394 RTMemFree(pThis);
395}
396
397
398RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer)
399{
400 /*
401 * Validate input.
402 */
403 if (hServer == NIL_RTLOCALIPCSERVER)
404 return VINF_SUCCESS;
405 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
406 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
407 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_MAGIC);
408
409 /*
410 * Cancel any thread currently busy using the server,
411 * leaving the cleanup to it.
412 */
413 RTCritSectEnter(&pThis->CritSect);
414 ASMAtomicUoWriteU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC);
415 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
416 pThis->cRefs--;
417
418 if (pThis->cRefs > 0)
419 {
420 BOOL fRc = SetEvent(pThis->hEvent);
421 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
422
423 RTCritSectLeave(&pThis->CritSect);
424 }
425 else
426 rtLocalIpcServerWinDestroy(pThis);
427
428 return VINF_SUCCESS;
429}
430
431
432RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION phClientSession)
433{
434 /*
435 * Validate input.
436 */
437 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
438 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
439 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_MAGIC);
440
441 /*
442 * Enter the critsect before inspecting the object further.
443 */
444 int rc;
445 RTCritSectEnter(&pThis->CritSect);
446 if (pThis->fCancelled)
447 {
448 pThis->fCancelled = false;
449 rc = VERR_CANCELLED;
450 RTCritSectLeave(&pThis->CritSect);
451 }
452 else
453 {
454 pThis->cRefs++;
455 ResetEvent(pThis->hEvent);
456 RTCritSectLeave(&pThis->CritSect);
457
458 /*
459 * Try connect a client. We need to use overlapped I/O here because
460 * of the cancellation done by RTLocalIpcServerCancel and RTLocalIpcServerDestroy.
461 */
462 SetLastError(NO_ERROR);
463 BOOL fRc = ConnectNamedPipe(pThis->hNmPipe, &pThis->OverlappedIO);
464 DWORD err = fRc ? NO_ERROR : GetLastError();
465 if ( !fRc
466 && err == ERROR_IO_PENDING)
467 {
468 WaitForSingleObject(pThis->hEvent, INFINITE);
469 DWORD dwIgnored;
470 fRc = GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &dwIgnored, FALSE /* bWait*/);
471 err = fRc ? NO_ERROR : GetLastError();
472 }
473
474 RTCritSectEnter(&pThis->CritSect);
475 if ( !pThis->fCancelled /* Event signalled but not cancelled? */
476 && pThis->u32Magic == RTLOCALIPCSERVER_MAGIC)
477 {
478 /*
479 * Still alive, some error or an actual client.
480 *
481 * If it's the latter we'll have to create a new pipe instance that
482 * replaces the current one for the server. The current pipe instance
483 * will be assigned to the client session.
484 */
485 if ( fRc
486 || err == ERROR_PIPE_CONNECTED)
487 {
488 HANDLE hNmPipe;
489 rc = rtLocalIpcServerWinCreatePipeInstance(&hNmPipe, pThis->szName, false /* fFirst */);
490 if (RT_SUCCESS(rc))
491 {
492 HANDLE hNmPipeSession = pThis->hNmPipe; /* consumed */
493 pThis->hNmPipe = hNmPipe;
494 rc = rtLocalIpcWinCreateSession(phClientSession, hNmPipeSession);
495 }
496 else
497 {
498 /*
499 * We failed to create a new instance for the server, disconnect
500 * the client and fail. Don't try service the client here.
501 */
502 fRc = DisconnectNamedPipe(pThis->hNmPipe);
503 AssertMsg(fRc, ("%d\n", GetLastError()));
504 }
505 }
506 else
507 rc = RTErrConvertFromWin32(err);
508 }
509 else
510 {
511 /*
512 * Cancelled or destroyed.
513 *
514 * Cancel the overlapped io if it didn't complete (must be done
515 * in the this thread) or disconnect the client.
516 */
517 if ( fRc
518 || err == ERROR_PIPE_CONNECTED)
519 fRc = DisconnectNamedPipe(pThis->hNmPipe);
520 else if (err == ERROR_IO_PENDING)
521 fRc = CancelIo(pThis->hNmPipe);
522 else
523 fRc = TRUE;
524 AssertMsg(fRc, ("%d\n", GetLastError()));
525 rc = VERR_CANCELLED;
526 }
527
528 pThis->cRefs--;
529 if (pThis->cRefs)
530 RTCritSectLeave(&pThis->CritSect);
531 else
532 rtLocalIpcServerWinDestroy(pThis);
533 }
534
535 return rc;
536}
537
538
539RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer)
540{
541 /*
542 * Validate input.
543 */
544 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
545 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
546 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_MAGIC);
547
548 /*
549 * Enter the critical section, then set the cancellation flag
550 * and signal the event (to wake up anyone in/at WaitForSingleObject).
551 */
552 RTCritSectEnter(&pThis->CritSect);
553
554 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
555 BOOL fRc = SetEvent(pThis->hEvent);
556 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
557
558 RTCritSectLeave(&pThis->CritSect);
559
560 return VINF_SUCCESS;
561}
562
563
564/**
565 * Create a session instance.
566 *
567 * @returns IPRT status code.
568 *
569 * @param phClientSession Where to store the session handle on success.
570 * @param hNmPipeSession The named pipe handle. This will be consumed by this session, meaning on failure
571 * to create the session it will be closed.
572 */
573static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSION phClientSession, HANDLE hNmPipeSession)
574{
575 AssertPtrReturn(phClientSession, VERR_INVALID_POINTER);
576 AssertReturn(hNmPipeSession != INVALID_HANDLE_VALUE, VERR_INVALID_HANDLE);
577
578 int rc;
579
580 /*
581 * Allocate and initialize the session instance data.
582 */
583 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAlloc(sizeof(*pThis));
584 if (pThis)
585 {
586 pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
587 pThis->cRefs = 1; /* our ref */
588 pThis->fCancelled = false;
589 pThis->hNmPipe = hNmPipeSession;
590
591 rc = RTCritSectInit(&pThis->CritSect);
592 if (RT_SUCCESS(rc))
593 {
594 pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
595 FALSE /*bInitialState*/, NULL /*lpName*/);
596 if (pThis->hEvent != NULL)
597 {
598 RT_ZERO(pThis->OverlappedIO);
599 pThis->OverlappedIO.Internal = STATUS_PENDING;
600 pThis->OverlappedIO.hEvent = pThis->hEvent;
601
602 *phClientSession = pThis;
603 return VINF_SUCCESS;
604 }
605
606 /* bail out */
607 rc = RTErrConvertFromWin32(GetLastError());
608 RTCritSectDelete(&pThis->CritSect);
609 }
610 RTMemFree(pThis);
611 }
612 else
613 rc = VERR_NO_MEMORY;
614
615 BOOL fRc = CloseHandle(hNmPipeSession);
616 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
617 return rc;
618}
619
620RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags)
621{
622 AssertPtrReturn(phSession, VERR_INVALID_POINTER);
623 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
624 AssertReturn(*pszName, VERR_INVALID_PARAMETER);
625 AssertReturn(!fFlags, VERR_INVALID_PARAMETER); /* Flags currently unused, must be 0. */
626
627 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAlloc(sizeof(*pThis));
628 if (!pThis)
629 return VERR_NO_MEMORY;
630
631 pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
632 pThis->cRefs = 1; /* The one we return. */
633 pThis->fIOPending = false;
634 pThis->fCancelled = false;
635 pThis->pbBounceBuf = NULL;
636 pThis->cbBounceBufAlloc = 0;
637 pThis->cbBounceBufUsed = 0;
638
639 int rc = RTCritSectInit(&pThis->CritSect);
640 if (RT_SUCCESS(rc))
641 {
642 pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
643 FALSE /*bInitialState*/, NULL /*lpName*/);
644 if (pThis->hEvent != NULL)
645 {
646 RT_ZERO(pThis->OverlappedIO);
647 pThis->OverlappedIO.Internal = STATUS_PENDING;
648 pThis->OverlappedIO.hEvent = pThis->hEvent;
649
650 char *pszPipe;
651 if (RTStrAPrintf(&pszPipe, "%s%s", RTLOCALIPC_WIN_PREFIX, pszName))
652 {
653 PSECURITY_DESCRIPTOR pSecDesc;
654 rc = rtLocalIpcServerWinAllocSecurityDescriptior(&pSecDesc);
655 if (RT_SUCCESS(rc))
656 {
657 SECURITY_ATTRIBUTES SecAttrs;
658 SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
659 SecAttrs.lpSecurityDescriptor = pSecDesc;
660 SecAttrs.bInheritHandle = FALSE;
661
662 HANDLE hPipe = CreateFile(pszPipe, /* pipe name */
663 GENERIC_READ | /* read and write access */
664 GENERIC_WRITE,
665 0, /* no sharing */
666 &SecAttrs, /* default security attributes */
667 OPEN_EXISTING, /* opens existing pipe */
668 FILE_FLAG_OVERLAPPED, /* default attributes */
669 NULL); /* no template file */
670 LocalFree(pSecDesc);
671 RTStrFree(pszPipe);
672
673 if (hPipe != INVALID_HANDLE_VALUE)
674 {
675 pThis->hNmPipe = hPipe;
676 *phSession = pThis;
677 return VINF_SUCCESS;
678 }
679 else
680 rc = RTErrConvertFromWin32(GetLastError());
681 }
682 }
683 else
684 rc = VERR_NO_MEMORY;
685
686 BOOL fRc = CloseHandle(pThis->hEvent);
687 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
688 }
689 else
690 rc = RTErrConvertFromWin32(GetLastError());
691
692 int rc2 = RTCritSectDelete(&pThis->CritSect);
693 AssertRC(rc2);
694 }
695
696 RTMemFree(pThis);
697 return rc;
698}
699
700
701/**
702 * Call when the reference count reaches 0.
703 * Caller owns the critsect.
704 * @param pThis The instance to destroy.
705 */
706static void rtLocalIpcSessionWinDestroy(PRTLOCALIPCSESSIONINT pThis)
707{
708 BOOL fRc = CloseHandle(pThis->hNmPipe);
709 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
710 pThis->hNmPipe = INVALID_HANDLE_VALUE;
711
712 fRc = CloseHandle(pThis->hEvent);
713 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
714 pThis->hEvent = NULL;
715
716 RTCritSectLeave(&pThis->CritSect);
717 RTCritSectDelete(&pThis->CritSect);
718
719 RTMemFree(pThis);
720}
721
722
723RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession)
724{
725 /*
726 * Validate input.
727 */
728 if (hSession == NIL_RTLOCALIPCSESSION)
729 return VINF_SUCCESS;
730 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
731 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
732 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_MAGIC);
733
734 /*
735 * Cancel any thread currently busy using the session,
736 * leaving the cleanup to it.
737 */
738 RTCritSectEnter(&pThis->CritSect);
739 ASMAtomicUoWriteU32(&pThis->u32Magic, ~RTLOCALIPCSESSION_MAGIC);
740 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
741 pThis->cRefs--;
742
743 if (pThis->cRefs > 0)
744 {
745 BOOL fRc = SetEvent(pThis->hEvent);
746 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
747
748 RTCritSectLeave(&pThis->CritSect);
749 }
750 else
751 rtLocalIpcSessionWinDestroy(pThis);
752
753 return VINF_SUCCESS;
754}
755
756
757RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
758{
759 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
760 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
761 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
762 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
763 /* pcbRead is optional. */
764
765 int rc = RTCritSectEnter(&pThis->CritSect);
766 if (RT_SUCCESS(rc))
767 {
768 /* No concurrent readers, sorry. */
769 if (pThis->cRefs == 0)
770 {
771 pThis->cRefs++;
772
773 /*
774 * If pcbRead is non-NULL this indicates the maximum number of bytes to read.
775 * If pcbRead is NULL the this is the exact number of bytes to read.
776 */
777 size_t cbToRead = pcbRead ? *pcbRead : cbBuffer;
778 size_t cbTotalRead = 0;
779 while (cbToRead > 0)
780 {
781 /*
782 * Kick of a an overlapped read. It should return immediately if
783 * there is bytes in the buffer. If not, we'll cancel it and see
784 * what we get back.
785 */
786 rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE);
787 DWORD cbRead = 0;
788 pThis->fIOPending = true;
789 RTCritSectLeave(&pThis->CritSect);
790
791 if (ReadFile(pThis->hNmPipe, pvBuffer,
792 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
793 &cbRead, &pThis->OverlappedIO))
794 rc = VINF_SUCCESS;
795 else if (GetLastError() == ERROR_IO_PENDING)
796 {
797 WaitForSingleObject(pThis->OverlappedIO.hEvent, INFINITE);
798 if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO,
799 &cbRead, TRUE /*fWait*/))
800 rc = VINF_SUCCESS;
801 else
802 rc = RTErrConvertFromWin32(GetLastError());
803 }
804 else
805 rc = RTErrConvertFromWin32(GetLastError());
806
807 RTCritSectEnter(&pThis->CritSect);
808 pThis->fIOPending = false;
809 if (RT_FAILURE(rc))
810 break;
811
812 /* Advance. */
813 cbToRead -= cbRead;
814 cbTotalRead += cbRead;
815 pvBuffer = (uint8_t *)pvBuffer + cbRead;
816 }
817
818 if (pcbRead)
819 {
820 *pcbRead = cbTotalRead;
821 if ( RT_FAILURE(rc)
822 && cbTotalRead
823 && rc != VERR_INVALID_POINTER)
824 rc = VINF_SUCCESS;
825 }
826
827 pThis->cRefs--;
828 }
829 else
830 rc = VERR_WRONG_ORDER;
831 RTCritSectLeave(&pThis->CritSect);
832 }
833
834 return rc;
835}
836
837
838/**
839 * Common worker for handling I/O completion.
840 *
841 * This is used by RTLocalIpcSessionClose and RTLocalIpcSessionWrite.
842 *
843 * @returns IPRT status code.
844 * @param pThis The pipe instance handle.
845 */
846static int rtLocalIpcSessionWriteCheckCompletion(PRTLOCALIPCSESSIONINT pThis)
847{
848 int rc;
849 DWORD dwRc = WaitForSingleObject(pThis->OverlappedIO.hEvent, 0);
850 if (dwRc == WAIT_OBJECT_0)
851 {
852 DWORD cbWritten = 0;
853 if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE))
854 {
855 for (;;)
856 {
857 if (cbWritten >= pThis->cbBounceBufUsed)
858 {
859 pThis->fIOPending = false;
860 rc = VINF_SUCCESS;
861 break;
862 }
863
864 /* resubmit the remainder of the buffer - can this actually happen? */
865 memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
866 rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE);
867 if (!WriteFile(pThis->hNmPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
868 &cbWritten, &pThis->OverlappedIO))
869 {
870 if (GetLastError() == ERROR_IO_PENDING)
871 rc = VINF_TRY_AGAIN;
872 else
873 {
874 pThis->fIOPending = false;
875 if (GetLastError() == ERROR_NO_DATA)
876 rc = VERR_BROKEN_PIPE;
877 else
878 rc = RTErrConvertFromWin32(GetLastError());
879 }
880 break;
881 }
882 Assert(cbWritten > 0);
883 }
884 }
885 else
886 {
887 pThis->fIOPending = false;
888 rc = RTErrConvertFromWin32(GetLastError());
889 }
890 }
891 else if (dwRc == WAIT_TIMEOUT)
892 rc = VINF_TRY_AGAIN;
893 else
894 {
895 pThis->fIOPending = false;
896 if (dwRc == WAIT_ABANDONED)
897 rc = VERR_INVALID_HANDLE;
898 else
899 rc = RTErrConvertFromWin32(GetLastError());
900 }
901 return rc;
902}
903
904
905RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuffer, size_t cbBuffer)
906{
907 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
908 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
909 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
910 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
911 AssertReturn(cbBuffer, VERR_INVALID_PARAMETER);
912
913 int rc = RTCritSectEnter(&pThis->CritSect);
914 if (RT_SUCCESS(rc))
915 {
916 /* No concurrent writers, sorry. */
917 if (pThis->cRefs == 0)
918 {
919 pThis->cRefs++;
920
921 /*
922 * If I/O is pending, wait for it to complete.
923 */
924 if (pThis->fIOPending)
925 {
926 rc = rtLocalIpcSessionWriteCheckCompletion(pThis);
927 while (rc == VINF_TRY_AGAIN)
928 {
929 Assert(pThis->fIOPending);
930 HANDLE hEvent = pThis->OverlappedIO.hEvent;
931 RTCritSectLeave(&pThis->CritSect);
932 WaitForSingleObject(pThis->OverlappedIO.hEvent, INFINITE);
933 RTCritSectEnter(&pThis->CritSect);
934 }
935 }
936 if (RT_SUCCESS(rc))
937 {
938 Assert(!pThis->fIOPending);
939
940 /*
941 * Try write everything.
942 * No bounce buffering, cUsers protects us.
943 */
944 size_t cbTotalWritten = 0;
945 while (cbBuffer > 0)
946 {
947 rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE);
948 pThis->fIOPending = true;
949 RTCritSectLeave(&pThis->CritSect);
950
951 DWORD cbWritten = 0;
952 if (WriteFile(pThis->hNmPipe, pvBuffer,
953 cbBuffer <= ~(DWORD)0 ? (DWORD)cbBuffer : ~(DWORD)0,
954 &cbWritten, &pThis->OverlappedIO))
955 rc = VINF_SUCCESS;
956 else if (GetLastError() == ERROR_IO_PENDING)
957 {
958 WaitForSingleObject(pThis->OverlappedIO.hEvent, INFINITE);
959 if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE /*fWait*/))
960 rc = VINF_SUCCESS;
961 else
962 rc = RTErrConvertFromWin32(GetLastError());
963 }
964 else if (GetLastError() == ERROR_NO_DATA)
965 rc = VERR_BROKEN_PIPE;
966 else
967 rc = RTErrConvertFromWin32(GetLastError());
968
969 RTCritSectEnter(&pThis->CritSect);
970 pThis->fIOPending = false;
971 if (RT_FAILURE(rc))
972 break;
973
974 /* Advance. */
975 pvBuffer = (char const *)pvBuffer + cbWritten;
976 cbTotalWritten += cbWritten;
977 cbBuffer -= cbWritten;
978 }
979 }
980
981 pThis->cRefs--;
982 }
983 else
984 rc = VERR_WRONG_ORDER;
985 RTCritSectLeave(&pThis->CritSect);
986 }
987
988 return rc;
989}
990
991
992RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession)
993{
994 /* No flushing on Windows needed since RTLocalIpcSessionWrite will block until
995 * all data was written (or an error occurred). */
996 /** @todo Implement this as soon as we want an explicit asynchronous version of
997 * RTLocalIpcSessionWrite on Windows. */
998 return VINF_SUCCESS;
999}
1000
1001
1002RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies)
1003{
1004 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1005 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1006 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1007
1008 int rc = RTCritSectEnter(&pThis->CritSect);
1009 if (RT_SUCCESS(rc))
1010 {
1011 /* No concurrent waiters, sorry. */
1012 if (pThis->cRefs == 0)
1013 {
1014 pThis->cRefs++;
1015 pThis->fCancelled = false; /* Reset canellation status. */
1016 RTCritSectLeave(&pThis->CritSect);
1017
1018 DWORD dwTimeout = cMillies == RT_INDEFINITE_WAIT
1019 ? INFINITE : cMillies;
1020 DWORD dwRc = WaitForSingleObject(pThis->OverlappedIO.hEvent, dwTimeout);
1021
1022 RTCritSectEnter(&pThis->CritSect);
1023 if (dwRc == WAIT_TIMEOUT)
1024 {
1025 rc = VERR_TIMEOUT;
1026 }
1027 else if (dwRc == WAIT_ABANDONED)
1028 {
1029 rc = VERR_BROKEN_PIPE;
1030 }
1031 else if (dwRc == WAIT_FAILED)
1032 rc = RTErrConvertFromWin32(GetLastError());
1033 else if (pThis->fCancelled)
1034 rc = VERR_CANCELLED;
1035
1036 pThis->cRefs--;
1037 }
1038 else
1039 rc = VERR_WRONG_ORDER;
1040 RTCritSectLeave(&pThis->CritSect);
1041 }
1042
1043 return rc;
1044}
1045
1046
1047RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession)
1048{
1049 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1050 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1051 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1052
1053 /*
1054 * Enter the critical section, then set the cancellation flag
1055 * and signal the event (to wake up anyone in/at WaitForSingleObject).
1056 */
1057 int rc = RTCritSectEnter(&pThis->CritSect);
1058 if (RT_SUCCESS(rc))
1059 {
1060 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
1061 BOOL fRc = SetEvent(pThis->hEvent);
1062 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
1063
1064 RTCritSectLeave(&pThis->CritSect);
1065 }
1066
1067 return rc;
1068}
1069
1070
1071RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess)
1072{
1073 return VERR_NOT_SUPPORTED;
1074}
1075
1076
1077RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid)
1078{
1079 return VERR_NOT_SUPPORTED;
1080}
1081
1082
1083RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTUID pUid)
1084{
1085 return VERR_NOT_SUPPORTED;
1086}
1087
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