VirtualBox

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

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

export to OSE

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