VirtualBox

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

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

Property cleanup (and reexport serial device).

  • 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 /** @todo implement non-blocking I/O */
112#ifdef __WIN__
113 if (pData->NamedPipe != NIL_RTFILE)
114 {
115 unsigned cbReallyRead;
116 pData->OverlappedRead.Offset = 0;
117 pData->OverlappedRead.OffsetHigh = 0;
118 if (!ReadFile((HANDLE)pData->NamedPipe, pvBuf, *cbRead, NULL, &pData->OverlappedRead))
119 {
120 DWORD uError = GetLastError();
121
122 if ( uError == ERROR_PIPE_LISTENING
123 || uError == ERROR_PIPE_NOT_CONNECTED)
124 {
125 /* No connection yet/anymore */
126 cbReallyRead = 0;
127
128 /* wait a bit or else we'll be called right back. */
129 RTThreadSleep(100);
130 }
131 else
132 {
133 if (uError == ERROR_IO_PENDING)
134 {
135 uError = 0;
136
137 /* Wait for incoming bytes. */
138 if (GetOverlappedResult((HANDLE)pData->NamedPipe, &pData->OverlappedRead, (DWORD *)&cbReallyRead, TRUE) == FALSE)
139 uError = GetLastError();
140 }
141
142 rc = RTErrConvertFromWin32(uError);
143 Log(("drvNamedPipeRead: ReadFile returned %d (%Vrc)\n", uError, rc));
144 }
145 }
146 else
147 cbReallyRead = *cbRead;
148
149 if (VBOX_FAILURE(rc))
150 {
151 Log(("drvNamedPipeRead: RTFileRead returned %Vrc\n", rc));
152 if ( rc == VERR_EOF
153 || rc == VERR_BROKEN_PIPE)
154 {
155 RTFILE tmp = pData->NamedPipe;
156 FlushFileBuffers((HANDLE)tmp);
157 DisconnectNamedPipe((HANDLE)tmp);
158 if (!pData->fIsServer)
159 {
160 pData->NamedPipe = NIL_RTFILE;
161 RTFileClose(tmp);
162 }
163 /* pretend success */
164 rc = VINF_SUCCESS;
165 }
166 cbReallyRead = 0;
167 }
168 *cbRead = cbReallyRead;
169 }
170#else /* !__WIN__ */
171 if (pData->LocalSocket != NIL_RTSOCKET)
172 {
173 ssize_t cbReallyRead;
174 cbReallyRead = recv(pData->LocalSocket, pvBuf, *cbRead, 0);
175 if (cbReallyRead == 0)
176 {
177 RTSOCKET tmp = pData->LocalSocket;
178 pData->LocalSocket = NIL_RTSOCKET;
179 close(tmp);
180 }
181 else if (cbReallyRead == -1)
182 {
183 cbReallyRead = 0;
184 rc = RTErrConvertFromErrno(errno);
185 }
186 *cbRead = cbReallyRead;
187 }
188#endif /* !__WIN__ */
189 else
190 {
191 RTThreadSleep(100);
192 *cbRead = 0;
193 }
194
195 LogFlow(("%s: cbRead=%d returns %Vrc\n", __FUNCTION__, *cbRead, rc));
196 return rc;
197}
198
199
200/** @copydoc PDMISTREAM::pfnWrite */
201static DECLCALLBACK(int) drvNamedPipeWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *cbWrite)
202{
203 int rc = VINF_SUCCESS;
204 PDRVNAMEDPIPE pData = PDMISTREAM_2_DRVNAMEDPIPE(pInterface);
205 LogFlow(("%s: pvBuf=%p cbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, cbWrite, pData->pszLocation));
206
207 Assert(pvBuf);
208 /** @todo implement non-blocking I/O */
209#ifdef __WIN__
210 if (pData->NamedPipe != NIL_RTFILE)
211 {
212 unsigned cbWritten;
213 pData->OverlappedWrite.Offset = 0;
214 pData->OverlappedWrite.OffsetHigh = 0;
215 if (!WriteFile((HANDLE)pData->NamedPipe, pvBuf, *cbWrite, NULL, &pData->OverlappedWrite))
216 {
217 DWORD uError = GetLastError();
218
219 if ( uError == ERROR_PIPE_LISTENING
220 || uError == ERROR_PIPE_NOT_CONNECTED)
221 {
222 /* No connection yet/anymore; just discard the write. */
223 cbWritten = *cbWrite;
224 }
225 else
226 if (uError != ERROR_IO_PENDING)
227 {
228 rc = RTErrConvertFromWin32(uError);
229 Log(("drvNamedPipeWrite: WriteFile returned %d (%Vrc)\n", uError, rc));
230 }
231 else
232 {
233 /* Wait for the write to complete. */
234 if (GetOverlappedResult((HANDLE)pData->NamedPipe, &pData->OverlappedWrite, (DWORD *)&cbWritten, TRUE) == FALSE)
235 uError = GetLastError();
236 }
237 }
238 else
239 cbWritten = *cbWrite;
240
241 if (VBOX_FAILURE(rc))
242 {
243 if ( rc == VERR_EOF
244 || rc == VERR_BROKEN_PIPE)
245 {
246 RTFILE tmp = pData->NamedPipe;
247 FlushFileBuffers((HANDLE)tmp);
248 DisconnectNamedPipe((HANDLE)tmp);
249 if (!pData->fIsServer)
250 {
251 pData->NamedPipe = NIL_RTFILE;
252 RTFileClose(tmp);
253 }
254 /* pretend success */
255 rc = VINF_SUCCESS;
256 }
257 cbWritten = 0;
258 }
259 *cbWrite = cbWritten;
260 }
261#else /* !__WIN__ */
262 if (pData->LocalSocket != NIL_RTSOCKET)
263 {
264 ssize_t cbWritten;
265 cbWritten = send(pData->LocalSocket, pvBuf, *cbWrite, 0);
266 if (cbWritten == 0)
267 {
268 RTSOCKET tmp = pData->LocalSocket;
269 pData->LocalSocket = NIL_RTSOCKET;
270 close(tmp);
271 }
272 else if (cbWritten == -1)
273 {
274 cbWritten = 0;
275 rc = RTErrConvertFromErrno(errno);
276 }
277 *cbWrite = cbWritten;
278 }
279#endif /* !__WIN__ */
280
281 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
282 return rc;
283}
284
285
286/**
287 * Queries an interface to the driver.
288 *
289 * @returns Pointer to interface.
290 * @returns NULL if the interface was not supported by the driver.
291 * @param pInterface Pointer to this interface structure.
292 * @param enmInterface The requested interface identification.
293 * @thread Any thread.
294 */
295static DECLCALLBACK(void *) drvNamedPipeQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
296{
297 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
298 PDRVNAMEDPIPE pDrv = PDMINS2DATA(pDrvIns, PDRVNAMEDPIPE);
299 switch (enmInterface)
300 {
301 case PDMINTERFACE_BASE:
302 return &pDrvIns->IBase;
303 case PDMINTERFACE_STREAM:
304 return &pDrv->IStream;
305 default:
306 return NULL;
307 }
308}
309
310
311/* -=-=-=-=- listen thread -=-=-=-=- */
312
313/**
314 * Receive thread loop.
315 *
316 * @returns 0 on success.
317 * @param ThreadSelf Thread handle to this thread.
318 * @param pvUser User argument.
319 */
320static DECLCALLBACK(int) drvNamedPipeListenLoop(RTTHREAD ThreadSelf, void *pvUser)
321{
322 PDRVNAMEDPIPE pData = (PDRVNAMEDPIPE)pvUser;
323 int rc = VINF_SUCCESS;
324#ifdef __WIN__
325 RTFILE NamedPipe = pData->NamedPipe;
326 HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, 0);
327#endif
328
329 while (RT_LIKELY(!pData->fShutdown))
330 {
331#ifdef __WIN__
332 OVERLAPPED overlapped;
333
334 memset(&overlapped, 0, sizeof(overlapped));
335 overlapped.hEvent = hEvent;
336
337 BOOL fConnected = ConnectNamedPipe((HANDLE)NamedPipe, &overlapped);
338 if (!fConnected)
339 {
340 DWORD hrc = GetLastError();
341
342 if (hrc == ERROR_IO_PENDING)
343 {
344 DWORD dummy;
345
346 hrc = 0;
347 if (GetOverlappedResult((HANDLE)pData->NamedPipe, &overlapped, &dummy, TRUE) == FALSE)
348 hrc = GetLastError();
349
350 }
351
352 if (hrc == ERROR_PIPE_CONNECTED)
353 {
354 RTThreadSleep(250);
355 }
356 else
357 if (hrc != ERROR_SUCCESS)
358 {
359 rc = RTErrConvertFromWin32(hrc);
360 LogRel(("NamedPipe%d: ConnectNamedPipe failed, rc=%Vrc\n", pData->pDrvIns->iInstance, rc));
361 break;
362 }
363 }
364#else /* !__WIN__ */
365 if (listen(pData->LocalSocketServer, 0) == -1)
366 {
367 rc = RTErrConvertFromErrno(errno);
368 LogRel(("NamedPipe%d: listen failed, rc=%Vrc\n", pData->pDrvIns->iInstance, rc));
369 break;
370 }
371 int s = accept(pData->LocalSocketServer, NULL, NULL);
372 if (s == -1)
373 {
374 rc = RTErrConvertFromErrno(errno);
375 LogRel(("NamedPipe%d: accept failed, rc=%Vrc\n", pData->pDrvIns->iInstance, rc));
376 break;
377 }
378 else
379 {
380 if (pData->LocalSocket != NIL_RTSOCKET)
381 {
382 LogRel(("NamedPipe%d: only single connection supported\n", pData->pDrvIns->iInstance));
383 close(s);
384 }
385 else
386 pData->LocalSocket = s;
387 }
388#endif /* !__WIN__ */
389 }
390
391#ifdef __WIN__
392 CloseHandle(hEvent);
393#endif
394 return VINF_SUCCESS;
395}
396
397
398/**
399 * Construct a named pipe stream driver instance.
400 *
401 * @returns VBox status.
402 * @param pDrvIns The driver instance data.
403 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
404 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
405 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
406 * iInstance it's expected to be used a bit in this function.
407 */
408static DECLCALLBACK(int) drvNamedPipeConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
409{
410 int rc;
411 PDRVNAMEDPIPE pData = PDMINS2DATA(pDrvIns, PDRVNAMEDPIPE);
412
413 /*
414 * Init the static parts.
415 */
416 pData->pDrvIns = pDrvIns;
417 pData->pszLocation = NULL;
418 pData->fIsServer = false;
419#ifdef __WIN__
420 pData->NamedPipe = NIL_RTFILE;
421#else /* !__WIN__ */
422 pData->LocalSocketServer = NIL_RTSOCKET;
423 pData->LocalSocket = NIL_RTSOCKET;
424#endif /* !__WIN__ */
425 pData->ListenThread = NIL_RTTHREAD;
426 pData->fShutdown = false;
427 /* IBase */
428 pDrvIns->IBase.pfnQueryInterface = drvNamedPipeQueryInterface;
429 /* IStream */
430 pData->IStream.pfnRead = drvNamedPipeRead;
431 pData->IStream.pfnWrite = drvNamedPipeWrite;
432
433 /*
434 * Read the configuration.
435 */
436 if (!CFGMR3AreValuesValid(pCfgHandle, "Location\0IsServer\0"))
437 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
438
439 char *pszLocation;
440 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Location", &pszLocation);
441 if (VBOX_FAILURE(rc))
442 {
443 AssertMsgFailed(("Configuration error: query \"Location\" resulted in %Vrc.\n", rc));
444 return rc;
445 }
446 pData->pszLocation = pszLocation;
447
448 bool fIsServer;
449 rc = CFGMR3QueryBool(pCfgHandle, "IsServer", &fIsServer);
450 if (VBOX_FAILURE(rc))
451 {
452 AssertMsgFailed(("Configuration error: query \"IsServer\" resulted in %Vrc.\n", rc));
453 goto out;
454 }
455 pData->fIsServer = fIsServer;
456
457#ifdef __WIN__
458 if (fIsServer)
459 {
460 HANDLE hPipe = CreateNamedPipe(pData->pszLocation, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 32, 32, 10000, NULL);
461 if (hPipe == INVALID_HANDLE_VALUE)
462 {
463 rc = RTErrConvertFromWin32(GetLastError());
464 LogRel(("NamedPipe%d: CreateNamedPipe failed rc=%Vrc\n", pData->pDrvIns->iInstance));
465 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create named pipe %s"), pDrvIns->iInstance, pszLocation);
466 }
467 pData->NamedPipe = (HFILE)hPipe;
468
469 rc = RTThreadCreate(&pData->ListenThread, drvNamedPipeListenLoop, (void *)pData, 0, RTTHREADTYPE_IO, 0, "NamedPipe");
470 if VBOX_FAILURE(rc)
471 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create listening thread\n"), pDrvIns->iInstance);
472 }
473 else
474 {
475 /* Connect to the named pipe. */
476 rc = RTFileOpen(&pData->NamedPipe, pszLocation, RTFILE_O_READWRITE);
477 if (VBOX_FAILURE(rc))
478 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to connect to named pipe %s"), pDrvIns->iInstance, pszLocation);
479 }
480
481 memset(&pData->OverlappedWrite, 0, sizeof(pData->OverlappedWrite));
482 memset(&pData->OverlappedRead, 0, sizeof(pData->OverlappedRead));
483 pData->OverlappedWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
484 pData->OverlappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
485
486#else /* !__WIN__ */
487 int s;
488 struct sockaddr_un addr;
489
490 if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
491 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to create local socket"), pDrvIns->iInstance);
492
493 memset(&addr, 0, sizeof(addr));
494 addr.sun_family = AF_UNIX;
495 strncpy(addr.sun_path, pszLocation, sizeof(addr.sun_path)-1);
496
497 if (fIsServer)
498 {
499 /* Bind address to the local socket. */
500 RTFileDelete(pszLocation);
501 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
502 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to bind to local socket %s"), pDrvIns->iInstance, pszLocation);
503 rc = RTThreadCreate(&pData->ListenThread, drvNamedPipeListenLoop, (void *)pData, 0, RTTHREADTYPE_IO, 0, "NamedPipe");
504 if VBOX_FAILURE(rc)
505 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create listening thread\n"), pDrvIns->iInstance);
506 pData->LocalSocketServer = s;
507 }
508 else
509 {
510 /* Connect to the local socket. */
511 if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
512 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to connect to local socket %s"), pDrvIns->iInstance, pszLocation);
513 pData->LocalSocket = s;
514 }
515#endif /* !__WIN__ */
516
517out:
518 if (VBOX_FAILURE(rc))
519 {
520 if (pszLocation)
521 MMR3HeapFree(pszLocation);
522 }
523 else
524 {
525 LogFlow(("drvNamedPipeConstruct: location %s isServer %d\n", pszLocation, fIsServer));
526 LogRel(("NamedPipe: location %s, %s\n", pszLocation, fIsServer ? "server" : "client"));
527 }
528 return rc;
529}
530
531
532/**
533 * Destruct a named pipe stream driver instance.
534 *
535 * Most VM resources are freed by the VM. This callback is provided so that
536 * any non-VM resources can be freed correctly.
537 *
538 * @param pDrvIns The driver instance data.
539 */
540static DECLCALLBACK(void) drvNamedPipeDestruct(PPDMDRVINS pDrvIns)
541{
542 PDRVNAMEDPIPE pData = PDMINS2DATA(pDrvIns, PDRVNAMEDPIPE);
543 LogFlow(("%s: %s\n", __FUNCTION__, pData->pszLocation));
544
545#ifdef __WIN__
546 if (pData->NamedPipe != NIL_RTFILE)
547 {
548 FlushFileBuffers((HANDLE)pData->NamedPipe);
549 if (!pData->fIsServer)
550 DisconnectNamedPipe((HANDLE)pData->NamedPipe);
551
552 RTFileClose(pData->NamedPipe);
553 pData->NamedPipe = NIL_RTFILE;
554 CloseHandle(pData->OverlappedRead.hEvent);
555 CloseHandle(pData->OverlappedWrite.hEvent);
556 }
557#else /* !__WIN__ */
558 if (pData->fIsServer)
559 {
560 if (pData->LocalSocketServer != NIL_RTSOCKET)
561 close(pData->LocalSocketServer);
562 if (pData->pszLocation)
563 RTFileDelete(pData->pszLocation);
564 }
565 else
566 {
567 if (pData->LocalSocket != NIL_RTSOCKET)
568 close(pData->LocalSocket);
569 }
570#endif /* !__WIN__ */
571 if (pData->pszLocation)
572 MMR3HeapFree(pData->pszLocation);
573}
574
575
576/**
577 * Named pipe driver registration record.
578 */
579const PDMDRVREG g_DrvNamedPipe =
580{
581 /* u32Version */
582 PDM_DRVREG_VERSION,
583 /* szDriverName */
584 "NamedPipe",
585 /* pszDescription */
586 "Named Pipe stream driver.",
587 /* fFlags */
588 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
589 /* fClass. */
590 PDM_DRVREG_CLASS_STREAM,
591 /* cMaxInstances */
592 ~0,
593 /* cbInstance */
594 sizeof(DRVNAMEDPIPE),
595 /* pfnConstruct */
596 drvNamedPipeConstruct,
597 /* pfnDestruct */
598 drvNamedPipeDestruct,
599 /* pfnIOCtl */
600 NULL,
601 /* pfnPowerOn */
602 NULL,
603 /* pfnReset */
604 NULL,
605 /* pfnSuspend */
606 NULL,
607 /* pfnResume */
608 NULL,
609 /* pfnDetach */
610 NULL,
611 /* pfnPowerOff */
612 NULL
613};
Note: See TracBrowser for help on using the repository browser.

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