VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DrvNamedPipe.cpp@ 68699

Last change on this file since 68699 was 68699, checked in by vboxsync, 8 years ago

pdmifs.h,Serial: Reworked stream interface. The old design with the two read/write threads had a race where the read thread could access already destroyed VMM structures during destruction if data was read. This was solved by adding a poll callback which waits for data to arrive and which can be interrupt to make the thread respond to VM state changes and suspend before destruction starts. This required reworking all the drivers using it. DrvTCP was reworked to make use of the RTTcp*, RTSocket* and RTPoll* API in that process to get rid of platform dependent code there (which wasn't all available when the driver was createt).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.0 KB
Line 
1 /* $Id: DrvNamedPipe.cpp 68699 2017-09-07 15:12:54Z vboxsync $ */
2/** @file
3 * Named pipe / local socket stream driver.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_NAMEDPIPE
23#include <VBox/vmm/pdmdrv.h>
24#include <iprt/assert.h>
25#include <iprt/file.h>
26#include <iprt/stream.h>
27#include <iprt/alloc.h>
28#include <iprt/pipe.h>
29#include <iprt/poll.h>
30#include <iprt/string.h>
31#include <iprt/semaphore.h>
32#include <iprt/socket.h>
33#include <iprt/uuid.h>
34
35#include "VBoxDD.h"
36
37#ifdef RT_OS_WINDOWS
38# include <iprt/win/windows.h>
39#else /* !RT_OS_WINDOWS */
40# include <errno.h>
41# include <unistd.h>
42# include <sys/types.h>
43# include <sys/socket.h>
44# include <sys/un.h>
45# ifndef SHUT_RDWR /* OS/2 */
46# define SHUT_RDWR 3
47# endif
48#endif /* !RT_OS_WINDOWS */
49
50
51/*********************************************************************************************************************************
52* Defined Constants And Macros *
53*********************************************************************************************************************************/
54
55#ifndef RT_OS_WINDOWS
56# define DRVNAMEDPIPE_POLLSET_ID_SOCKET 0
57# define DRVNAMEDPIPE_POLLSET_ID_WAKEUP 1
58#endif
59
60# define DRVNAMEDPIPE_WAKEUP_REASON_EXTERNAL 0
61# define DRVNAMEDPIPE_WAKEUP_REASON_NEW_CONNECTION 1
62
63/*********************************************************************************************************************************
64* Structures and Typedefs *
65*********************************************************************************************************************************/
66/**
67 * Named pipe driver instance data.
68 *
69 * @implements PDMISTREAM
70 */
71typedef struct DRVNAMEDPIPE
72{
73 /** The stream interface. */
74 PDMISTREAM IStream;
75 /** Pointer to the driver instance. */
76 PPDMDRVINS pDrvIns;
77 /** Pointer to the named pipe file name. (Freed by MM) */
78 char *pszLocation;
79 /** Flag whether VirtualBox represents the server or client side. */
80 bool fIsServer;
81#ifdef RT_OS_WINDOWS
82 /** File handle of the named pipe. */
83 HANDLE NamedPipe;
84 /** The wake event handle. */
85 HANDLE hEvtWake;
86 /** Overlapped structure for writes. */
87 OVERLAPPED OverlappedWrite;
88 /** Overlapped structure for reads. */
89 OVERLAPPED OverlappedRead;
90 /** Listen thread wakeup semaphore */
91 RTSEMEVENTMULTI ListenSem;
92 /** Read buffer. */
93 uint8_t abBufRead[32];
94 /** Write buffer. */
95 uint8_t abBufWrite[32];
96 /** Read buffer currently used. */
97 size_t cbReadBufUsed;
98 /** Size of the write buffer used. */
99 size_t cbWriteBufUsed;
100 /** Flag whether a wake operation was caused by an external trigger. */
101 volatile bool fWakeExternal;
102 /** Flag whether a read was started. */
103 bool fReadPending;
104#else /* !RT_OS_WINDOWS */
105 /** Poll set used to wait for I/O events. */
106 RTPOLLSET hPollSet;
107 /** Reading end of the wakeup pipe. */
108 RTPIPE hPipeWakeR;
109 /** Writing end of the wakeup pipe. */
110 RTPIPE hPipeWakeW;
111 /** Socket handle. */
112 RTSOCKET hSock;
113 /** Flag whether the socket is in the pollset. */
114 bool fSockInPollSet;
115 /** Socket handle of the local socket for server. */
116 int LocalSocketServer;
117#endif /* !RT_OS_WINDOWS */
118 /** Thread for listening for new connections. */
119 RTTHREAD ListenThread;
120 /** Flag to signal listening thread to shut down. */
121 bool volatile fShutdown;
122} DRVNAMEDPIPE, *PDRVNAMEDPIPE;
123
124
125/*********************************************************************************************************************************
126* Internal Functions *
127*********************************************************************************************************************************/
128
129
130/**
131 * Kicks any possibly polling thread to get informed about changes.
132 *
133 * @returns VBOx status code.
134 * @param pThis The named pipe driver instance.
135 * @param bReason The reason code to handle.
136 */
137static int drvNamedPipePollerKick(PDRVNAMEDPIPE pThis, uint8_t bReason)
138{
139#ifdef RT_OS_WINDOWS
140 if (bReason == DRVNAMEDPIPE_WAKEUP_REASON_EXTERNAL)
141 ASMAtomicXchgBool(&pThis->fWakeExternal, true);
142 if (!SetEvent(pThis->hEvtWake))
143 return RTErrConvertFromWin32(GetLastError());
144
145 return VINF_SUCCESS;
146#else
147 size_t cbWritten = 0;
148 return RTPipeWrite(pThis->hPipeWakeW, &bReason, 1, &cbWritten);
149#endif
150}
151
152
153/** @interface_method_impl{PDMISTREAM,pfnPoll} */
154static DECLCALLBACK(int) drvNamedPipePoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies)
155{
156 int rc = VINF_SUCCESS;
157 PDRVNAMEDPIPE pThis = RT_FROM_MEMBER(pInterface, DRVNAMEDPIPE, IStream);
158
159 LogFlowFunc(("pInterface=%#p fEvts=%#x pfEvts=%#p cMillies=%u\n", pInterface, fEvts, pfEvts, cMillies));
160
161#ifdef RT_OS_WINDOWS
162 /* Immediately return if there is something to read or no write pending and the respective events are set. */
163 *pfEvts = 0;
164 if ( (fEvts & RTPOLL_EVT_READ)
165 && pThis->cbReadBufUsed > 0)
166 *pfEvts |= RTPOLL_EVT_READ;
167 if ( (fEvts & RTPOLL_EVT_WRITE)
168 && !pThis->cbWriteBufUsed)
169 *pfEvts |= RTPOLL_EVT_WRITE;
170
171 if (*pfEvts)
172 return VINF_SUCCESS;
173
174 while (RT_SUCCESS(rc))
175 {
176 /* Set up the waiting handles. */
177 HANDLE ahEvts[3];
178 unsigned cEvts = 0;
179
180 ahEvts[cEvts++] = pThis->hEvtWake;
181 if (fEvts & RTPOLL_EVT_WRITE)
182 {
183 Assert(pThis->cbWriteBufUsed);
184 ahEvts[cEvts++] = pThis->OverlappedWrite.hEvent;
185 }
186 if ( (fEvts & RTPOLL_EVT_READ)
187 && pThis->NamedPipe != INVALID_HANDLE_VALUE
188 && !pThis->fReadPending)
189 {
190 Assert(!pThis->cbReadBufUsed);
191
192 DWORD cbReallyRead;
193 pThis->OverlappedRead.Offset = 0;
194 pThis->OverlappedRead.OffsetHigh = 0;
195 if (!ReadFile(pThis->NamedPipe, &pThis->abBufRead[0], sizeof(pThis->abBufRead), &cbReallyRead, &pThis->OverlappedRead))
196 {
197 DWORD uError = GetLastError();
198
199 if (uError == ERROR_IO_PENDING)
200 {
201 uError = 0;
202 pThis->fReadPending = true;
203 }
204
205 if ( uError == ERROR_PIPE_LISTENING
206 || uError == ERROR_PIPE_NOT_CONNECTED)
207 {
208 /* No connection yet/anymore */
209 cbReallyRead = 0;
210 }
211 else
212 {
213 rc = RTErrConvertFromWin32(uError);
214 Log(("drvNamedPipePoll: ReadFile returned %d (%Rrc)\n", uError, rc));
215 }
216 }
217 else
218 {
219 LogFlowFunc(("Read completed: cbReallyRead=%u\n", cbReallyRead));
220 pThis->fReadPending = false;
221 *pfEvts |= RTPOLL_EVT_READ;
222 return VINF_SUCCESS;
223 }
224
225 if (RT_FAILURE(rc))
226 {
227 Log(("drvNamedPipePoll: FileRead returned %Rrc fShutdown=%d\n", rc, pThis->fShutdown));
228 if ( !pThis->fShutdown
229 && ( rc == VERR_EOF
230 || rc == VERR_BROKEN_PIPE
231 )
232 )
233 {
234 FlushFileBuffers(pThis->NamedPipe);
235 DisconnectNamedPipe(pThis->NamedPipe);
236 if (!pThis->fIsServer)
237 {
238 CloseHandle(pThis->NamedPipe);
239 pThis->NamedPipe = INVALID_HANDLE_VALUE;
240 }
241 /* pretend success */
242 rc = VINF_SUCCESS;
243 }
244 cbReallyRead = 0;
245 }
246 }
247
248 if (pThis->fReadPending)
249 ahEvts[cEvts++] = pThis->OverlappedRead.hEvent;
250
251 DWORD dwMillies = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies;
252 DWORD uErr = WaitForMultipleObjects(cEvts, &ahEvts[0], FALSE /* bWaitAll */, dwMillies);
253 if (uErr == WAIT_TIMEOUT)
254 rc = VERR_TIMEOUT;
255 else if (uErr == WAIT_FAILED)
256 rc = RTErrConvertFromWin32(GetLastError());
257 else
258 {
259 /* Something triggered. */
260 unsigned idxEvt = uErr - WAIT_OBJECT_0;
261 Assert(idxEvt < cEvts);
262
263 LogFlowFunc(("Interrupted by pipe activity: idxEvt=%u\n", idxEvt));
264
265 if (idxEvt == 0)
266 {
267 /* The wakeup triggered. */
268 if (ASMAtomicXchgBool(&pThis->fWakeExternal, false))
269 rc = VERR_INTERRUPTED;
270 else
271 {
272 /*
273 * Internal event because there was a new connection from the listener thread,
274 * restart everything.
275 */
276 rc = VINF_SUCCESS;
277 }
278 }
279 else if (ahEvts[idxEvt] == pThis->OverlappedWrite.hEvent)
280 {
281 LogFlowFunc(("Write completed\n"));
282 /* Fetch the result of the write. */
283 DWORD cbWritten = 0;
284 if (GetOverlappedResult(pThis->NamedPipe, &pThis->OverlappedWrite, &cbWritten, TRUE) == FALSE)
285 {
286 uErr = GetLastError();
287 rc = RTErrConvertFromWin32(uErr);
288 Log(("drvNamedPipePoll: Write completed with %d (%Rrc)\n", uErr, rc));
289
290 if (RT_FAILURE(rc))
291 {
292 /** @todo WriteFile(pipe) has been observed to return ERROR_NO_DATA
293 * (VERR_NO_DATA) instead of ERROR_BROKEN_PIPE, when the pipe is
294 * disconnected. */
295 if ( rc == VERR_EOF
296 || rc == VERR_BROKEN_PIPE)
297 {
298 FlushFileBuffers(pThis->NamedPipe);
299 DisconnectNamedPipe(pThis->NamedPipe);
300 if (!pThis->fIsServer)
301 {
302 CloseHandle(pThis->NamedPipe);
303 pThis->NamedPipe = INVALID_HANDLE_VALUE;
304 }
305 /* pretend success */
306 rc = VINF_SUCCESS;
307 }
308 cbWritten = (DWORD)pThis->cbWriteBufUsed;
309 }
310 }
311
312 pThis->cbWriteBufUsed -= cbWritten;
313 if (!pThis->cbWriteBufUsed && (fEvts & RTPOLL_EVT_WRITE))
314 {
315 *pfEvts |= RTPOLL_EVT_WRITE;
316 break;
317 }
318 }
319 else
320 {
321 Assert(ahEvts[idxEvt] == pThis->OverlappedRead.hEvent);
322
323 DWORD cbRead = 0;
324 if (GetOverlappedResult(pThis->NamedPipe, &pThis->OverlappedRead, &cbRead, TRUE) == FALSE)
325 {
326 uErr = GetLastError();
327 rc = RTErrConvertFromWin32(uErr);
328 Log(("drvNamedPipePoll: Read completed with %d (%Rrc)\n", uErr, rc));
329
330 if (RT_FAILURE(rc))
331 {
332 /** @todo WriteFile(pipe) has been observed to return ERROR_NO_DATA
333 * (VERR_NO_DATA) instead of ERROR_BROKEN_PIPE, when the pipe is
334 * disconnected. */
335 if ( rc == VERR_EOF
336 || rc == VERR_BROKEN_PIPE)
337 {
338 FlushFileBuffers(pThis->NamedPipe);
339 DisconnectNamedPipe(pThis->NamedPipe);
340 if (!pThis->fIsServer)
341 {
342 CloseHandle(pThis->NamedPipe);
343 pThis->NamedPipe = INVALID_HANDLE_VALUE;
344 }
345 /* pretend success */
346 rc = VINF_SUCCESS;
347 }
348 cbRead = 0;
349 }
350 }
351
352 LogFlowFunc(("Read completed with cbRead=%u\n", cbRead));
353 pThis->fReadPending = false;
354 pThis->cbReadBufUsed = cbRead;
355 if (pThis->cbReadBufUsed && (fEvts & RTPOLL_EVT_READ))
356 {
357 *pfEvts |= RTPOLL_EVT_READ;
358 break;
359 }
360 }
361 }
362 }
363#else
364 if (pThis->hSock != NIL_RTSOCKET)
365 {
366 if (!pThis->fSockInPollSet)
367 {
368 rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hSock,
369 fEvts, DRVNAMEDPIPE_POLLSET_ID_SOCKET);
370 if (RT_SUCCESS(rc))
371 pThis->fSockInPollSet = true;
372 }
373 else
374 {
375 /* Always include error event. */
376 fEvts |= RTPOLL_EVT_ERROR;
377 rc = RTPollSetEventsChange(pThis->hPollSet, DRVNAMEDPIPE_POLLSET_ID_SOCKET, fEvts);
378 AssertRC(rc);
379 }
380 }
381
382 while (RT_SUCCESS(rc))
383 {
384 uint32_t fEvtsRecv = 0;
385 uint32_t idHnd = 0;
386
387 rc = RTPoll(pThis->hPollSet, cMillies, &fEvtsRecv, &idHnd);
388 if (RT_SUCCESS(rc))
389 {
390 if (idHnd == DRVNAMEDPIPE_POLLSET_ID_WAKEUP)
391 {
392 /* We got woken up, drain the pipe and return. */
393 uint8_t bReason;
394 size_t cbRead = 0;
395 rc = RTPipeRead(pThis->hPipeWakeR, &bReason, 1, &cbRead);
396 AssertRC(rc);
397
398 if (bReason == DRVNAMEDPIPE_WAKEUP_REASON_EXTERNAL)
399 rc = VERR_INTERRUPTED;
400 else if (bReason == DRVNAMEDPIPE_WAKEUP_REASON_NEW_CONNECTION)
401 {
402 Assert(!pThis->fSockInPollSet);
403 rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hSock,
404 fEvts, DRVNAMEDPIPE_POLLSET_ID_SOCKET);
405 if (RT_SUCCESS(rc))
406 pThis->fSockInPollSet = true;
407 }
408 else
409 AssertMsgFailed(("Unknown wakeup reason in pipe %u\n", bReason));
410 }
411 else
412 {
413 Assert(idHnd == DRVNAMEDPIPE_POLLSET_ID_SOCKET);
414
415 /* On error we close the socket here. */
416 if (fEvtsRecv & RTPOLL_EVT_ERROR)
417 {
418 rc = RTPollSetRemove(pThis->hPollSet, DRVNAMEDPIPE_POLLSET_ID_SOCKET);
419 AssertRC(rc);
420
421 RTSocketClose(pThis->hSock);
422 pThis->hSock = NIL_RTSOCKET;
423 pThis->fSockInPollSet = false;
424 /* Continue with polling. */
425 }
426 else
427 {
428 *pfEvts = fEvtsRecv;
429 break;
430 }
431 }
432 }
433 }
434#endif
435
436 LogFlowFunc(("returns %Rrc\n", rc));
437 return rc;
438}
439
440
441/** @interface_method_impl{PDMISTREAM,pfnPollInterrupt} */
442static DECLCALLBACK(int) drvNamedPipePollInterrupt(PPDMISTREAM pInterface)
443{
444 PDRVNAMEDPIPE pThis = RT_FROM_MEMBER(pInterface, DRVNAMEDPIPE, IStream);
445 return drvNamedPipePollerKick(pThis, DRVNAMEDPIPE_WAKEUP_REASON_EXTERNAL);
446}
447
448
449/** @interface_method_impl{PDMISTREAM,pfnRead} */
450static DECLCALLBACK(int) drvNamedPipeRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)
451{
452 int rc = VINF_SUCCESS;
453 PDRVNAMEDPIPE pThis = RT_FROM_MEMBER(pInterface, DRVNAMEDPIPE, IStream);
454 LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation));
455
456 Assert(pvBuf);
457#ifdef RT_OS_WINDOWS
458 if (pThis->NamedPipe != INVALID_HANDLE_VALUE)
459 {
460 /* Check if there is something in the read buffer and return as much as we can. */
461 if (pThis->cbReadBufUsed)
462 {
463 size_t cbRead = RT_MIN(*pcbRead, pThis->cbReadBufUsed);
464
465 memcpy(pvBuf, &pThis->abBufRead[0], cbRead);
466 if (cbRead < pThis->cbReadBufUsed)
467 memmove(&pThis->abBufRead[0], &pThis->abBufRead[cbRead], pThis->cbReadBufUsed - cbRead);
468 pThis->cbReadBufUsed -= cbRead;
469 *pcbRead = cbRead;
470 }
471 else
472 *pcbRead = 0;
473 }
474#else /* !RT_OS_WINDOWS */
475 if (pThis->hSock != NIL_RTSOCKET)
476 {
477 size_t cbRead;
478 size_t cbBuf = *pcbRead;
479 rc = RTSocketReadNB(pThis->hSock, pvBuf, cbBuf, &cbRead);
480 if (RT_SUCCESS(rc))
481 {
482 if (!cbRead && rc != VINF_TRY_AGAIN)
483 {
484 rc = RTPollSetRemove(pThis->hPollSet, DRVNAMEDPIPE_POLLSET_ID_SOCKET);
485 AssertRC(rc);
486
487 RTSocketClose(pThis->hSock);
488 pThis->hSock = NIL_RTSOCKET;
489 pThis->fSockInPollSet = false;
490 rc = VINF_SUCCESS;
491 }
492 *pcbRead = cbRead;
493 }
494 }
495#endif /* !RT_OS_WINDOWS */
496 else
497 {
498 RTThreadSleep(100);
499 *pcbRead = 0;
500 }
501
502 LogFlow(("%s: *pcbRead=%zu returns %Rrc\n", __FUNCTION__, *pcbRead, rc));
503 return rc;
504}
505
506
507/** @interface_method_impl{PDMISTREAM,pfnWrite} */
508static DECLCALLBACK(int) drvNamedPipeWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite)
509{
510 int rc = VINF_SUCCESS;
511 PDRVNAMEDPIPE pThis = RT_FROM_MEMBER(pInterface, DRVNAMEDPIPE, IStream);
512 LogFlow(("%s: pvBuf=%p *pcbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbWrite, pThis->pszLocation));
513
514 Assert(pvBuf);
515#ifdef RT_OS_WINDOWS
516 if (pThis->NamedPipe != INVALID_HANDLE_VALUE)
517 {
518 /* Accept the data in case the write buffer is empty. */
519 if (!pThis->cbWriteBufUsed)
520 {
521 size_t cbWrite = RT_MIN(*pcbWrite, sizeof(pThis->cbWriteBufUsed));
522
523 memcpy(&pThis->abBufWrite[0], pvBuf, cbWrite);
524 pThis->cbWriteBufUsed += cbWrite;
525
526 /* Initiate the write. */
527 pThis->OverlappedWrite.Offset = 0;
528 pThis->OverlappedWrite.OffsetHigh = 0;
529 if (!WriteFile(pThis->NamedPipe, pvBuf, (DWORD)cbWrite, NULL, &pThis->OverlappedWrite))
530 {
531 DWORD uError = GetLastError();
532
533 if ( uError == ERROR_PIPE_LISTENING
534 || uError == ERROR_PIPE_NOT_CONNECTED)
535 {
536 /* No connection yet/anymore; just discard the write (pretending everything was written). */
537 pThis->cbWriteBufUsed = 0;
538 cbWrite = *pcbWrite;
539 }
540 else if (uError != ERROR_IO_PENDING) /* We wait for the write to complete in the poll callback. */
541 {
542 rc = RTErrConvertFromWin32(uError);
543 Log(("drvNamedPipeWrite: WriteFile returned %d (%Rrc)\n", uError, rc));
544 cbWrite = 0;
545 }
546 }
547
548 if (RT_FAILURE(rc))
549 {
550 /** @todo WriteFile(pipe) has been observed to return ERROR_NO_DATA
551 * (VERR_NO_DATA) instead of ERROR_BROKEN_PIPE, when the pipe is
552 * disconnected. */
553 if ( rc == VERR_EOF
554 || rc == VERR_BROKEN_PIPE)
555 {
556 FlushFileBuffers(pThis->NamedPipe);
557 DisconnectNamedPipe(pThis->NamedPipe);
558 if (!pThis->fIsServer)
559 {
560 CloseHandle(pThis->NamedPipe);
561 pThis->NamedPipe = INVALID_HANDLE_VALUE;
562 }
563 /* pretend success */
564 rc = VINF_SUCCESS;
565 }
566 cbWrite = 0;
567 }
568
569 *pcbWrite = cbWrite;
570 }
571 else
572 *pcbWrite = 0;
573 }
574#else /* !RT_OS_WINDOWS */
575 if (pThis->hSock != NIL_RTSOCKET)
576 {
577 size_t cbBuf = *pcbWrite;
578 rc = RTSocketWriteNB(pThis->hSock, pvBuf, cbBuf, pcbWrite);
579 }
580 else
581 *pcbWrite = 0;
582#endif /* !RT_OS_WINDOWS */
583
584 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
585 return rc;
586}
587
588
589/**
590 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
591 */
592static DECLCALLBACK(void *) drvNamedPipeQueryInterface(PPDMIBASE pInterface, const char *pszIID)
593{
594 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
595 PDRVNAMEDPIPE pThis = PDMINS_2_DATA(pDrvIns, PDRVNAMEDPIPE);
596 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
597 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISTREAM, &pThis->IStream);
598 return NULL;
599}
600
601
602/* -=-=-=-=- listen thread -=-=-=-=- */
603
604/**
605 * Receive thread loop.
606 *
607 * @returns 0 on success.
608 * @param hThreadSelf Thread handle to this thread.
609 * @param pvUser User argument.
610 */
611static DECLCALLBACK(int) drvNamedPipeListenLoop(RTTHREAD hThreadSelf, void *pvUser)
612{
613 RT_NOREF(hThreadSelf);
614 PDRVNAMEDPIPE pThis = (PDRVNAMEDPIPE)pvUser;
615 int rc = VINF_SUCCESS;
616#ifdef RT_OS_WINDOWS
617 HANDLE NamedPipe = pThis->NamedPipe;
618 HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, 0);
619#endif
620
621 while (RT_LIKELY(!pThis->fShutdown))
622 {
623#ifdef RT_OS_WINDOWS
624 OVERLAPPED overlapped;
625
626 memset(&overlapped, 0, sizeof(overlapped));
627 overlapped.hEvent = hEvent;
628
629 BOOL fConnected = ConnectNamedPipe(NamedPipe, &overlapped);
630 if ( !fConnected
631 && !pThis->fShutdown)
632 {
633 DWORD hrc = GetLastError();
634
635 if (hrc == ERROR_IO_PENDING)
636 {
637 DWORD dummy;
638
639 hrc = 0;
640 if (GetOverlappedResult(pThis->NamedPipe, &overlapped, &dummy, TRUE) == FALSE)
641 hrc = GetLastError();
642 else
643 drvNamedPipePollerKick(pThis, DRVNAMEDPIPE_WAKEUP_REASON_NEW_CONNECTION);
644 }
645
646 if (pThis->fShutdown)
647 break;
648
649 if (hrc == ERROR_PIPE_CONNECTED)
650 {
651 RTSemEventMultiWait(pThis->ListenSem, 250);
652 }
653 else if (hrc != ERROR_SUCCESS)
654 {
655 rc = RTErrConvertFromWin32(hrc);
656 LogRel(("NamedPipe%d: ConnectNamedPipe failed, rc=%Rrc\n", pThis->pDrvIns->iInstance, rc));
657 break;
658 }
659 }
660#else /* !RT_OS_WINDOWS */
661 if (listen(pThis->LocalSocketServer, 0) == -1)
662 {
663 rc = RTErrConvertFromErrno(errno);
664 LogRel(("NamedPipe%d: listen failed, rc=%Rrc\n", pThis->pDrvIns->iInstance, rc));
665 break;
666 }
667 int s = accept(pThis->LocalSocketServer, NULL, NULL);
668 if (s == -1)
669 {
670 rc = RTErrConvertFromErrno(errno);
671 LogRel(("NamedPipe%d: accept failed, rc=%Rrc\n", pThis->pDrvIns->iInstance, rc));
672 break;
673 }
674 if (pThis->hSock != NIL_RTSOCKET)
675 {
676 LogRel(("NamedPipe%d: only single connection supported\n", pThis->pDrvIns->iInstance));
677 close(s);
678 }
679 else
680 {
681 RTSOCKET hSockNew = NIL_RTSOCKET;
682 rc = RTSocketFromNative(&hSockNew, s);
683 if (RT_SUCCESS(rc))
684 {
685 pThis->hSock = hSockNew;
686 /* Inform the poller about the new socket. */
687 drvNamedPipePollerKick(pThis, DRVNAMEDPIPE_WAKEUP_REASON_NEW_CONNECTION);
688 }
689 else
690 {
691 LogRel(("NamedPipe%d: Failed to wrap socket with %Rrc\n", pThis->pDrvIns->iInstance));
692 close(s);
693 }
694 }
695#endif /* !RT_OS_WINDOWS */
696 }
697
698#ifdef RT_OS_WINDOWS
699 CloseHandle(hEvent);
700#endif
701 return VINF_SUCCESS;
702}
703
704/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
705
706/**
707 * Common worker for drvNamedPipePowerOff and drvNamedPipeDestructor.
708 *
709 * @param pThis The instance data.
710 */
711static void drvNamedPipeShutdownListener(PDRVNAMEDPIPE pThis)
712{
713 /*
714 * Signal shutdown of the listener thread.
715 */
716 pThis->fShutdown = true;
717#ifdef RT_OS_WINDOWS
718 if ( pThis->fIsServer
719 && pThis->NamedPipe != INVALID_HANDLE_VALUE)
720 {
721 FlushFileBuffers(pThis->NamedPipe);
722 DisconnectNamedPipe(pThis->NamedPipe);
723
724 BOOL fRc = CloseHandle(pThis->NamedPipe);
725 Assert(fRc); NOREF(fRc);
726 pThis->NamedPipe = INVALID_HANDLE_VALUE;
727
728 /* Wake up listen thread */
729 if (pThis->ListenSem != NIL_RTSEMEVENT)
730 RTSemEventMultiSignal(pThis->ListenSem);
731 }
732#else
733 if ( pThis->fIsServer
734 && pThis->LocalSocketServer != -1)
735 {
736 int rc = shutdown(pThis->LocalSocketServer, SHUT_RDWR);
737 AssertRC(rc == 0); NOREF(rc);
738
739 rc = close(pThis->LocalSocketServer);
740 AssertRC(rc == 0);
741 pThis->LocalSocketServer = -1;
742 }
743#endif
744}
745
746
747/**
748 * Power off a named pipe stream driver instance.
749 *
750 * This does most of the destruction work, to avoid ordering dependencies.
751 *
752 * @param pDrvIns The driver instance data.
753 */
754static DECLCALLBACK(void) drvNamedPipePowerOff(PPDMDRVINS pDrvIns)
755{
756 PDRVNAMEDPIPE pThis = PDMINS_2_DATA(pDrvIns, PDRVNAMEDPIPE);
757 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
758
759 drvNamedPipeShutdownListener(pThis);
760}
761
762
763/**
764 * Destruct a named pipe stream driver instance.
765 *
766 * Most VM resources are freed by the VM. This callback is provided so that
767 * any non-VM resources can be freed correctly.
768 *
769 * @param pDrvIns The driver instance data.
770 */
771static DECLCALLBACK(void) drvNamedPipeDestruct(PPDMDRVINS pDrvIns)
772{
773 PDRVNAMEDPIPE pThis = PDMINS_2_DATA(pDrvIns, PDRVNAMEDPIPE);
774 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
775 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
776
777 drvNamedPipeShutdownListener(pThis);
778
779 /*
780 * While the thread exits, clean up as much as we can.
781 */
782#ifdef RT_OS_WINDOWS
783 if (pThis->NamedPipe != INVALID_HANDLE_VALUE)
784 {
785 CloseHandle(pThis->NamedPipe);
786 pThis->NamedPipe = INVALID_HANDLE_VALUE;
787 }
788 if (pThis->OverlappedRead.hEvent != NULL)
789 {
790 CloseHandle(pThis->OverlappedRead.hEvent);
791 pThis->OverlappedRead.hEvent = NULL;
792 }
793 if (pThis->OverlappedWrite.hEvent != NULL)
794 {
795 CloseHandle(pThis->OverlappedWrite.hEvent);
796 pThis->OverlappedWrite.hEvent = NULL;
797 }
798 if (pThis->hEvtWake != NULL)
799 {
800 CloseHandle(pThis->hEvtWake);
801 pThis->hEvtWake = NULL;
802 }
803#else /* !RT_OS_WINDOWS */
804 Assert(pThis->LocalSocketServer == -1);
805
806 if (pThis->hSock != NIL_RTSOCKET)
807 {
808 int rc = RTPollSetRemove(pThis->hPollSet, DRVNAMEDPIPE_POLLSET_ID_SOCKET);
809 AssertRC(rc);
810
811 rc = RTSocketShutdown(pThis->hSock, true /* fRead */, true /* fWrite */);
812 AssertRC(rc);
813
814 rc = RTSocketClose(pThis->hSock);
815 AssertRC(rc); RT_NOREF(rc);
816
817 pThis->hSock = NIL_RTSOCKET;
818 }
819
820 if (pThis->hPipeWakeR != NIL_RTPIPE)
821 {
822 int rc = RTPipeClose(pThis->hPipeWakeR);
823 AssertRC(rc);
824
825 pThis->hPipeWakeR = NIL_RTPIPE;
826 }
827
828 if (pThis->hPipeWakeW != NIL_RTPIPE)
829 {
830 int rc = RTPipeClose(pThis->hPipeWakeW);
831 AssertRC(rc);
832
833 pThis->hPipeWakeW = NIL_RTPIPE;
834 }
835
836 if (pThis->hPollSet != NIL_RTPOLLSET)
837 {
838 int rc = RTPollSetDestroy(pThis->hPollSet);
839 AssertRC(rc);
840
841 pThis->hPollSet = NIL_RTPOLLSET;
842 }
843
844 if ( pThis->fIsServer
845 && pThis->pszLocation)
846 RTFileDelete(pThis->pszLocation);
847#endif /* !RT_OS_WINDOWS */
848
849 MMR3HeapFree(pThis->pszLocation);
850 pThis->pszLocation = NULL;
851
852 /*
853 * Wait for the thread.
854 */
855 if (pThis->ListenThread != NIL_RTTHREAD)
856 {
857 int rc = RTThreadWait(pThis->ListenThread, 30000, NULL);
858 if (RT_SUCCESS(rc))
859 pThis->ListenThread = NIL_RTTHREAD;
860 else
861 LogRel(("NamedPipe%d: listen thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
862 }
863
864 /*
865 * The last bits of cleanup.
866 */
867#ifdef RT_OS_WINDOWS
868 if (pThis->ListenSem != NIL_RTSEMEVENT)
869 {
870 RTSemEventMultiDestroy(pThis->ListenSem);
871 pThis->ListenSem = NIL_RTSEMEVENT;
872 }
873#endif
874}
875
876
877/**
878 * Construct a named pipe stream driver instance.
879 *
880 * @copydoc FNPDMDRVCONSTRUCT
881 */
882static DECLCALLBACK(int) drvNamedPipeConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
883{
884 RT_NOREF(fFlags);
885 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
886 PDRVNAMEDPIPE pThis = PDMINS_2_DATA(pDrvIns, PDRVNAMEDPIPE);
887
888 /*
889 * Init the static parts.
890 */
891 pThis->pDrvIns = pDrvIns;
892 pThis->pszLocation = NULL;
893 pThis->fIsServer = false;
894#ifdef RT_OS_WINDOWS
895 pThis->NamedPipe = INVALID_HANDLE_VALUE;
896 pThis->ListenSem = NIL_RTSEMEVENTMULTI;
897 pThis->OverlappedWrite.hEvent = NULL;
898 pThis->OverlappedRead.hEvent = NULL;
899 pThis->hEvtWake = NULL;
900#else /* !RT_OS_WINDOWS */
901 pThis->LocalSocketServer = -1;
902 pThis->hSock = NIL_RTSOCKET;
903
904 pThis->hPollSet = NIL_RTPOLLSET;
905 pThis->hPipeWakeR = NIL_RTPIPE;
906 pThis->hPipeWakeW = NIL_RTPIPE;
907 pThis->fSockInPollSet = false;
908#endif /* !RT_OS_WINDOWS */
909 pThis->ListenThread = NIL_RTTHREAD;
910 pThis->fShutdown = false;
911 /* IBase */
912 pDrvIns->IBase.pfnQueryInterface = drvNamedPipeQueryInterface;
913 /* IStream */
914 pThis->IStream.pfnPoll = drvNamedPipePoll;
915 pThis->IStream.pfnPollInterrupt = drvNamedPipePollInterrupt;
916 pThis->IStream.pfnRead = drvNamedPipeRead;
917 pThis->IStream.pfnWrite = drvNamedPipeWrite;
918
919 /*
920 * Validate and read the configuration.
921 */
922 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|IsServer", "");
923
924 int rc = CFGMR3QueryStringAlloc(pCfg, "Location", &pThis->pszLocation);
925 if (RT_FAILURE(rc))
926 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
927 N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
928 rc = CFGMR3QueryBool(pCfg, "IsServer", &pThis->fIsServer);
929 if (RT_FAILURE(rc))
930 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
931 N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc);
932
933 /*
934 * Create/Open the pipe.
935 */
936#ifdef RT_OS_WINDOWS
937 if (pThis->fIsServer)
938 {
939 pThis->NamedPipe = CreateNamedPipe(pThis->pszLocation,
940 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
941 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
942 1, /*nMaxInstances*/
943 32, /*nOutBufferSize*/
944 32, /*nOutBufferSize*/
945 10000, /*nDefaultTimeOut*/
946 NULL); /* lpSecurityAttributes*/
947 if (pThis->NamedPipe == INVALID_HANDLE_VALUE)
948 {
949 rc = RTErrConvertFromWin32(GetLastError());
950 LogRel(("NamedPipe%d: CreateNamedPipe failed rc=%Rrc\n", pThis->pDrvIns->iInstance));
951 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create named pipe %s"),
952 pDrvIns->iInstance, pThis->pszLocation);
953 }
954
955 rc = RTSemEventMultiCreate(&pThis->ListenSem);
956 AssertRCReturn(rc, rc);
957
958 rc = RTThreadCreate(&pThis->ListenThread, drvNamedPipeListenLoop, (void *)pThis, 0,
959 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SerPipe");
960 if (RT_FAILURE(rc))
961 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create listening thread"),
962 pDrvIns->iInstance);
963
964 }
965 else
966 {
967 /* Connect to the named pipe. */
968 pThis->NamedPipe = CreateFile(pThis->pszLocation, GENERIC_READ | GENERIC_WRITE, 0, NULL,
969 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
970 if (pThis->NamedPipe == INVALID_HANDLE_VALUE)
971 {
972 rc = RTErrConvertFromWin32(GetLastError());
973 LogRel(("NamedPipe%d: CreateFile failed rc=%Rrc\n", pThis->pDrvIns->iInstance));
974 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to connect to named pipe %s"),
975 pDrvIns->iInstance, pThis->pszLocation);
976 }
977 }
978
979 memset(&pThis->OverlappedWrite, 0, sizeof(pThis->OverlappedWrite));
980 pThis->OverlappedWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
981 AssertReturn(pThis->OverlappedWrite.hEvent != NULL, VERR_OUT_OF_RESOURCES);
982
983 memset(&pThis->OverlappedRead, 0, sizeof(pThis->OverlappedRead));
984 pThis->OverlappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
985 AssertReturn(pThis->OverlappedRead.hEvent != NULL, VERR_OUT_OF_RESOURCES);
986
987 pThis->hEvtWake = CreateEvent(NULL, FALSE, FALSE, NULL);
988 AssertReturn(pThis->hEvtWake != NULL, VERR_OUT_OF_RESOURCES);
989
990#else /* !RT_OS_WINDOWS */
991 rc = RTPipeCreate(&pThis->hPipeWakeR, &pThis->hPipeWakeW, 0 /* fFlags */);
992 if (RT_FAILURE(rc))
993 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
994 N_("DrvTCP#%d: Failed to create wake pipe"), pDrvIns->iInstance);
995
996 rc = RTPollSetCreate(&pThis->hPollSet);
997 if (RT_FAILURE(rc))
998 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
999 N_("DrvTCP#%d: Failed to create poll set"), pDrvIns->iInstance);
1000
1001 rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeWakeR,
1002 RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
1003 DRVNAMEDPIPE_POLLSET_ID_WAKEUP);
1004 if (RT_FAILURE(rc))
1005 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1006 N_("DrvTCP#%d failed to add wakeup pipe for %s to poll set"),
1007 pDrvIns->iInstance, pThis->pszLocation);
1008
1009 int s = socket(PF_UNIX, SOCK_STREAM, 0);
1010 if (s == -1)
1011 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
1012 N_("NamedPipe#%d failed to create local socket"), pDrvIns->iInstance);
1013
1014 struct sockaddr_un addr;
1015 memset(&addr, 0, sizeof(addr));
1016 addr.sun_family = AF_UNIX;
1017 strncpy(addr.sun_path, pThis->pszLocation, sizeof(addr.sun_path) - 1);
1018
1019 if (pThis->fIsServer)
1020 {
1021 /* Bind address to the local socket. */
1022 pThis->LocalSocketServer = s;
1023 RTFileDelete(pThis->pszLocation);
1024 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
1025 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
1026 N_("NamedPipe#%d failed to bind to local socket %s"),
1027 pDrvIns->iInstance, pThis->pszLocation);
1028 rc = RTThreadCreate(&pThis->ListenThread, drvNamedPipeListenLoop, (void *)pThis, 0,
1029 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SerPipe");
1030 if (RT_FAILURE(rc))
1031 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1032 N_("NamedPipe#%d failed to create listening thread"), pDrvIns->iInstance);
1033 }
1034 else
1035 {
1036 /* Connect to the local socket. */
1037 if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
1038 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
1039 N_("NamedPipe#%d failed to connect to local socket %s"),
1040 pDrvIns->iInstance, pThis->pszLocation);
1041
1042 rc = RTSocketFromNative(&pThis->hSock, s);
1043 if (RT_FAILURE(rc))
1044 {
1045 close(s);
1046 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1047 N_("NamedPipe#%d failed to wrap socket %Rrc"),
1048 pDrvIns->iInstance, pThis->pszLocation);
1049 }
1050 }
1051#endif /* !RT_OS_WINDOWS */
1052
1053 LogRel(("NamedPipe: location %s, %s\n", pThis->pszLocation, pThis->fIsServer ? "server" : "client"));
1054 return VINF_SUCCESS;
1055}
1056
1057
1058/**
1059 * Named pipe driver registration record.
1060 */
1061const PDMDRVREG g_DrvNamedPipe =
1062{
1063 /* u32Version */
1064 PDM_DRVREG_VERSION,
1065 /* szName */
1066 "NamedPipe",
1067 /* szRCMod */
1068 "",
1069 /* szR0Mod */
1070 "",
1071 /* pszDescription */
1072 "Named Pipe stream driver.",
1073 /* fFlags */
1074 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1075 /* fClass. */
1076 PDM_DRVREG_CLASS_STREAM,
1077 /* cMaxInstances */
1078 ~0U,
1079 /* cbInstance */
1080 sizeof(DRVNAMEDPIPE),
1081 /* pfnConstruct */
1082 drvNamedPipeConstruct,
1083 /* pfnDestruct */
1084 drvNamedPipeDestruct,
1085 /* pfnRelocate */
1086 NULL,
1087 /* pfnIOCtl */
1088 NULL,
1089 /* pfnPowerOn */
1090 NULL,
1091 /* pfnReset */
1092 NULL,
1093 /* pfnSuspend */
1094 NULL,
1095 /* pfnResume */
1096 NULL,
1097 /* pfnAttach */
1098 NULL,
1099 /* pfnDetach */
1100 NULL,
1101 /* pfnPowerOff */
1102 drvNamedPipePowerOff,
1103 /* pfnSoftReset */
1104 NULL,
1105 /* u32EndVersion */
1106 PDM_DRVREG_VERSION
1107};
1108
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