VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DrvTCP.cpp@ 85561

Last change on this file since 85561 was 84688, checked in by vboxsync, 5 years ago

Devices/Serial/DrvTCP: Avoid filling up the pipe with external woken up requests by setting a flag and avoiding writes to the pipe when it is already set, also drain the complete pipe in one go when checking for external requests instead of processing just a single item

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.2 KB
Line 
1/* $Id: DrvTCP.cpp 84688 2020-06-05 09:04:51Z vboxsync $ */
2/** @file
3 * TCP socket driver implementing the IStream interface.
4 */
5
6/*
7 * Contributed by Alexey Eromenko (derived from DrvNamedPipe).
8 *
9 * Copyright (C) 2006-2020 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#define LOG_GROUP LOG_GROUP_DRV_TCP
25#include <VBox/vmm/pdmdrv.h>
26#include <iprt/assert.h>
27#include <iprt/file.h>
28#include <iprt/stream.h>
29#include <iprt/alloc.h>
30#include <iprt/pipe.h>
31#include <iprt/poll.h>
32#include <iprt/string.h>
33#include <iprt/semaphore.h>
34#include <iprt/socket.h>
35#include <iprt/tcp.h>
36#include <iprt/uuid.h>
37#include <stdlib.h>
38
39#include "VBoxDD.h"
40
41
42/*********************************************************************************************************************************
43* Defined Constants And Macros *
44*********************************************************************************************************************************/
45
46#define DRVTCP_POLLSET_ID_SOCKET 0
47#define DRVTCP_POLLSET_ID_WAKEUP 1
48
49#define DRVTCP_WAKEUP_REASON_EXTERNAL 0
50#define DRVTCP_WAKEUP_REASON_NEW_CONNECTION 1
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56/**
57 * TCP driver instance data.
58 *
59 * @implements PDMISTREAM
60 */
61typedef struct DRVTCP
62{
63 /** The stream interface. */
64 PDMISTREAM IStream;
65 /** Pointer to the driver instance. */
66 PPDMDRVINS pDrvIns;
67 /** Pointer to the TCP server address:port or port only. (Freed by MM) */
68 char *pszLocation;
69 /** Flag whether VirtualBox represents the server or client side. */
70 bool fIsServer;
71
72 /** Handle of the TCP server for incoming connections. */
73 PRTTCPSERVER hTcpServ;
74 /** Socket handle of the TCP socket connection. */
75 RTSOCKET hTcpSock;
76
77 /** Poll set used to wait for I/O events. */
78 RTPOLLSET hPollSet;
79 /** Reading end of the wakeup pipe. */
80 RTPIPE hPipeWakeR;
81 /** Writing end of the wakeup pipe. */
82 RTPIPE hPipeWakeW;
83 /** Flag whether the send buffer is full nad it is required to wait for more
84 * space until there is room again. */
85 bool fXmitBufFull;
86
87 /** Number of connections active. */
88 volatile uint32_t cConnections;
89 /** Thread for listening for new connections. */
90 RTTHREAD ListenThread;
91 /** Flag to signal listening thread to shut down. */
92 bool volatile fShutdown;
93 /** Flag to signal whether the thread was woken up from external. */
94 bool volatile fWokenUp;
95} DRVTCP, *PDRVTCP;
96
97
98/*********************************************************************************************************************************
99* Internal Functions *
100*********************************************************************************************************************************/
101
102
103/**
104 * Kicks any possibly polling thread to get informed about changes - extended version
105 * sending additional data along with the wakeup reason.
106 *
107 * @returns VBOx status code.
108 * @param pThis The TCP driver instance.
109 * @param bReason The reason code to handle.
110 * @param pvData The additional to send along with the wakeup reason.
111 * @param cbData Number of bytes to send along.
112 */
113static int drvTcpPollerKickEx(PDRVTCP pThis, uint8_t bReason, const void *pvData, size_t cbData)
114{
115 size_t cbWritten = 0;
116 int rc = RTPipeWriteBlocking(pThis->hPipeWakeW, &bReason, 1, &cbWritten);
117 if (RT_SUCCESS(rc))
118 rc = RTPipeWriteBlocking(pThis->hPipeWakeW, pvData, cbData, &cbWritten);
119 return rc;
120}
121
122
123/**
124 * Kicks any possibly polling thread to get informed about changes.
125 *
126 * @returns VBOx status code.
127 * @param pThis The TCP driver instance.
128 * @param bReason The reason code to handle.
129 */
130static int drvTcpPollerKick(PDRVTCP pThis, uint8_t bReason)
131{
132 size_t cbWritten = 0;
133 return RTPipeWriteBlocking(pThis->hPipeWakeW, &bReason, 1, &cbWritten);
134}
135
136
137/**
138 * Checks the wakeup pipe for events.
139 *
140 * @returns VBox status code.
141 * @param pThis The TCP driver instance.
142 * @param fEvts Event mask to set if a new connection arrived.
143 */
144static int drvTcpWakeupPipeCheckForRequest(PDRVTCP pThis, uint32_t fEvts)
145{
146 int rc = VINF_SUCCESS;
147
148 while ( RT_SUCCESS(rc)
149 || rc == VERR_INTERRUPTED)
150 {
151 uint8_t bReason;
152 size_t cbRead = 0;
153 int rc2 = RTPipeRead(pThis->hPipeWakeR, &bReason, 1, &cbRead);
154 if (rc2 == VINF_TRY_AGAIN) /* Nothing there so we are done here. */
155 break;
156 else if (RT_SUCCESS(rc2))
157 {
158 if (bReason == DRVTCP_WAKEUP_REASON_EXTERNAL)
159 {
160 ASMAtomicXchgBool(&pThis->fWokenUp, false);
161 rc = VERR_INTERRUPTED;
162 }
163 else if (bReason == DRVTCP_WAKEUP_REASON_NEW_CONNECTION)
164 {
165 Assert(pThis->hTcpSock == NIL_RTSOCKET);
166
167 /* Read the socket handle. */
168 RTSOCKET hTcpSockNew = NIL_RTSOCKET;
169 rc = RTPipeReadBlocking(pThis->hPipeWakeR, &hTcpSockNew, sizeof(hTcpSockNew), NULL);
170 AssertRC(rc);
171
172 /* Always include error event. */
173 fEvts |= RTPOLL_EVT_ERROR;
174 rc = RTPollSetAddSocket(pThis->hPollSet, hTcpSockNew,
175 fEvts, DRVTCP_POLLSET_ID_SOCKET);
176 if (RT_SUCCESS(rc))
177 pThis->hTcpSock = hTcpSockNew;
178 }
179 else
180 AssertMsgFailed(("Unknown wakeup reason in pipe %u\n", bReason));
181 }
182 }
183
184 return rc;
185}
186
187
188/** @interface_method_impl{PDMISTREAM,pfnPoll} */
189static DECLCALLBACK(int) drvTcpPoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies)
190{
191 int rc = VINF_SUCCESS;
192 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
193
194 if (pThis->hTcpSock != NIL_RTSOCKET)
195 {
196 Assert(ASMAtomicReadU32(&pThis->cConnections) > 0);
197
198 /* Always include error event. */
199 fEvts |= RTPOLL_EVT_ERROR;
200 rc = RTPollSetEventsChange(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET, fEvts);
201 AssertRC(rc);
202 }
203 else
204 {
205 /*
206 * Check whether new connection arrived first so we don't miss it in case
207 * the guest is constantly writing data and we always end up here.
208 */
209 rc = drvTcpWakeupPipeCheckForRequest(pThis, fEvts);
210 if ( pThis->hTcpSock == NIL_RTSOCKET
211 && (fEvts & RTPOLL_EVT_WRITE))
212 {
213 /*
214 * Just pretend we can always write to not fill up any buffers and block the guest
215 * from sending data.
216 */
217 *pfEvts |= RTPOLL_EVT_WRITE;
218 return rc;
219 }
220 }
221
222 if (RT_SUCCESS(rc))
223 {
224 while (RT_SUCCESS(rc))
225 {
226 uint32_t fEvtsRecv = 0;
227 uint32_t idHnd = 0;
228 uint64_t tsStartMs = RTTimeMilliTS();
229 RTMSINTERVAL cThisWaitMs = cMillies;
230
231 /*
232 * Just check for data available to be read if the send buffer wasn't full till now and
233 * the caller wants to check whether writing is possible with the event set.
234 *
235 * On Windows the write event is only posted after a send operation returned
236 * WSAEWOULDBLOCK. So without this we would block in the poll call below waiting
237 * for an event which would never happen if the buffer has space left.
238 */
239 if ( (fEvts & RTPOLL_EVT_WRITE)
240 && !pThis->fXmitBufFull
241 && pThis->hTcpSock != NIL_RTSOCKET)
242 cThisWaitMs = 0;
243
244 rc = RTPoll(pThis->hPollSet, cThisWaitMs, &fEvtsRecv, &idHnd);
245
246 /* Adjust remaining time to wait. */
247 uint64_t tsPollSpanMs = RTTimeMilliTS() - tsStartMs;
248 cMillies -= RT_MIN(cMillies, tsPollSpanMs);
249 if (RT_SUCCESS(rc))
250 {
251 if (idHnd == DRVTCP_POLLSET_ID_WAKEUP)
252 {
253 /* We got woken up, drain the pipe and return. */
254 rc = drvTcpWakeupPipeCheckForRequest(pThis, fEvts);
255 }
256 else
257 {
258 Assert(idHnd == DRVTCP_POLLSET_ID_SOCKET);
259
260 /* On error we close the socket here. */
261 if (fEvtsRecv & RTPOLL_EVT_ERROR)
262 {
263 rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
264 AssertRC(rc);
265
266 if (pThis->fIsServer)
267 RTTcpServerDisconnectClient2(pThis->hTcpSock);
268 else
269 RTSocketClose(pThis->hTcpSock);
270 pThis->hTcpSock = NIL_RTSOCKET;
271 ASMAtomicDecU32(&pThis->cConnections);
272 /* Continue with polling. */
273 }
274 else
275 {
276 if (fEvtsRecv & RTPOLL_EVT_WRITE)
277 pThis->fXmitBufFull = false;
278 else if (!pThis->fXmitBufFull)
279 fEvtsRecv |= RTPOLL_EVT_WRITE;
280 *pfEvts = fEvtsRecv;
281 break;
282 }
283 }
284 }
285 else if ( rc == VERR_TIMEOUT
286 && !pThis->fXmitBufFull)
287 {
288 *pfEvts = RTPOLL_EVT_WRITE;
289 rc = VINF_SUCCESS;
290 break;
291 }
292 }
293 }
294
295 return rc;
296}
297
298
299/** @interface_method_impl{PDMISTREAM,pfnPollInterrupt} */
300static DECLCALLBACK(int) drvTcpPollInterrupt(PPDMISTREAM pInterface)
301{
302 int rc = VINF_SUCCESS;
303 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
304
305 if (!ASMAtomicXchgBool(&pThis->fWokenUp, true))
306 rc = drvTcpPollerKick(pThis, DRVTCP_WAKEUP_REASON_EXTERNAL);
307
308 return rc;
309}
310
311
312/** @interface_method_impl{PDMISTREAM,pfnRead} */
313static DECLCALLBACK(int) drvTcpRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)
314{
315 int rc = VINF_SUCCESS;
316 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
317 LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation));
318
319 Assert(pvBuf);
320
321 if (pThis->hTcpSock != NIL_RTSOCKET)
322 {
323 size_t cbRead;
324 size_t cbBuf = *pcbRead;
325 rc = RTSocketReadNB(pThis->hTcpSock, pvBuf, cbBuf, &cbRead);
326 if (RT_SUCCESS(rc))
327 {
328 if (!cbRead && rc != VINF_TRY_AGAIN)
329 {
330 rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
331 AssertRC(rc);
332
333 if (pThis->fIsServer)
334 RTTcpServerDisconnectClient2(pThis->hTcpSock);
335 else
336 RTSocketClose(pThis->hTcpSock);
337 pThis->hTcpSock = NIL_RTSOCKET;
338 rc = VINF_SUCCESS;
339 }
340 *pcbRead = cbRead;
341 }
342 }
343 else
344 {
345 RTThreadSleep(100);
346 *pcbRead = 0;
347 }
348
349 LogFlow(("%s: *pcbRead=%zu returns %Rrc\n", __FUNCTION__, *pcbRead, rc));
350 return rc;
351}
352
353
354/** @interface_method_impl{PDMISTREAM,pfnWrite} */
355static DECLCALLBACK(int) drvTcpWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite)
356{
357 int rc = VINF_SUCCESS;
358 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
359 LogFlow(("%s: pvBuf=%p *pcbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbWrite, pThis->pszLocation));
360
361 Assert(pvBuf);
362 if (pThis->hTcpSock != NIL_RTSOCKET)
363 {
364 size_t cbBuf = *pcbWrite;
365 rc = RTSocketWriteNB(pThis->hTcpSock, pvBuf, cbBuf, pcbWrite);
366 if (rc == VINF_TRY_AGAIN)
367 {
368 Assert(*pcbWrite == 0);
369 pThis->fXmitBufFull = true;
370 rc = VERR_TIMEOUT;
371 }
372 }
373 /* else Just pretend we wrote everything to not block. */
374
375 LogFlow(("%s: returns %Rrc *pcbWrite=%zu\n", __FUNCTION__, rc, *pcbWrite));
376 return rc;
377}
378
379
380/**
381 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
382 */
383static DECLCALLBACK(void *) drvTCPQueryInterface(PPDMIBASE pInterface, const char *pszIID)
384{
385 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
386 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
387 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
388 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISTREAM, &pThis->IStream);
389 return NULL;
390}
391
392
393/* -=-=-=-=- listen thread -=-=-=-=- */
394
395/**
396 * Receive thread loop.
397 *
398 * @returns VINF_SUCCESS
399 * @param hThreadSelf Thread handle to this thread.
400 * @param pvUser User argument.
401 */
402static DECLCALLBACK(int) drvTCPListenLoop(RTTHREAD hThreadSelf, void *pvUser)
403{
404 RT_NOREF(hThreadSelf);
405 PDRVTCP pThis = (PDRVTCP)pvUser;
406
407 while (RT_LIKELY(!pThis->fShutdown))
408 {
409 RTSOCKET hTcpSockNew = NIL_RTSOCKET;
410 int rc = RTTcpServerListen2(pThis->hTcpServ, &hTcpSockNew);
411 if (RT_SUCCESS(rc))
412 {
413 if (ASMAtomicReadU32(&pThis->cConnections) > 0)
414 {
415 LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance));
416 RTTcpServerDisconnectClient2(hTcpSockNew);
417 }
418 else
419 {
420 ASMAtomicIncU32(&pThis->cConnections);
421
422 /* Inform the poller about the new socket. */
423 drvTcpPollerKickEx(pThis, DRVTCP_WAKEUP_REASON_NEW_CONNECTION, &hTcpSockNew, sizeof(hTcpSockNew));
424 }
425 }
426 }
427
428 return VINF_SUCCESS;
429}
430
431/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
432
433/**
434 * Common worker for drvTCPPowerOff and drvTCPDestructor.
435 *
436 * @param pThis The instance data.
437 */
438static void drvTCPShutdownListener(PDRVTCP pThis)
439{
440 /*
441 * Signal shutdown of the listener thread.
442 */
443 pThis->fShutdown = true;
444 if ( pThis->fIsServer
445 && pThis->hTcpServ != NULL)
446 {
447 int rc = RTTcpServerShutdown(pThis->hTcpServ);
448 AssertRC(rc);
449 pThis->hTcpServ = NULL;
450 }
451}
452
453
454/**
455 * Power off a TCP socket stream driver instance.
456 *
457 * This does most of the destruction work, to avoid ordering dependencies.
458 *
459 * @param pDrvIns The driver instance data.
460 */
461static DECLCALLBACK(void) drvTCPPowerOff(PPDMDRVINS pDrvIns)
462{
463 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
464 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
465
466 drvTCPShutdownListener(pThis);
467}
468
469
470/**
471 * Destruct a TCP socket stream driver instance.
472 *
473 * Most VM resources are freed by the VM. This callback is provided so that
474 * any non-VM resources can be freed correctly.
475 *
476 * @param pDrvIns The driver instance data.
477 */
478static DECLCALLBACK(void) drvTCPDestruct(PPDMDRVINS pDrvIns)
479{
480 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
481 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
482 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
483
484 drvTCPShutdownListener(pThis);
485
486 /*
487 * While the thread exits, clean up as much as we can.
488 */
489 if (pThis->hTcpSock != NIL_RTSOCKET)
490 {
491 int rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
492 AssertRC(rc);
493
494 rc = RTSocketShutdown(pThis->hTcpSock, true /* fRead */, true /* fWrite */);
495 AssertRC(rc);
496
497 rc = RTSocketClose(pThis->hTcpSock);
498 AssertRC(rc); RT_NOREF(rc);
499
500 pThis->hTcpSock = NIL_RTSOCKET;
501 }
502
503 if (pThis->hPipeWakeR != NIL_RTPIPE)
504 {
505 int rc = RTPipeClose(pThis->hPipeWakeR);
506 AssertRC(rc);
507
508 pThis->hPipeWakeR = NIL_RTPIPE;
509 }
510
511 if (pThis->hPipeWakeW != NIL_RTPIPE)
512 {
513 int rc = RTPipeClose(pThis->hPipeWakeW);
514 AssertRC(rc);
515
516 pThis->hPipeWakeW = NIL_RTPIPE;
517 }
518
519 if (pThis->hPollSet != NIL_RTPOLLSET)
520 {
521 int rc = RTPollSetDestroy(pThis->hPollSet);
522 AssertRC(rc);
523
524 pThis->hPollSet = NIL_RTPOLLSET;
525 }
526
527 MMR3HeapFree(pThis->pszLocation);
528 pThis->pszLocation = NULL;
529
530 /*
531 * Wait for the thread.
532 */
533 if (pThis->ListenThread != NIL_RTTHREAD)
534 {
535 int rc = RTThreadWait(pThis->ListenThread, 30000, NULL);
536 if (RT_SUCCESS(rc))
537 pThis->ListenThread = NIL_RTTHREAD;
538 else
539 LogRel(("DrvTCP%d: listen thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
540 }
541}
542
543
544/**
545 * Construct a TCP socket stream driver instance.
546 *
547 * @copydoc FNPDMDRVCONSTRUCT
548 */
549static DECLCALLBACK(int) drvTCPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
550{
551 RT_NOREF(fFlags);
552 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
553 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
554
555 /*
556 * Init the static parts.
557 */
558 pThis->pDrvIns = pDrvIns;
559 pThis->pszLocation = NULL;
560 pThis->fIsServer = false;
561 pThis->fXmitBufFull = false;
562 pThis->cConnections = 0;
563
564 pThis->hTcpServ = NULL;
565 pThis->hTcpSock = NIL_RTSOCKET;
566
567 pThis->hPollSet = NIL_RTPOLLSET;
568 pThis->hPipeWakeR = NIL_RTPIPE;
569 pThis->hPipeWakeW = NIL_RTPIPE;
570
571 pThis->ListenThread = NIL_RTTHREAD;
572 pThis->fShutdown = false;
573 pThis->fWokenUp = false;
574 /* IBase */
575 pDrvIns->IBase.pfnQueryInterface = drvTCPQueryInterface;
576 /* IStream */
577 pThis->IStream.pfnPoll = drvTcpPoll;
578 pThis->IStream.pfnPollInterrupt = drvTcpPollInterrupt;
579 pThis->IStream.pfnRead = drvTcpRead;
580 pThis->IStream.pfnWrite = drvTcpWrite;
581
582 /*
583 * Validate and read the configuration.
584 */
585 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|IsServer", "");
586
587 int rc = CFGMR3QueryStringAlloc(pCfg, "Location", &pThis->pszLocation);
588 if (RT_FAILURE(rc))
589 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
590 N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
591 rc = CFGMR3QueryBool(pCfg, "IsServer", &pThis->fIsServer);
592 if (RT_FAILURE(rc))
593 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
594 N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc);
595
596 rc = RTPipeCreate(&pThis->hPipeWakeR, &pThis->hPipeWakeW, 0 /* fFlags */);
597 if (RT_FAILURE(rc))
598 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
599 N_("DrvTCP#%d: Failed to create wake pipe"), pDrvIns->iInstance);
600
601 rc = RTPollSetCreate(&pThis->hPollSet);
602 if (RT_FAILURE(rc))
603 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
604 N_("DrvTCP#%d: Failed to create poll set"), pDrvIns->iInstance);
605
606 rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeWakeR,
607 RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
608 DRVTCP_POLLSET_ID_WAKEUP);
609 if (RT_FAILURE(rc))
610 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
611 N_("DrvTCP#%d failed to add wakeup pipe for %s to poll set"),
612 pDrvIns->iInstance, pThis->pszLocation);
613
614 /*
615 * Create/Open the socket.
616 */
617 if (pThis->fIsServer)
618 {
619 uint32_t uPort = 0;
620 rc = RTStrToUInt32Ex(pThis->pszLocation, NULL, 10, &uPort);
621 if (RT_FAILURE(rc))
622 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
623 N_("DrvTCP#%d: The port part of the location is not a numerical value"),
624 pDrvIns->iInstance);
625
626 /** @todo Allow binding to distinct interfaces. */
627 rc = RTTcpServerCreateEx(NULL, uPort, &pThis->hTcpServ);
628 if (RT_FAILURE(rc))
629 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
630 N_("DrvTCP#%d failed to create server socket"), pDrvIns->iInstance);
631
632 rc = RTThreadCreate(&pThis->ListenThread, drvTCPListenLoop, (void *)pThis, 0,
633 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "DrvTCPStream");
634 if (RT_FAILURE(rc))
635 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
636 N_("DrvTCP#%d failed to create listening thread"), pDrvIns->iInstance);
637 }
638 else
639 {
640 char *pszPort = strchr(pThis->pszLocation, ':');
641 if (!pszPort)
642 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS,
643 N_("DrvTCP#%d: The location misses the port to connect to"),
644 pDrvIns->iInstance);
645
646 *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
647 uint32_t uPort = 0;
648 rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort);
649 if (RT_FAILURE(rc))
650 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
651 N_("DrvTCP#%d: The port part of the location is not a numerical value"),
652 pDrvIns->iInstance);
653
654 rc = RTTcpClientConnect(pThis->pszLocation, uPort, &pThis->hTcpSock);
655 *pszPort = ':'; /* Restore delimiter before checking the status. */
656 if (RT_FAILURE(rc))
657 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
658 N_("DrvTCP#%d failed to connect to socket %s"),
659 pDrvIns->iInstance, pThis->pszLocation);
660
661 rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
662 RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR,
663 DRVTCP_POLLSET_ID_SOCKET);
664 if (RT_FAILURE(rc))
665 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
666 N_("DrvTCP#%d failed to add socket for %s to poll set"),
667 pDrvIns->iInstance, pThis->pszLocation);
668
669 ASMAtomicIncU32(&pThis->cConnections);
670 }
671
672 LogRel(("DrvTCP: %s, %s\n", pThis->pszLocation, pThis->fIsServer ? "server" : "client"));
673 return VINF_SUCCESS;
674}
675
676
677/**
678 * TCP stream driver registration record.
679 */
680const PDMDRVREG g_DrvTCP =
681{
682 /* u32Version */
683 PDM_DRVREG_VERSION,
684 /* szName */
685 "TCP",
686 /* szRCMod */
687 "",
688 /* szR0Mod */
689 "",
690 /* pszDescription */
691 "TCP serial stream driver.",
692 /* fFlags */
693 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
694 /* fClass. */
695 PDM_DRVREG_CLASS_STREAM,
696 /* cMaxInstances */
697 ~0U,
698 /* cbInstance */
699 sizeof(DRVTCP),
700 /* pfnConstruct */
701 drvTCPConstruct,
702 /* pfnDestruct */
703 drvTCPDestruct,
704 /* pfnRelocate */
705 NULL,
706 /* pfnIOCtl */
707 NULL,
708 /* pfnPowerOn */
709 NULL,
710 /* pfnReset */
711 NULL,
712 /* pfnSuspend */
713 NULL,
714 /* pfnResume */
715 NULL,
716 /* pfnAttach */
717 NULL,
718 /* pfnDetach */
719 NULL,
720 /* pfnPowerOff */
721 drvTCPPowerOff,
722 /* pfnSoftReset */
723 NULL,
724 /* u32EndVersion */
725 PDM_DRVREG_VERSION
726};
727
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