VirtualBox

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

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

fix typo

File size: 15.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#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, rc));
267 break;
268 }
269 }
270 pData->NamedPipe = (RTFILE)hPipe;
271#else /* !__WIN__ */
272 if (listen(pData->LocalSocketServer, 0) == -1)
273 {
274 rc = RTErrConvertFromErrno(errno);
275 LogRel(("NamedPipe%d: listen failed, rc=%Vrc\n", pData->pDrvIns->iInstance, rc));
276 break;
277 }
278 int s = accept(pData->LocalSocketServer, NULL, NULL);
279 if (s == -1)
280 {
281 rc = RTErrConvertFromErrno(errno);
282 LogRel(("NamedPipe%d: accept failed, rc=%Vrc\n", pData->pDrvIns->iInstance, rc));
283 break;
284 }
285 else
286 {
287 if (pData->LocalSocket != NIL_RTSOCKET)
288 {
289 LogRel(("NamedPipe%d: only single connection supported\n", pData->pDrvIns->iInstance));
290 close(s);
291 }
292 else
293 pData->LocalSocket = s;
294 }
295#endif /* !__WIN__ */
296 }
297
298 return VINF_SUCCESS;
299}
300
301
302/**
303 * Construct a named pipe stream driver instance.
304 *
305 * @returns VBox status.
306 * @param pDrvIns The driver instance data.
307 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
308 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
309 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
310 * iInstance it's expected to be used a bit in this function.
311 */
312static DECLCALLBACK(int) drvNamedPipeConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
313{
314 int rc;
315 PDRVNAMEDPIPE pData = PDMINS2DATA(pDrvIns, PDRVNAMEDPIPE);
316
317 /*
318 * Init the static parts.
319 */
320 pData->pDrvIns = pDrvIns;
321 pData->pszLocation = NULL;
322 pData->fIsServer = false;
323#ifdef __WIN__
324 pData->NamedPipe = NIL_RTFILE;
325#else /* !__WIN__ */
326 pData->LocalSocketServer = NIL_RTSOCKET;
327 pData->LocalSocket = NIL_RTSOCKET;
328#endif /* !__WIN__ */
329 pData->ListenThread = NIL_RTTHREAD;
330 pData->fShutdown = false;
331 /* IBase */
332 pDrvIns->IBase.pfnQueryInterface = drvNamedPipeQueryInterface;
333 /* IStream */
334 pData->IStream.pfnRead = drvNamedPipeRead;
335 pData->IStream.pfnWrite = drvNamedPipeWrite;
336
337 /*
338 * Read the configuration.
339 */
340 if (!CFGMR3AreValuesValid(pCfgHandle, "Location\0IsServer\0"))
341 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
342
343 char *pszLocation;
344 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Location", &pszLocation);
345 if (VBOX_FAILURE(rc))
346 {
347 AssertMsgFailed(("Configuration error: query \"Location\" resulted in %Vrc.\n", rc));
348 return rc;
349 }
350 pData->pszLocation = pszLocation;
351
352 bool fIsServer;
353 rc = CFGMR3QueryBool(pCfgHandle, "IsServer", &fIsServer);
354 if (VBOX_FAILURE(rc))
355 {
356 AssertMsgFailed(("Configuration error: query \"IsServer\" resulted in %Vrc.\n", rc));
357 goto out;
358 }
359 pData->fIsServer = fIsServer;
360
361#ifdef __WIN__
362 if (fIsServer)
363 {
364 rc = RTThreadCreate(&pData->ListenThread, drvNamedPipeListenLoop, (void *)pData, 0, RTTHREADTYPE_IO, 0, "NamedPipe");
365 if VBOX_FAILURE(rc)
366 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create listening thread\n"), pDrvIns->iInstance);
367 }
368 else
369 {
370 /* Connect to the named pipe. */
371 rc = RTFileOpen(&pData->NamedPipe, pData->pszLocation, RTFILE_O_READWRITE);
372 if (VBOX_FAILURE(rc))
373 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to connect to named pipe %s"), pDrvIns->iInstance, pszLocation);
374 }
375#else /* !__WIN__ */
376 int s;
377 struct sockaddr_un addr;
378
379 if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
380 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to create local socket"), pDrvIns->iInstance);
381
382 memset(&addr, 0, sizeof(addr));
383 addr.sun_family = AF_UNIX;
384 strncpy(addr.sun_path, pszLocation, sizeof(addr.sun_path)-1);
385
386 if (fIsServer)
387 {
388 /* Bind address to the local socket. */
389 RTFileDelete(pData->pszLocation);
390 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
391 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to bind to local socket %s"), pDrvIns->iInstance, pszLocation);
392 rc = RTThreadCreate(&pData->ListenThread, drvNamedPipeListenLoop, (void *)pData, 0, RTTHREADTYPE_IO, 0, "NamedPipe");
393 if VBOX_FAILURE(rc)
394 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NamedPipe#%d failed to create listening thread\n"), pDrvIns->iInstance);
395 pData->LocalSocketServer = s;
396 }
397 else
398 {
399 /* Connect to the local socket. */
400 if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
401 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS, N_("NamedPipe#%d failed to connect to local socket %s"), pDrvIns->iInstance, pszLocation);
402 pData->LocalSocket = s;
403 }
404#endif /* !__WIN__ */
405
406out:
407 if (VBOX_FAILURE(rc))
408 {
409 if (pszLocation)
410 MMR3HeapFree(pszLocation);
411 }
412 else
413 {
414 LogFlow(("drvNamedPipeConstruct: location %s isServer %d\n", pszLocation, fIsServer));
415 LogRel(("NamedPipe: location %s, %s\n", pszLocation, fIsServer ? "server" : "client"));
416 }
417 return rc;
418}
419
420
421/**
422 * Destruct a named pipe stream driver instance.
423 *
424 * Most VM resources are freed by the VM. This callback is provided so that
425 * any non-VM resources can be freed correctly.
426 *
427 * @param pDrvIns The driver instance data.
428 */
429static DECLCALLBACK(void) drvNamedPipeDestruct(PPDMDRVINS pDrvIns)
430{
431 PDRVNAMEDPIPE pData = PDMINS2DATA(pDrvIns, PDRVNAMEDPIPE);
432 LogFlow(("%s: %s\n", __FUNCTION__, pData->pszLocation));
433
434#ifdef __WIN__
435 if (pData->NamedPipe != NIL_RTFILE)
436 {
437 FlushFileBuffers((HANDLE)pData->NamedPipe);
438 DisconnectNamedPipe((HANDLE)pData->NamedPipe);
439 RTFileClose(pData->NamedPipe);
440 }
441#else /* !__WIN__ */
442 if (pData->fIsServer)
443 {
444 if (pData->LocalSocketServer != NIL_RTSOCKET)
445 close(pData->LocalSocketServer);
446 if (pData->pszLocation)
447 RTFileDelete(pData->pszLocation);
448 }
449 else
450 {
451 if (pData->LocalSocket != NIL_RTSOCKET)
452 close(pData->LocalSocket);
453 }
454#endif /* !__WIN__ */
455 if (pData->pszLocation)
456 MMR3HeapFree(pData->pszLocation);
457}
458
459
460/**
461 * Named pipe driver registration record.
462 */
463const PDMDRVREG g_DrvNamedPipe =
464{
465 /* u32Version */
466 PDM_DRVREG_VERSION,
467 /* szDriverName */
468 "NamedPipe",
469 /* pszDescription */
470 "Named Pipe stream driver.",
471 /* fFlags */
472 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
473 /* fClass. */
474 PDM_DRVREG_CLASS_STREAM,
475 /* cMaxInstances */
476 ~0,
477 /* cbInstance */
478 sizeof(DRVNAMEDPIPE),
479 /* pfnConstruct */
480 drvNamedPipeConstruct,
481 /* pfnDestruct */
482 drvNamedPipeDestruct,
483 /* pfnIOCtl */
484 NULL,
485 /* pfnPowerOn */
486 NULL,
487 /* pfnReset */
488 NULL,
489 /* pfnSuspend */
490 NULL,
491 /* pfnResume */
492 NULL,
493 /* pfnDetach */
494 NULL,
495 /* pfnPowerOff */
496 NULL
497};
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