VirtualBox

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

Last change on this file since 1760 was 1760, checked in by vboxsync, 18 years ago

Updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.7 KB
Line 
1/** @file
2 *
3 * VBox stream devices:
4 * Named pipe stream
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_NAMEDPIPE
28#include <VBox/pdm.h>
29#include <VBox/cfgm.h>
30#include <VBox/err.h>
31
32#include <VBox/log.h>
33#include <VBox/mm.h>
34#include <iprt/assert.h>
35#include <iprt/file.h>
36#include <iprt/stream.h>
37#include <iprt/alloc.h>
38#include <iprt/string.h>
39
40#include "Builtins.h"
41
42#ifdef __WIN__
43#include <windows.h>
44#else /* !__WIN__ */
45#include <errno.h>
46#include <unistd.h>
47#include <sys/types.h>
48#include <sys/socket.h>
49#include <sys/un.h>
50#endif /* !__WIN__ */
51
52/*******************************************************************************
53* Defined Constants And Macros *
54*******************************************************************************/
55
56/** Converts a pointer to DRVNAMEDPIPE::IMedia to a PDRVNAMEDPIPE. */
57#define PDMISTREAM_2_DRVNAMEDPIPE(pInterface) ( (PDRVNAMEDPIPE)((uintptr_t)pInterface - RT_OFFSETOF(DRVNAMEDPIPE, IStream)) )
58
59/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
60#define PDMIBASE_2_DRVINS(pInterface) ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
61
62/*******************************************************************************
63* Structures and Typedefs *
64*******************************************************************************/
65/**
66 * Named pipe driver instance data.
67 */
68typedef struct DRVNAMEDPIPE
69{
70 /** The stream interface. */
71 PDMISTREAM IStream;
72 /** Pointer to the driver instance. */
73 PPDMDRVINS pDrvIns;
74 /** Pointer to the named pipe file name. (Freed by MM) */
75 char *pszLocation;
76 /** Flag whether VirtualBox represents the server or client side. */
77 bool fIsServer;
78#ifdef __WIN__
79 /* File handle of the named pipe. */
80 RTFILE NamedPipe;
81 /* Overlapped structure for writes. */
82 OVERLAPPED OverlappedWrite;
83 /* Overlapped structure for reads. */
84 OVERLAPPED OverlappedRead;
85#else /* !__WIN__ */
86 /** Socket handle of the local socket for server. */
87 RTSOCKET LocalSocketServer;
88 /** Socket handle of the local socket. */
89 RTSOCKET LocalSocket;
90#endif /* !__WIN__ */
91 /** Thread for listening for new connections. */
92 RTTHREAD ListenThread;
93 /** Flag to signal listening thread to shut down. */
94 bool fShutdown;
95} DRVNAMEDPIPE, *PDRVNAMEDPIPE;
96
97
98/*******************************************************************************
99* Internal Functions *
100*******************************************************************************/
101
102
103/** @copydoc PDMISTREAM::pfnRead */
104static DECLCALLBACK(int) drvNamedPipeRead(PPDMISTREAM pInterface, void *pvBuf, size_t *cbRead)
105{
106 int rc = VINF_SUCCESS;
107 PDRVNAMEDPIPE pData = PDMISTREAM_2_DRVNAMEDPIPE(pInterface);
108 LogFlow(("%s: pvBuf=%p cbRead=%#x (%s)\n", __FUNCTION__, pvBuf, cbRead, pData->pszLocation));
109
110 Assert(pvBuf);
111#ifdef __WIN__
112 if (pData->NamedPipe != NIL_RTFILE)
113 {
114 DWORD cbReallyRead;
115 pData->OverlappedRead.Offset = 0;
116 pData->OverlappedRead.OffsetHigh = 0;
117 if (!ReadFile((HANDLE)pData->NamedPipe, pvBuf, *cbRead, &cbReallyRead, &pData->OverlappedRead))
118 {
119 DWORD uError = GetLastError();
120
121 if ( uError == ERROR_PIPE_LISTENING
122 || uError == ERROR_PIPE_NOT_CONNECTED)
123 {
124 /* No connection yet/anymore */
125 cbReallyRead = 0;
126
127 /* wait a bit or else we'll be called right back. */
128 RTThreadSleep(100);
129 }
130 else
131 {
132 if (uError == ERROR_IO_PENDING)
133 {
134 uError = 0;
135
136 /* Wait for incoming bytes. */
137 if (GetOverlappedResult((HANDLE)pData->NamedPipe, &pData->OverlappedRead, &cbReallyRead, TRUE) == FALSE)
138 uError = GetLastError();
139 }
140
141 rc = RTErrConvertFromWin32(uError);
142 Log(("drvNamedPipeRead: ReadFile returned %d (%Vrc)\n", uError, rc));
143 }
144 }
145
146 if (VBOX_FAILURE(rc))
147 {
148 Log(("drvNamedPipeRead: RTFileRead returned %Vrc\n", rc));
149 if ( !pData->fShutdown
150 && ( rc == VERR_EOF
151 || rc == VERR_BROKEN_PIPE
152 )
153 )
154
155 {
156 RTFILE tmp = pData->NamedPipe;
157 FlushFileBuffers((HANDLE)tmp);
158 DisconnectNamedPipe((HANDLE)tmp);
159 if (!pData->fIsServer)
160 {
161 pData->NamedPipe = NIL_RTFILE;
162 RTFileClose(tmp);
163 }
164 /* pretend success */
165 rc = VINF_SUCCESS;
166 }
167 cbReallyRead = 0;
168 }
169 *cbRead = (size_t)cbReallyRead;
170 }
171#else /* !__WIN__ */
172 if (pData->LocalSocket != NIL_RTSOCKET)
173 {
174 ssize_t cbReallyRead;
175 cbReallyRead = recv(pData->LocalSocket, pvBuf, *cbRead, 0);
176 if (cbReallyRead == 0)
177 {
178 RTSOCKET tmp = pData->LocalSocket;
179 pData->LocalSocket = NIL_RTSOCKET;
180 close(tmp);
181 }
182 else if (cbReallyRead == -1)
183 {
184 cbReallyRead = 0;
185 rc = RTErrConvertFromErrno(errno);
186 }
187 *cbRead = cbReallyRead;
188 }
189#endif /* !__WIN__ */
190 else
191 {
192 RTThreadSleep(100);
193 *cbRead = 0;
194 }
195
196 LogFlow(("%s: cbRead=%d returns %Vrc\n", __FUNCTION__, *cbRead, rc));
197 return rc;
198}
199
200
201/** @copydoc PDMISTREAM::pfnWrite */
202static DECLCALLBACK(int) drvNamedPipeWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *cbWrite)
203{
204 int rc = VINF_SUCCESS;
205 PDRVNAMEDPIPE pData = PDMISTREAM_2_DRVNAMEDPIPE(pInterface);
206 LogFlow(("%s: pvBuf=%p cbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, cbWrite, pData->pszLocation));
207
208 Assert(pvBuf);
209 /** @todo implement non-blocking I/O */
210#ifdef __WIN__
211 if (pData->NamedPipe != NIL_RTFILE)
212 {
213 unsigned cbWritten;
214 pData->OverlappedWrite.Offset = 0;
215 pData->OverlappedWrite.OffsetHigh = 0;
216 if (!WriteFile((HANDLE)pData->NamedPipe, pvBuf, *cbWrite, NULL, &pData->OverlappedWrite))
217 {
218 DWORD uError = GetLastError();
219
220 if ( uError == ERROR_PIPE_LISTENING
221 || uError == ERROR_PIPE_NOT_CONNECTED)
222 {
223 /* No connection yet/anymore; just discard the write. */
224 cbWritten = *cbWrite;
225 }
226 else
227 if (uError != ERROR_IO_PENDING)
228 {
229 rc = RTErrConvertFromWin32(uError);
230 Log(("drvNamedPipeWrite: WriteFile returned %d (%Vrc)\n", uError, rc));
231 }
232 else
233 {
234 /* Wait for the write to complete. */
235 if (GetOverlappedResult((HANDLE)pData->NamedPipe, &pData->OverlappedWrite, (DWORD *)&cbWritten, TRUE) == FALSE)
236 uError = GetLastError();
237 }
238 }
239 else
240 cbWritten = *cbWrite;
241
242 if (VBOX_FAILURE(rc))
243 {
244 if ( rc == VERR_EOF
245 || rc == VERR_BROKEN_PIPE)
246 {
247 RTFILE tmp = pData->NamedPipe;
248 FlushFileBuffers((HANDLE)tmp);
249 DisconnectNamedPipe((HANDLE)tmp);
250 if (!pData->fIsServer)
251 {
252 pData->NamedPipe = NIL_RTFILE;
253 RTFileClose(tmp);
254 }
255 /* pretend success */
256 rc = VINF_SUCCESS;
257 }
258 cbWritten = 0;
259 }
260 *cbWrite = cbWritten;
261 }
262#else /* !__WIN__ */
263 if (pData->LocalSocket != NIL_RTSOCKET)
264 {
265 ssize_t cbWritten;
266 cbWritten = send(pData->LocalSocket, pvBuf, *cbWrite, 0);
267 if (cbWritten == 0)
268 {
269 RTSOCKET tmp = pData->LocalSocket;
270 pData->LocalSocket = NIL_RTSOCKET;
271 close(tmp);
272 }
273 else if (cbWritten == -1)
274 {
275 cbWritten = 0;
276 rc = RTErrConvertFromErrno(errno);
277 }
278 *cbWrite = cbWritten;
279 }
280#endif /* !__WIN__ */
281
282 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
283 return rc;
284}
285
286
287/**
288 * Queries an interface to the driver.
289 *
290 * @returns Pointer to interface.
291 * @returns NULL if the interface was not supported by the driver.
292 * @param pInterface Pointer to this interface structure.
293 * @param enmInterface The requested interface identification.
294 * @thread Any thread.
295 */
296static DECLCALLBACK(void *) drvNamedPipeQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
297{
298 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
299 PDRVNAMEDPIPE pDrv = PDMINS2DATA(pDrvIns, PDRVNAMEDPIPE);
300 switch (enmInterface)
301 {
302 case PDMINTERFACE_BASE:
303 return &pDrvIns->IBase;
304 case PDMINTERFACE_STREAM:
305 return &pDrv->IStream;
306 default:
307 return NULL;
308 }
309}
310
311
312/* -=-=-=-=- listen thread -=-=-=-=- */
313
314/**
315 * Receive thread loop.
316 *
317 * @returns 0 on success.
318 * @param ThreadSelf Thread handle to this thread.
319 * @param pvUser User argument.
320 */
321static DECLCALLBACK(int) drvNamedPipeListenLoop(RTTHREAD ThreadSelf, void *pvUser)
322{
323 PDRVNAMEDPIPE pData = (PDRVNAMEDPIPE)pvUser;
324 int rc = VINF_SUCCESS;
325#ifdef __WIN__
326 RTFILE NamedPipe = pData->NamedPipe;
327 HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, 0);
328#endif
329
330 while (RT_LIKELY(!pData->fShutdown))
331 {
332#ifdef __WIN__
333 OVERLAPPED overlapped;
334
335 memset(&overlapped, 0, sizeof(overlapped));
336 overlapped.hEvent = hEvent;
337
338 BOOL fConnected = ConnectNamedPipe((HANDLE)NamedPipe, &overlapped);
339 if (!fConnected)
340 {
341 DWORD hrc = GetLastError();
342
343 if (hrc == ERROR_IO_PENDING)
344 {
345 DWORD dummy;
346
347 hrc = 0;
348 if (GetOverlappedResult((HANDLE)pData->NamedPipe, &overlapped, &dummy, TRUE) == FALSE)
349 hrc = GetLastError();
350
351 }
352
353 if (hrc == ERROR_PIPE_CONNECTED)
354 {
355 RTThreadSleep(250);
356 }
357 else
358 if (hrc != ERROR_SUCCESS)
359 {
360 rc = RTErrConvertFromWin32(hrc);
361 LogRel(("NamedPipe%d: ConnectNamedPipe failed, rc=%Vrc\n", pData->pDrvIns->iInstance, rc));
362 break;
363 }
364 }
365#else /* !__WIN__ */
366 if (listen(pData->LocalSocketServer, 0) == -1)
367 {
368 rc = RTErrConvertFromErrno(errno);
369 LogRel(("NamedPipe%d: listen failed, rc=%Vrc\n", pData->pDrvIns->iInstance, rc));
370 break;
371 }
372 int s = accept(pData->LocalSocketServer, NULL, NULL);
373 if (s == -1)
374 {
375 rc = RTErrConvertFromErrno(errno);
376 LogRel(("NamedPipe%d: accept failed, rc=%Vrc\n", pData->pDrvIns->iInstance, rc));
377 break;
378 }
379 else
380 {
381 if (pData->LocalSocket != NIL_RTSOCKET)
382 {
383 LogRel(("NamedPipe%d: only single connection supported\n", pData->pDrvIns->iInstance));
384 close(s);
385 }
386 else
387 pData->LocalSocket = s;
388 }
389#endif /* !__WIN__ */
390 }
391
392#ifdef __WIN__
393 CloseHandle(hEvent);
394#endif
395 return VINF_SUCCESS;
396}
397
398
399/**
400 * Construct a named pipe stream driver instance.
401 *
402 * @returns VBox status.
403 * @param pDrvIns The driver instance data.
404 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
405 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
406 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
407 * iInstance it's expected to be used a bit in this function.
408 */
409static DECLCALLBACK(int) drvNamedPipeConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
410{
411 int rc;
412 PDRVNAMEDPIPE pData = PDMINS2DATA(pDrvIns, PDRVNAMEDPIPE);
413
414 /*
415 * Init the static parts.
416 */
417 pData->pDrvIns = pDrvIns;
418 pData->pszLocation = NULL;
419 pData->fIsServer = false;
420#ifdef __WIN__
421 pData->NamedPipe = NIL_RTFILE;
422#else /* !__WIN__ */
423 pData->LocalSocketServer = NIL_RTSOCKET;
424 pData->LocalSocket = NIL_RTSOCKET;
425#endif /* !__WIN__ */
426 pData->ListenThread = NIL_RTTHREAD;
427 pData->fShutdown = false;
428 /* IBase */
429 pDrvIns->IBase.pfnQueryInterface = drvNamedPipeQueryInterface;
430 /* IStream */
431 pData->IStream.pfnRead = drvNamedPipeRead;
432 pData->IStream.pfnWrite = drvNamedPipeWrite;
433
434 /*
435 * Read the configuration.
436 */
437 if (!CFGMR3AreValuesValid(pCfgHandle, "Location\0IsServer\0"))
438 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
439
440 char *pszLocation;
441 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Location", &pszLocation);
442 if (VBOX_FAILURE(rc))
443 {
444 AssertMsgFailed(("Configuration error: query \"Location\" resulted in %Vrc.\n", rc));
445 return rc;
446 }
447 pData->pszLocation = pszLocation;
448
449 bool fIsServer;
450 rc = CFGMR3QueryBool(pCfgHandle, "IsServer", &fIsServer);
451 if (VBOX_FAILURE(rc))
452 {
453 AssertMsgFailed(("Configuration error: query \"IsServer\" resulted in %Vrc.\n", rc));
454 goto out;
455 }
456 pData->fIsServer = fIsServer;
457
458#ifdef __WIN__
459 if (fIsServer)
460 {
461 HANDLE hPipe = CreateNamedPipe(pData->pszLocation, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 32, 32, 10000, NULL);
462 if (hPipe == INVALID_HANDLE_VALUE)
463 {
464 rc = RTErrConvertFromWin32(GetLastError());
465 LogRel(("NamedPipe%d: CreateNamedPipe failed rc=%Vrc\n", pData->pDrvIns->iInstance));
466 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create named pipe %s"), pDrvIns->iInstance, pszLocation);
467 }
468 pData->NamedPipe = (HFILE)hPipe;
469
470 rc = RTThreadCreate(&pData->ListenThread, drvNamedPipeListenLoop, (void *)pData, 0, RTTHREADTYPE_IO, 0, "NamedPipe");
471 if VBOX_FAILURE(rc)
472 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create listening thread\n"), pDrvIns->iInstance);
473 }
474 else
475 {
476 /* Connect to the named pipe. */
477 rc = RTFileOpen(&pData->NamedPipe, pszLocation, RTFILE_O_READWRITE);
478 if (VBOX_FAILURE(rc))
479 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to connect to named pipe %s"), pDrvIns->iInstance, pszLocation);
480 }
481
482 memset(&pData->OverlappedWrite, 0, sizeof(pData->OverlappedWrite));
483 memset(&pData->OverlappedRead, 0, sizeof(pData->OverlappedRead));
484 pData->OverlappedWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
485 pData->OverlappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
486
487#else /* !__WIN__ */
488 int s;
489 struct sockaddr_un addr;
490
491 if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
492 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to create local socket"), pDrvIns->iInstance);
493
494 memset(&addr, 0, sizeof(addr));
495 addr.sun_family = AF_UNIX;
496 strncpy(addr.sun_path, pszLocation, sizeof(addr.sun_path)-1);
497
498 if (fIsServer)
499 {
500 /* Bind address to the local socket. */
501 RTFileDelete(pszLocation);
502 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
503 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to bind to local socket %s"), pDrvIns->iInstance, pszLocation);
504 rc = RTThreadCreate(&pData->ListenThread, drvNamedPipeListenLoop, (void *)pData, 0, RTTHREADTYPE_IO, 0, "NamedPipe");
505 if VBOX_FAILURE(rc)
506 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create listening thread\n"), pDrvIns->iInstance);
507 pData->LocalSocketServer = s;
508 }
509 else
510 {
511 /* Connect to the local socket. */
512 if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
513 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to connect to local socket %s"), pDrvIns->iInstance, pszLocation);
514 pData->LocalSocket = s;
515 }
516#endif /* !__WIN__ */
517
518out:
519 if (VBOX_FAILURE(rc))
520 {
521 if (pszLocation)
522 MMR3HeapFree(pszLocation);
523 }
524 else
525 {
526 LogFlow(("drvNamedPipeConstruct: location %s isServer %d\n", pszLocation, fIsServer));
527 LogRel(("NamedPipe: location %s, %s\n", pszLocation, fIsServer ? "server" : "client"));
528 }
529 return rc;
530}
531
532
533/**
534 * Destruct a named pipe stream driver instance.
535 *
536 * Most VM resources are freed by the VM. This callback is provided so that
537 * any non-VM resources can be freed correctly.
538 *
539 * @param pDrvIns The driver instance data.
540 */
541static DECLCALLBACK(void) drvNamedPipeDestruct(PPDMDRVINS pDrvIns)
542{
543 PDRVNAMEDPIPE pData = PDMINS2DATA(pDrvIns, PDRVNAMEDPIPE);
544 LogFlow(("%s: %s\n", __FUNCTION__, pData->pszLocation));
545
546 pData->fShutdown = true;
547
548#ifdef __WIN__
549 if (pData->NamedPipe != NIL_RTFILE)
550 {
551 FlushFileBuffers((HANDLE)pData->NamedPipe);
552 if (!pData->fIsServer)
553 DisconnectNamedPipe((HANDLE)pData->NamedPipe);
554
555 RTFileClose(pData->NamedPipe);
556 pData->NamedPipe = NIL_RTFILE;
557 CloseHandle(pData->OverlappedRead.hEvent);
558 CloseHandle(pData->OverlappedWrite.hEvent);
559 }
560#else /* !__WIN__ */
561 if (pData->fIsServer)
562 {
563 if (pData->LocalSocketServer != NIL_RTSOCKET)
564 close(pData->LocalSocketServer);
565 if (pData->pszLocation)
566 RTFileDelete(pData->pszLocation);
567 }
568 else
569 {
570 if (pData->LocalSocket != NIL_RTSOCKET)
571 close(pData->LocalSocket);
572 }
573#endif /* !__WIN__ */
574 if (pData->pszLocation)
575 MMR3HeapFree(pData->pszLocation);
576}
577
578
579/**
580 * Named pipe driver registration record.
581 */
582const PDMDRVREG g_DrvNamedPipe =
583{
584 /* u32Version */
585 PDM_DRVREG_VERSION,
586 /* szDriverName */
587 "NamedPipe",
588 /* pszDescription */
589 "Named Pipe stream driver.",
590 /* fFlags */
591 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
592 /* fClass. */
593 PDM_DRVREG_CLASS_STREAM,
594 /* cMaxInstances */
595 ~0,
596 /* cbInstance */
597 sizeof(DRVNAMEDPIPE),
598 /* pfnConstruct */
599 drvNamedPipeConstruct,
600 /* pfnDestruct */
601 drvNamedPipeDestruct,
602 /* pfnIOCtl */
603 NULL,
604 /* pfnPowerOn */
605 NULL,
606 /* pfnReset */
607 NULL,
608 /* pfnSuspend */
609 NULL,
610 /* pfnResume */
611 NULL,
612 /* pfnDetach */
613 NULL,
614 /* pfnPowerOff */
615 NULL
616};
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