VirtualBox

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

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

Wait a bit when nobody has connected yet.

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