VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioTestService.cpp@ 91030

Last change on this file since 91030 was 91030, checked in by vboxsync, 4 years ago

Audio/Validation Kit: Trying to resolve the connection issues by checking whether clients were connected in reverse mode or not. This would otherwise end up in connecting more and more clients without any real use. See comments. ​bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.2 KB
Line 
1/* $Id: AudioTestService.cpp 91030 2021-08-31 12:03:07Z vboxsync $ */
2/** @file
3 * AudioTestService - Audio test execution server.
4 */
5
6/*
7 * Copyright (C) 2021 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_AUDIO_TEST
23#include <iprt/log.h>
24
25#include <iprt/alloca.h>
26#include <iprt/asm.h>
27#include <iprt/assert.h>
28#include <iprt/critsect.h>
29#include <iprt/crc.h>
30#include <iprt/ctype.h>
31#include <iprt/dir.h>
32#include <iprt/env.h>
33#include <iprt/err.h>
34#include <iprt/file.h>
35#include <iprt/getopt.h>
36#include <iprt/handle.h>
37#include <iprt/initterm.h>
38#include <iprt/json.h>
39#include <iprt/list.h>
40#include <iprt/mem.h>
41#include <iprt/message.h>
42#include <iprt/param.h>
43#include <iprt/path.h>
44#include <iprt/pipe.h>
45#include <iprt/poll.h>
46#include <iprt/process.h>
47#include <iprt/stream.h>
48#include <iprt/string.h>
49#include <iprt/thread.h>
50
51#include <VBox/log.h>
52
53#include "AudioTestService.h"
54#include "AudioTestServiceInternal.h"
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60/**
61 * A generic ATS reply, used by the client
62 * to process the incoming packets.
63 */
64typedef struct ATSSRVREPLY
65{
66 char szOp[ATSPKT_OPCODE_MAX_LEN];
67 void *pvPayload;
68 size_t cbPayload;
69} ATSSRVREPLY;
70/** Pointer to a generic ATS reply. */
71typedef struct ATSSRVREPLY *PATSSRVREPLY;
72
73
74/*********************************************************************************************************************************
75* Global Variables *
76*********************************************************************************************************************************/
77/**
78 * Transport layers.
79 */
80const PCATSTRANSPORT g_apTransports[] =
81{
82 &g_TcpTransport
83};
84/** Number of transport layers in \a g_apTransports. */
85const size_t g_cTransports = RT_ELEMENTS(g_apTransports);
86
87/**
88 * ATS client state.
89 */
90typedef enum ATSCLIENTSTATE
91{
92 /** Invalid client state. */
93 ATSCLIENTSTATE_INVALID = 0,
94 /** Client is initialising, only the HOWDY and BYE packets are allowed. */
95 ATSCLIENTSTATE_INITIALISING,
96 /** Client is in fully cuntional state and ready to process all requests. */
97 ATSCLIENTSTATE_READY,
98 /** Client is destroying. */
99 ATSCLIENTSTATE_DESTROYING,
100 /** 32bit hack. */
101 ATSCLIENTSTATE_32BIT_HACK = 0x7fffffff
102} ATSCLIENTSTATE;
103
104/**
105 * ATS client instance.
106 */
107typedef struct ATSCLIENTINST
108{
109 /** List node for new clients. */
110 RTLISTNODE NdLst;
111 /** The current client state. */
112 ATSCLIENTSTATE enmState;
113 /** Transport backend specific data. */
114 PATSTRANSPORTCLIENT pTransportClient;
115 /** Client hostname. */
116 char *pszHostname;
117} ATSCLIENTINST;
118/** Pointer to a ATS client instance. */
119typedef ATSCLIENTINST *PATSCLIENTINST;
120
121
122/*********************************************************************************************************************************
123* Prototypes *
124*********************************************************************************************************************************/
125static int atsClientDisconnect(PATSSERVER pThis, PATSCLIENTINST pInst);
126
127
128
129/**
130 * Returns the string represenation of the given state.
131 */
132static const char *atsClientStateStringify(ATSCLIENTSTATE enmState)
133{
134 switch (enmState)
135 {
136 case ATSCLIENTSTATE_INVALID:
137 return "INVALID";
138 case ATSCLIENTSTATE_INITIALISING:
139 return "INITIALISING";
140 case ATSCLIENTSTATE_READY:
141 return "READY";
142 case ATSCLIENTSTATE_DESTROYING:
143 return "DESTROYING";
144 case ATSCLIENTSTATE_32BIT_HACK:
145 default:
146 break;
147 }
148
149 AssertMsgFailed(("Unknown state %#x\n", enmState));
150 return "UNKNOWN";
151}
152
153/**
154 * Calculates the checksum value, zero any padding space and send the packet.
155 *
156 * @returns IPRT status code.
157 * @param pThis The ATS instance.
158 * @param pInst The ATS client structure.
159 * @param pPkt The packet to send. Must point to a correctly
160 * aligned buffer.
161 */
162static int atsSendPkt(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPkt)
163{
164 Assert(pPkt->cb >= sizeof(*pPkt));
165 pPkt->uCrc32 = RTCrc32(pPkt->achOpcode, pPkt->cb - RT_UOFFSETOF(ATSPKTHDR, achOpcode));
166 if (pPkt->cb != RT_ALIGN_32(pPkt->cb, ATSPKT_ALIGNMENT))
167 memset((uint8_t *)pPkt + pPkt->cb, '\0', RT_ALIGN_32(pPkt->cb, ATSPKT_ALIGNMENT) - pPkt->cb);
168
169 LogFlowFunc(("cb=%RU32 (%#x), payload=%RU32 (%#x), opcode=%.8s\n",
170 pPkt->cb, pPkt->cb, pPkt->cb - sizeof(ATSPKTHDR), pPkt->cb - sizeof(ATSPKTHDR), pPkt->achOpcode));
171 int rc = pThis->pTransport->pfnSendPkt(pThis->pTransportInst, pInst->pTransportClient, pPkt);
172 while (RT_UNLIKELY(rc == VERR_INTERRUPTED) && !pThis->fTerminate)
173 rc = pThis->pTransport->pfnSendPkt(pThis->pTransportInst, pInst->pTransportClient, pPkt);
174
175 return rc;
176}
177
178/**
179 * Sends a babble reply and disconnects the client (if applicable).
180 *
181 * @param pThis The ATS instance.
182 * @param pInst The ATS server instance.
183 * @param pszOpcode The BABBLE opcode.
184 */
185static void atsReplyBabble(PATSSERVER pThis, PATSCLIENTINST pInst, const char *pszOpcode)
186{
187 ATSPKTHDR Reply;
188 Reply.cb = sizeof(Reply);
189 Reply.uCrc32 = 0;
190 memcpy(Reply.achOpcode, pszOpcode, sizeof(Reply.achOpcode));
191
192 pThis->pTransport->pfnBabble(pThis->pTransportInst, pInst->pTransportClient, &Reply, 20*1000);
193}
194
195/**
196 * Receive and validate a packet.
197 *
198 * Will send bable responses to malformed packets that results in a error status
199 * code.
200 *
201 * @returns IPRT status code.
202 * @param pThis The ATS instance.
203 * @param pInst The opaque ATS instance structure.
204 * @param ppPktHdr Where to return the packet on success. Free
205 * with RTMemFree.
206 * @param fAutoRetryOnFailure Whether to retry on error.
207 */
208static int atsRecvPkt(PATSSERVER pThis, PATSCLIENTINST pInst, PPATSPKTHDR ppPktHdr, bool fAutoRetryOnFailure)
209{
210 for (;;)
211 {
212 PATSPKTHDR pPktHdr;
213 int rc = pThis->pTransport->pfnRecvPkt(pThis->pTransportInst, pInst->pTransportClient, &pPktHdr);
214 if (RT_SUCCESS(rc))
215 {
216 /* validate the packet. */
217 if ( pPktHdr->cb >= sizeof(ATSPKTHDR)
218 && pPktHdr->cb < ATSPKT_MAX_SIZE)
219 {
220 Log2Func(("pPktHdr=%p cb=%#x crc32=%#x opcode=%.8s\n",
221 pPktHdr, pPktHdr->cb, pPktHdr->uCrc32, pPktHdr->achOpcode));
222 uint32_t uCrc32Calc = pPktHdr->uCrc32 != 0
223 ? RTCrc32(&pPktHdr->achOpcode[0], pPktHdr->cb - RT_UOFFSETOF(ATSPKTHDR, achOpcode))
224 : 0;
225 if (pPktHdr->uCrc32 == uCrc32Calc)
226 {
227 AssertCompileMemberSize(ATSPKTHDR, achOpcode, 8);
228 if ( RT_C_IS_UPPER(pPktHdr->achOpcode[0])
229 && RT_C_IS_UPPER(pPktHdr->achOpcode[1])
230 && (RT_C_IS_UPPER(pPktHdr->achOpcode[2]) || pPktHdr->achOpcode[2] == ' ')
231 && (RT_C_IS_PRINT(pPktHdr->achOpcode[3]) || pPktHdr->achOpcode[3] == ' ')
232 && (RT_C_IS_PRINT(pPktHdr->achOpcode[4]) || pPktHdr->achOpcode[4] == ' ')
233 && (RT_C_IS_PRINT(pPktHdr->achOpcode[5]) || pPktHdr->achOpcode[5] == ' ')
234 && (RT_C_IS_PRINT(pPktHdr->achOpcode[6]) || pPktHdr->achOpcode[6] == ' ')
235 && (RT_C_IS_PRINT(pPktHdr->achOpcode[7]) || pPktHdr->achOpcode[7] == ' ')
236 )
237 {
238 Log(("cb=%#x opcode=%.8s\n", pPktHdr->cb, pPktHdr->achOpcode));
239 *ppPktHdr = pPktHdr;
240 return rc;
241 }
242
243 rc = VERR_IO_BAD_COMMAND;
244 }
245 else
246 {
247 Log(("cb=%#x opcode=%.8s crc32=%#x actual=%#x\n",
248 pPktHdr->cb, pPktHdr->achOpcode, pPktHdr->uCrc32, uCrc32Calc));
249 rc = VERR_IO_CRC;
250 }
251 }
252 else
253 rc = VERR_IO_BAD_LENGTH;
254
255 /* Send babble reply and disconnect the client if the transport is
256 connection oriented. */
257 if (rc == VERR_IO_BAD_LENGTH)
258 atsReplyBabble(pThis, pInst, "BABBLE L");
259 else if (rc == VERR_IO_CRC)
260 atsReplyBabble(pThis, pInst, "BABBLE C");
261 else if (rc == VERR_IO_BAD_COMMAND)
262 atsReplyBabble(pThis, pInst, "BABBLE O");
263 else
264 atsReplyBabble(pThis, pInst, "BABBLE ");
265 RTMemFree(pPktHdr);
266 }
267
268 /* Try again or return failure? */
269 if ( pThis->fTerminate
270 || rc != VERR_INTERRUPTED
271 || !fAutoRetryOnFailure
272 )
273 {
274 Log(("rc=%Rrc\n", rc));
275 return rc;
276 }
277 }
278}
279
280/**
281 * Make a simple reply, only status opcode.
282 *
283 * @returns IPRT status code of the send.
284 * @param pThis The ATS instance.
285 * @param pInst The opaque ATS instance structure.
286 * @param pReply The reply packet.
287 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
288 * with space.
289 * @param cbExtra Bytes in addition to the header.
290 */
291static int atsReplyInternal(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pReply, const char *pszOpcode, size_t cbExtra)
292{
293 /* copy the opcode, don't be too strict in case of a padding screw up. */
294 size_t cchOpcode = strlen(pszOpcode);
295 if (RT_LIKELY(cchOpcode == sizeof(pReply->achOpcode)))
296 memcpy(pReply->achOpcode, pszOpcode, sizeof(pReply->achOpcode));
297 else
298 {
299 Assert(cchOpcode == sizeof(pReply->achOpcode));
300 while (cchOpcode > 0 && pszOpcode[cchOpcode - 1] == ' ')
301 cchOpcode--;
302 AssertMsgReturn(cchOpcode < sizeof(pReply->achOpcode), ("%d/'%.8s'\n", cchOpcode, pszOpcode), VERR_INTERNAL_ERROR_4);
303 memcpy(pReply->achOpcode, pszOpcode, cchOpcode);
304 memset(&pReply->achOpcode[cchOpcode], ' ', sizeof(pReply->achOpcode) - cchOpcode);
305 }
306
307 pReply->cb = (uint32_t)sizeof(ATSPKTHDR) + (uint32_t)cbExtra;
308 pReply->uCrc32 = 0;
309
310 return atsSendPkt(pThis, pInst, pReply);
311}
312
313/**
314 * Make a simple reply, only status opcode.
315 *
316 * @returns IPRT status code of the send.
317 * @param pThis The ATS instance.
318 * @param pInst The opaque ATS instance structure.
319 * @param pPktHdr The original packet (for future use).
320 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
321 * with space.
322 */
323static int atsReplySimple(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr, const char *pszOpcode)
324{
325 return atsReplyInternal(pThis, pInst, pPktHdr, pszOpcode, 0);
326}
327
328/**
329 * Acknowledges a packet with success.
330 *
331 * @returns IPRT status code of the send.
332 * @param pThis The ATS instance.
333 * @param pInst The opaque ATS instance structure.
334 * @param pPktHdr The original packet (for future use).
335 */
336static int atsReplyAck(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
337{
338 return atsReplySimple(pThis, pInst, pPktHdr, "ACK ");
339}
340
341/**
342 * Replies with a failure.
343 *
344 * @returns IPRT status code of the send.
345 * @param pThis The ATS instance.
346 * @param pInst The opaque ATS instance structure.
347 * @param pPktHdr The original packet (for future use).
348 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
349 * with space.
350 * @param rcReq The status code of the request.
351 * @param pszDetailFmt Longer description of the problem (format string).
352 * @param va Format arguments.
353 */
354static int atsReplyFailureV(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr,
355 const char *pszOpcode, int rcReq, const char *pszDetailFmt, va_list va)
356{
357 RT_NOREF(pPktHdr);
358 union
359 {
360 ATSPKTHDR Hdr;
361 int rc;
362 char ach[256];
363 } uPkt;
364 RT_ZERO(uPkt);
365
366 size_t cchDetail = RTStrPrintfV(&uPkt.ach[sizeof(ATSPKTHDR)],
367 sizeof(uPkt) - sizeof(ATSPKTHDR),
368 pszDetailFmt, va);
369
370 uPkt.rc = rcReq;
371
372 return atsReplyInternal(pThis, pInst, &uPkt.Hdr, pszOpcode, sizeof(int) + cchDetail + 1);
373}
374
375/**
376 * Replies with a failure.
377 *
378 * @returns IPRT status code of the send.
379 * @param pThis The ATS instance.
380 * @param pInst The opaque ATS instance structure.
381 * @param pPktHdr The original packet (for future use).
382 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
383 * with space.
384 * @param rcReq Status code.
385 * @param pszDetailFmt Longer description of the problem (format string).
386 * @param ... Format arguments.
387 */
388static int atsReplyFailure(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr,
389 const char *pszOpcode, int rcReq, const char *pszDetailFmt, ...)
390{
391 va_list va;
392 va_start(va, pszDetailFmt);
393 int rc = atsReplyFailureV(pThis, pInst, pPktHdr, pszOpcode, rcReq, pszDetailFmt, va);
394 va_end(va);
395 return rc;
396}
397
398/**
399 * Replies according to the return code.
400 *
401 * @returns IPRT status code of the send.
402 * @param pThis The ATS instance.
403 * @param pInst The opaque ATS instance structure.
404 * @param pPktHdr The packet to reply to.
405 * @param rcOperation The status code to report.
406 * @param pszOperationFmt The operation that failed. Typically giving the
407 * function call with important arguments.
408 * @param ... Arguments to the format string.
409 */
410static int atsReplyRC(PATSSERVER pThis,
411 PATSCLIENTINST pInst, PATSPKTHDR pPktHdr, int rcOperation, const char *pszOperationFmt, ...)
412{
413 if (RT_SUCCESS(rcOperation))
414 return atsReplyAck(pThis, pInst, pPktHdr);
415
416 char szOperation[128];
417 va_list va;
418 va_start(va, pszOperationFmt);
419 RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va);
420 va_end(va);
421
422 return atsReplyFailure(pThis, pInst, pPktHdr, "FAILED ", rcOperation, "%s failed with rc=%Rrc (opcode '%.8s')",
423 szOperation, rcOperation, pPktHdr->achOpcode);
424}
425
426/**
427 * Signal a bad packet exact size.
428 *
429 * @returns IPRT status code of the send.
430 * @param pThis The ATS instance.
431 * @param pInst The opaque ATS instance structure.
432 * @param pPktHdr The packet to reply to.
433 * @param cb The wanted size.
434 */
435static int atsReplyBadSize(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr, size_t cb)
436{
437 return atsReplyFailure(pThis, pInst, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at %zu bytes, got %u (opcode '%.8s')",
438 cb, pPktHdr->cb, pPktHdr->achOpcode);
439}
440
441/**
442 * Deals with a unknown command.
443 *
444 * @returns IPRT status code of the send.
445 * @param pThis The ATS instance.
446 * @param pInst The opaque ATS instance structure.
447 * @param pPktHdr The packet to reply to.
448 */
449static int atsReplyUnknown(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
450{
451 return atsReplyFailure(pThis, pInst, pPktHdr, "UNKNOWN ", VERR_NOT_FOUND, "Opcode '%.8s' is not known", pPktHdr->achOpcode);
452}
453
454/**
455 * Deals with a command sent in an invalid client state.
456 *
457 * @returns IPRT status code of the send.
458 * @param pThis The ATS instance.
459 * @param pInst The opaque ATS instance structure.
460 * @param pPktHdr The packet containing the unterminated string.
461 */
462static int atsReplyInvalidState(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
463{
464 return atsReplyFailure(pThis, pInst, pPktHdr, "INVSTATE", VERR_INVALID_STATE, "Opcode '%.8s' is not supported at client state '%s",
465 pPktHdr->achOpcode, atsClientStateStringify(pInst->enmState));
466}
467
468/**
469 * Verifies and acknowledges a "BYE" request.
470 *
471 * @returns IPRT status code.
472 * @param pThis The ATS instance.
473 * @param pInst The opaque ATS instance structure.
474 * @param pPktHdr The bye packet.
475 */
476static int atsDoBye(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
477{
478 int rc;
479 if (pPktHdr->cb == sizeof(ATSPKTHDR))
480 {
481 if (pThis->Callbacks.pfnBye)
482 {
483 rc = pThis->Callbacks.pfnBye(pThis->Callbacks.pvUser);
484 }
485 else
486 rc = VINF_SUCCESS;
487
488 if (RT_SUCCESS(rc))
489 {
490 rc = atsReplyAck(pThis, pInst, pPktHdr);
491 }
492 else
493 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Disconnecting client failed");
494 }
495 else
496 rc = atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTHDR));
497 return rc;
498}
499
500/**
501 * Verifies and acknowledges a "HOWDY" request.
502 *
503 * @returns IPRT status code.
504 * @param pThis The ATS instance.
505 * @param pInst The opaque ATS instance structure.
506 * @param pPktHdr The howdy packet.
507 */
508static int atsDoHowdy(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
509{
510 int rc = VINF_SUCCESS;
511
512 if (pPktHdr->cb != sizeof(ATSPKTREQHOWDY))
513 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQHOWDY));
514
515 if (pInst->enmState != ATSCLIENTSTATE_INITIALISING)
516 return atsReplyInvalidState(pThis, pInst, pPktHdr);
517
518 PATSPKTREQHOWDY pReq = (PATSPKTREQHOWDY)pPktHdr;
519
520 if (pReq->uVersion != ATS_PROTOCOL_VS)
521 return atsReplyRC(pThis, pInst, pPktHdr, VERR_VERSION_MISMATCH, "The given version %#x is not supported", pReq->uVersion);
522
523 ATSPKTREPHOWDY Rep;
524 RT_ZERO(Rep);
525
526 Rep.uVersion = ATS_PROTOCOL_VS;
527
528 rc = atsReplyInternal(pThis, pInst, &Rep.Hdr, "ACK ", sizeof(Rep) - sizeof(ATSPKTHDR));
529 if (RT_SUCCESS(rc))
530 {
531 pThis->pTransport->pfnNotifyHowdy(pThis->pTransportInst, pInst->pTransportClient);
532
533 if (pThis->Callbacks.pfnHowdy)
534 rc = pThis->Callbacks.pfnHowdy(pThis->Callbacks.pvUser);
535
536 if (RT_SUCCESS(rc))
537 pInst->enmState = ATSCLIENTSTATE_READY;
538 }
539
540 return rc;
541}
542
543/**
544 * Verifies and acknowledges a "TSET BEG" request.
545 *
546 * @returns IPRT status code.
547 * @param pThis The ATS instance.
548 * @param pInst The opaque ATS instance structure.
549 * @param pPktHdr The test set begin packet.
550 */
551static int atsDoTestSetBegin(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
552{
553 if (pPktHdr->cb != sizeof(ATSPKTREQTSETBEG))
554 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTSETBEG));
555
556 PATSPKTREQTSETBEG pReq = (PATSPKTREQTSETBEG)pPktHdr;
557
558 int rc = VINF_SUCCESS;
559
560 if (pThis->Callbacks.pfnTestSetBegin)
561 {
562 rc = pThis->Callbacks.pfnTestSetBegin(pThis->Callbacks.pvUser, pReq->szTag);
563 if (RT_FAILURE(rc))
564 return atsReplyRC(pThis, pInst, pPktHdr, rc, "Beginning test set '%s' failed", pReq->szTag);
565 }
566
567 if (RT_SUCCESS(rc))
568 {
569 rc = atsReplyAck(pThis, pInst, pPktHdr);
570 }
571 else
572 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Beginning test set failed");
573
574 return rc;
575}
576
577/**
578 * Verifies and acknowledges a "TSET END" request.
579 *
580 * @returns IPRT status code.
581 * @param pThis The ATS instance.
582 * @param pInst The opaque ATS instance structure.
583 * @param pPktHdr The test set end packet.
584 */
585static int atsDoTestSetEnd(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
586{
587 if (pPktHdr->cb != sizeof(ATSPKTREQTSETEND))
588 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTSETEND));
589
590 PATSPKTREQTSETEND pReq = (PATSPKTREQTSETEND)pPktHdr;
591
592 int rc = VINF_SUCCESS;
593
594 if (pThis->Callbacks.pfnTestSetEnd)
595 {
596 rc = pThis->Callbacks.pfnTestSetEnd(pThis->Callbacks.pvUser, pReq->szTag);
597 if (RT_FAILURE(rc))
598 return atsReplyRC(pThis, pInst, pPktHdr, rc, "Ending test set '%s' failed", pReq->szTag);
599 }
600 if (RT_SUCCESS(rc))
601 {
602 rc = atsReplyAck(pThis, pInst, pPktHdr);
603 }
604 else
605 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Ending test set failed");
606
607 return rc;
608}
609
610/**
611 * Used by atsDoTestSetSend to wait for a reply ACK from the client.
612 *
613 * @returns VINF_SUCCESS on ACK, VERR_GENERAL_FAILURE on NACK,
614 * VERR_NET_NOT_CONNECTED on unknown response (sending a bable reply),
615 * or whatever atsRecvPkt returns.
616 * @param pThis The ATS instance.
617 * @param pInst The opaque ATS instance structure.
618 * @param pPktHdr The original packet (for future use).
619 */
620static int atsWaitForAck(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
621{
622 RT_NOREF(pPktHdr);
623 /** @todo timeout? */
624 PATSPKTHDR pReply;
625 int rc = atsRecvPkt(pThis, pInst, &pReply, false /*fAutoRetryOnFailure*/);
626 if (RT_SUCCESS(rc))
627 {
628 if (atsIsSameOpcode(pReply, "ACK"))
629 rc = VINF_SUCCESS;
630 else if (atsIsSameOpcode(pReply, "NACK"))
631 rc = VERR_GENERAL_FAILURE;
632 else
633 {
634 atsReplyBabble(pThis, pInst, "BABBLE ");
635 rc = VERR_NET_NOT_CONNECTED;
636 }
637 RTMemFree(pReply);
638 }
639 return rc;
640}
641
642/**
643 * Verifies and acknowledges a "TSET SND" request.
644 *
645 * @returns IPRT status code.
646 * @param pThis The ATS instance.
647 * @param pInst The opaque ATS instance structure.
648 * @param pPktHdr The test set end packet.
649 */
650static int atsDoTestSetSend(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
651{
652 if (pPktHdr->cb != sizeof(ATSPKTREQTSETSND))
653 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTSETSND));
654
655 PATSPKTREQTSETSND pReq = (PATSPKTREQTSETSND)pPktHdr;
656
657 int rc = VINF_SUCCESS;
658
659 if (!pThis->Callbacks.pfnTestSetSendRead)
660 return atsReplyRC(pThis, pInst, pPktHdr, VERR_NOT_SUPPORTED, "Sending test set not implemented");
661
662 if (pThis->Callbacks.pfnTestSetSendBegin)
663 {
664 rc = pThis->Callbacks.pfnTestSetSendBegin(pThis->Callbacks.pvUser, pReq->szTag);
665 if (RT_FAILURE(rc))
666 return atsReplyRC(pThis, pInst, pPktHdr, rc, "Beginning sending test set '%s' failed", pReq->szTag);
667 }
668
669 for (;;)
670 {
671 uint32_t uMyCrc32 = RTCrc32Start();
672 struct
673 {
674 ATSPKTHDR Hdr;
675 uint32_t uCrc32;
676 char ab[_64K];
677 char abPadding[ATSPKT_ALIGNMENT];
678 } Pkt;
679#ifdef DEBUG
680 RT_ZERO(Pkt);
681#endif
682 size_t cbRead = 0;
683 rc = pThis->Callbacks.pfnTestSetSendRead(pThis->Callbacks.pvUser, pReq->szTag, &Pkt.ab, sizeof(Pkt.ab), &cbRead);
684 if ( RT_FAILURE(rc)
685 || cbRead == 0)
686 {
687 if ( rc == VERR_EOF
688 || (RT_SUCCESS(rc) && cbRead == 0))
689 {
690 Pkt.uCrc32 = RTCrc32Finish(uMyCrc32);
691 rc = atsReplyInternal(pThis, pInst, &Pkt.Hdr, "DATA EOF", sizeof(uint32_t) /* uCrc32 */);
692 if (RT_SUCCESS(rc))
693 rc = atsWaitForAck(pThis, pInst, &Pkt.Hdr);
694 }
695 else
696 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Sending data for test set '%s' failed", pReq->szTag);
697 break;
698 }
699
700 uMyCrc32 = RTCrc32Process(uMyCrc32, &Pkt.ab[0], cbRead);
701 Pkt.uCrc32 = RTCrc32Finish(uMyCrc32);
702
703 Log2Func(("cbRead=%zu -> uCrc32=%#x\n", cbRead, Pkt.uCrc32));
704
705 Assert(cbRead <= sizeof(Pkt.ab));
706
707 rc = atsReplyInternal(pThis, pInst, &Pkt.Hdr, "DATA ", sizeof(uint32_t) /* uCrc32 */ + cbRead);
708 if (RT_FAILURE(rc))
709 break;
710
711 rc = atsWaitForAck(pThis, pInst, &Pkt.Hdr);
712 if (RT_FAILURE(rc))
713 break;
714 }
715
716 if (pThis->Callbacks.pfnTestSetSendEnd)
717 {
718 int rc2 = pThis->Callbacks.pfnTestSetSendEnd(pThis->Callbacks.pvUser, pReq->szTag);
719 if (RT_FAILURE(rc2))
720 return atsReplyRC(pThis, pInst, pPktHdr, rc2, "Ending sending test set '%s' failed", pReq->szTag);
721 }
722
723 return rc;
724}
725
726/**
727 * Verifies and processes a "TN PLY" request.
728 *
729 * @returns IPRT status code.
730 * @param pThis The ATS instance.
731 * @param pInst The opaque ATS instance structure.
732 * @param pPktHdr The packet header.
733 */
734static int atsDoTonePlay(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
735{
736 int rc = VINF_SUCCESS;
737
738 if (pPktHdr->cb < sizeof(ATSPKTREQTONEPLAY))
739 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTONEPLAY));
740
741 if (pInst->enmState != ATSCLIENTSTATE_READY)
742 return atsReplyInvalidState(pThis, pInst, pPktHdr);
743
744 if (!pThis->Callbacks.pfnTonePlay)
745 return atsReplyRC(pThis, pInst, pPktHdr, VERR_NOT_SUPPORTED, "Playing tones not supported");
746
747 PATSPKTREQTONEPLAY pReq = (PATSPKTREQTONEPLAY)pPktHdr;
748 rc = pThis->Callbacks.pfnTonePlay(pThis->Callbacks.pvUser, &pReq->ToneParms);
749
750 int rc2 = atsReplyAck(pThis, pInst, pPktHdr);
751 if (RT_SUCCESS(rc))
752 rc = rc2;
753
754 return rc;
755}
756
757/**
758 * Verifies and processes a "TN REC" request.
759 *
760 * @returns IPRT status code.
761 * @param pThis The ATS instance.
762 * @param pInst The opaque ATS instance structure.
763 * @param pPktHdr The packet header.
764 */
765static int atsDoToneRecord(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
766{
767 int rc = VINF_SUCCESS;
768
769 if (pPktHdr->cb < sizeof(ATSPKTREQTONEREC))
770 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTONEREC));
771
772 if (pInst->enmState != ATSCLIENTSTATE_READY)
773 return atsReplyInvalidState(pThis, pInst, pPktHdr);
774
775 if (!pThis->Callbacks.pfnToneRecord)
776 return atsReplyRC(pThis, pInst, pPktHdr, VERR_NOT_SUPPORTED, "Recording tones not supported");
777
778 PATSPKTREQTONEREC pReq = (PATSPKTREQTONEREC)pPktHdr;
779 rc = pThis->Callbacks.pfnToneRecord(pThis->Callbacks.pvUser, &pReq->ToneParms);
780
781 int rc2 = atsReplyAck(pThis, pInst, pPktHdr);
782 if (RT_SUCCESS(rc))
783 rc = rc2;
784
785 return rc;
786}
787
788/**
789 * Main request processing routine for each client.
790 *
791 * @returns IPRT status code.
792 * @param pThis The ATS instance.
793 * @param pInst The ATS client structure sending the request.
794 * @param pfDisconnect Where to return whether to disconnect the client on success or not.
795 */
796static int atsClientReqProcess(PATSSERVER pThis, PATSCLIENTINST pInst, bool *pfDisconnect)
797{
798 /*
799 * Read client command packet and process it.
800 */
801 PATSPKTHDR pPktHdr = NULL;
802 int rc = atsRecvPkt(pThis, pInst, &pPktHdr, true /*fAutoRetryOnFailure*/);
803 if (RT_FAILURE(rc))
804 return rc;
805
806 /*
807 * Do a string switch on the opcode bit.
808 */
809 /* Connection: */
810 if ( atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_HOWDY))
811 rc = atsDoHowdy(pThis, pInst, pPktHdr);
812 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_BYE))
813 {
814 rc = atsDoBye(pThis, pInst, pPktHdr);
815 if (RT_SUCCESS(rc))
816 *pfDisconnect = true;
817 }
818 /* Test set handling: */
819 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TESTSET_BEGIN))
820 rc = atsDoTestSetBegin(pThis, pInst, pPktHdr);
821 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TESTSET_END))
822 rc = atsDoTestSetEnd(pThis, pInst, pPktHdr);
823 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TESTSET_SEND))
824 rc = atsDoTestSetSend(pThis, pInst, pPktHdr);
825 /* Audio testing: */
826 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TONE_PLAY))
827 rc = atsDoTonePlay(pThis, pInst, pPktHdr);
828 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TONE_RECORD))
829 rc = atsDoToneRecord(pThis, pInst, pPktHdr);
830 /* Misc: */
831 else
832 rc = atsReplyUnknown(pThis, pInst, pPktHdr);
833
834 RTMemFree(pPktHdr);
835
836 return rc;
837}
838
839static int atsClientDisconnect(PATSSERVER pThis, PATSCLIENTINST pInst)
840{
841 AssertReturn(pInst->enmState != ATSCLIENTSTATE_DESTROYING, VERR_WRONG_ORDER);
842
843 pInst->enmState = ATSCLIENTSTATE_DESTROYING;
844 pThis->pTransport->pfnNotifyBye(pThis->pTransportInst, pInst->pTransportClient);
845
846 return VINF_SUCCESS;
847}
848
849/**
850 * Free's (destroys) a client instance.
851 *
852 * @returns nothing.
853 * @param pInst The opaque ATS instance structure.
854 */
855static void atsClientFree(PATSCLIENTINST pInst)
856{
857 if (pInst->pszHostname)
858 RTStrFree(pInst->pszHostname);
859 RTMemFree(pInst);
860}
861
862/**
863 * The main thread worker serving the clients.
864 */
865static DECLCALLBACK(int) atsClientWorker(RTTHREAD hThread, void *pvUser)
866{
867 RT_NOREF(hThread);
868
869 PATSSERVER pThis = (PATSSERVER)pvUser;
870 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
871
872 unsigned cClientsMax = 0;
873 unsigned cClientsCur = 0;
874 PATSCLIENTINST *papInsts = NULL;
875
876 /* Add the pipe to the poll set. */
877 int rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 0);
878 if (RT_SUCCESS(rc))
879 {
880 while (!pThis->fTerminate)
881 {
882 uint32_t fEvts;
883 uint32_t uId;
884 rc = RTPoll(pThis->hPollSet, RT_INDEFINITE_WAIT, &fEvts, &uId);
885 if (RT_SUCCESS(rc))
886 {
887 if (uId == 0)
888 {
889 if (fEvts & RTPOLL_EVT_ERROR)
890 break;
891
892 /* We got woken up because of a new client. */
893 Assert(fEvts & RTPOLL_EVT_READ);
894
895 uint8_t bRead;
896 size_t cbRead = 0;
897 rc = RTPipeRead(pThis->hPipeR, &bRead, 1, &cbRead);
898 AssertRC(rc);
899
900 RTCritSectEnter(&pThis->CritSectClients);
901 /* Walk the list and add all new clients. */
902 PATSCLIENTINST pIt, pItNext;
903 RTListForEachSafe(&pThis->LstClientsNew, pIt, pItNext, ATSCLIENTINST, NdLst)
904 {
905 RTListNodeRemove(&pIt->NdLst);
906 Assert(cClientsCur <= cClientsMax);
907 if (cClientsCur == cClientsMax)
908 {
909 /* Realloc to accommodate for the new clients. */
910 PATSCLIENTINST *papInstsNew = (PATSCLIENTINST *)RTMemReallocZ(papInsts, cClientsMax * sizeof(PATSCLIENTINST), (cClientsMax + 10) * sizeof(PATSCLIENTINST));
911 if (RT_LIKELY(papInstsNew))
912 {
913 cClientsMax += 10;
914 papInsts = papInstsNew;
915 }
916 }
917 if (cClientsCur < cClientsMax)
918 {
919 /* Find a free slot in the client array. */
920 unsigned idxSlt = 0;
921 while ( idxSlt < cClientsMax
922 && papInsts[idxSlt] != NULL)
923 idxSlt++;
924
925 rc = pThis->pTransport->pfnPollSetAdd(pThis->pTransportInst, pThis->hPollSet, pIt->pTransportClient, idxSlt + 1);
926 if (RT_SUCCESS(rc))
927 {
928 cClientsCur++;
929 papInsts[idxSlt] = pIt;
930 }
931 else
932 {
933 atsClientDisconnect(pThis, pIt);
934 atsClientFree(pIt);
935 pIt = NULL;
936 }
937 }
938 else
939 {
940 atsClientDisconnect(pThis, pIt);
941 atsClientFree(pIt);
942 pIt = NULL;
943 }
944 }
945 RTCritSectLeave(&pThis->CritSectClients);
946 }
947 else
948 {
949 bool fDisconnect = false;
950
951 /* Client sends a request, pick the right client and process it. */
952 PATSCLIENTINST pInst = papInsts[uId - 1];
953 AssertPtr(pInst);
954 if (fEvts & RTPOLL_EVT_READ)
955 rc = atsClientReqProcess(pThis, pInst, &fDisconnect);
956
957 if ( (fEvts & RTPOLL_EVT_ERROR)
958 || RT_FAILURE(rc)
959 || fDisconnect)
960 {
961 /* Close connection and remove client from array. */
962 int rc2 = pThis->pTransport->pfnPollSetRemove(pThis->pTransportInst, pThis->hPollSet, pInst->pTransportClient, uId);
963 AssertRC(rc2);
964
965 atsClientDisconnect(pThis, pInst);
966 atsClientFree(pInst);
967 pInst = NULL;
968
969 papInsts[uId - 1] = NULL;
970 Assert(cClientsCur);
971 cClientsCur--;
972 }
973 }
974 }
975 }
976 }
977
978 return rc;
979}
980
981/**
982 * The main thread waiting for new client connections.
983 *
984 * @returns VBox status code.
985 */
986static DECLCALLBACK(int) atsMainThread(RTTHREAD hThread, void *pvUser)
987{
988 RT_NOREF(hThread);
989
990 LogRelFlowFuncEnter();
991
992 PATSSERVER pThis = (PATSSERVER)pvUser;
993 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
994
995 int rc = RTThreadUserSignal(hThread);
996 AssertRCReturn(rc, rc);
997
998 while (!pThis->fTerminate)
999 {
1000 /*
1001 * Wait for new connection and spin off a new thread
1002 * for every new client.
1003 */
1004 bool fFromServer;
1005 PATSTRANSPORTCLIENT pTransportClient;
1006 rc = pThis->pTransport->pfnWaitForConnect(pThis->pTransportInst, 1000 /* msTimeout */, &fFromServer, &pTransportClient);
1007 if (RT_FAILURE(rc))
1008 continue;
1009
1010 /*
1011 * New connection, create new client structure and spin of
1012 * the request handling thread.
1013 */
1014 PATSCLIENTINST pInst = (PATSCLIENTINST)RTMemAllocZ(sizeof(ATSCLIENTINST));
1015 if (RT_LIKELY(pInst))
1016 {
1017 pInst->enmState = ATSCLIENTSTATE_INITIALISING;
1018 pInst->pTransportClient = pTransportClient;
1019 pInst->pszHostname = NULL;
1020
1021 /* Add client to the new list and inform the worker thread. */
1022 RTCritSectEnter(&pThis->CritSectClients);
1023 RTListAppend(&pThis->LstClientsNew, &pInst->NdLst);
1024 RTCritSectLeave(&pThis->CritSectClients);
1025
1026 size_t cbWritten = 0;
1027 rc = RTPipeWrite(pThis->hPipeW, "", 1, &cbWritten);
1028 if (RT_FAILURE(rc))
1029 LogRelFunc(("Failed to inform worker thread of a new client, rc=%Rrc\n", rc));
1030 }
1031 else
1032 {
1033 LogRelFunc(("Creating new client structure failed with out of memory error\n"));
1034 pThis->pTransport->pfnNotifyBye(pThis->pTransportInst, pTransportClient);
1035 rc = VERR_NO_MEMORY;
1036 break; /* This is fatal, break out of the loop. */
1037 }
1038
1039 if (RT_SUCCESS(rc))
1040 {
1041 LogRelFunc(("New connection established (%s)\n", fFromServer ? "from server" : "as client"));
1042
1043 /**
1044 * If the new client is not from our server but from a remote server (also called a reverse connection),
1045 * exit this loop and stop trying to connect to the remote server.
1046 *
1047 * Otherwise we would connect lots and lots of clients without any real use.
1048 *
1049 ** @todo Improve this handling -- there might be a better / more elegant solution.
1050 */
1051 if (!fFromServer)
1052 break;
1053 }
1054 }
1055
1056 LogRelFlowFuncLeaveRC(rc);
1057 return rc;
1058}
1059
1060/**
1061 * Initializes an ATS instance.
1062 *
1063 * @note This does *not* start the server!
1064 *
1065 * @returns VBox status code.
1066 * @param pThis The ATS instance.
1067 * @param pCallbacks The callbacks table to use.
1068 */
1069int AudioTestSvcInit(PATSSERVER pThis, PCATSCALLBACKS pCallbacks)
1070{
1071 LogRelFlowFuncEnter();
1072
1073 RT_BZERO(pThis, sizeof(ATSSERVER));
1074
1075 pThis->hPipeR = NIL_RTPIPE;
1076 pThis->hPipeW = NIL_RTPIPE;
1077
1078 RTListInit(&pThis->LstClientsNew);
1079
1080 /* Copy callback table. */
1081 memcpy(&pThis->Callbacks, pCallbacks, sizeof(ATSCALLBACKS));
1082
1083 int rc = RTCritSectInit(&pThis->CritSectClients);
1084 if (RT_SUCCESS(rc))
1085 {
1086 rc = RTPollSetCreate(&pThis->hPollSet);
1087 if (RT_SUCCESS(rc))
1088 {
1089 rc = RTPipeCreate(&pThis->hPipeR, &pThis->hPipeW, 0);
1090 if (RT_SUCCESS(rc))
1091 {
1092 /*
1093 * The default transporter is the first one.
1094 */
1095 pThis->pTransport = g_apTransports[0]; /** @todo Make this dynamic. */
1096
1097 rc = pThis->pTransport->pfnCreate(&pThis->pTransportInst);
1098 if (RT_SUCCESS(rc))
1099 return VINF_SUCCESS;
1100
1101 RTPipeClose(pThis->hPipeR);
1102 RTPipeClose(pThis->hPipeW);
1103 }
1104 else
1105 LogRel(("Creating communications pipe failed with %Rrc\n", rc));
1106
1107 RTPollSetDestroy(pThis->hPollSet);
1108 }
1109 else
1110 LogRel(("Creating pollset failed with %Rrc\n", rc));
1111
1112 RTCritSectDelete(&pThis->CritSectClients);
1113 }
1114 else
1115 LogRel(("Creating critical section failed with %Rrc\n", rc));
1116
1117 if (RT_FAILURE(rc))
1118 LogRel(("Creating server failed with %Rrc\n", rc));
1119
1120 LogRelFlowFuncLeaveRC(rc);
1121 return rc;
1122}
1123
1124/**
1125 * Handles a command line option.
1126 *
1127 * @returns VBox status code.
1128 * @param pThis The ATS instance to handle option for.
1129 * @param ch Option (short) to handle.
1130 * @param pVal Option union to store the result in on success.
1131 */
1132int AudioTestSvcHandleOption(PATSSERVER pThis, int ch, PCRTGETOPTUNION pVal)
1133{
1134 AssertPtrReturn(pThis->pTransport, VERR_WRONG_ORDER); /* Must be creatd first. */
1135 if (!pThis->pTransport->pfnOption)
1136 return VERR_GETOPT_UNKNOWN_OPTION;
1137 return pThis->pTransport->pfnOption(pThis->pTransportInst, ch, pVal);
1138}
1139
1140/**
1141 * Starts a formerly initialized ATS instance.
1142 *
1143 * @returns VBox status code.
1144 * @param pThis The ATS instance to start.
1145 */
1146int AudioTestSvcStart(PATSSERVER pThis)
1147{
1148 LogRelFlowFuncEnter();
1149
1150 /* Spin off the thread serving connections. */
1151 int rc = RTThreadCreate(&pThis->hThreadServing, atsClientWorker, pThis, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
1152 "ATSCLWORK");
1153 if (RT_FAILURE(rc))
1154 {
1155 LogRel(("Creating the client worker thread failed with %Rrc\n", rc));
1156 return rc;
1157 }
1158
1159 rc = pThis->pTransport->pfnStart(pThis->pTransportInst);
1160 if (RT_SUCCESS(rc))
1161 {
1162 /* Spin off the connection thread. */
1163 rc = RTThreadCreate(&pThis->hThreadMain, atsMainThread, pThis, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
1164 "ATSMAIN");
1165 if (RT_SUCCESS(rc))
1166 {
1167 rc = RTThreadUserWait(pThis->hThreadMain, RT_MS_30SEC);
1168 if (RT_SUCCESS(rc))
1169 pThis->fStarted = true;
1170 }
1171 }
1172
1173 LogRelFlowFuncLeaveRC(rc);
1174 return rc;
1175}
1176
1177/**
1178 * Stops (shuts down) a formerly started ATS instance.
1179 *
1180 * @returns VBox status code.
1181 * @param pThis The ATS instance.
1182 */
1183int AudioTestSvcStop(PATSSERVER pThis)
1184{
1185 if (!pThis->fStarted)
1186 return VINF_SUCCESS;
1187
1188 LogRelFlowFuncEnter();
1189
1190 ASMAtomicXchgBool(&pThis->fTerminate, true);
1191
1192 if (pThis->pTransport)
1193 pThis->pTransport->pfnTerm(pThis->pTransportInst);
1194
1195 size_t cbWritten;
1196 int rc = RTPipeWrite(pThis->hPipeW, "", 1, &cbWritten);
1197 AssertRCReturn(rc, rc);
1198
1199 /* First close serving thread. */
1200 int rcThread;
1201 rc = RTThreadWait(pThis->hThreadServing, RT_MS_30SEC, &rcThread);
1202 if (RT_SUCCESS(rc))
1203 {
1204 rc = rcThread;
1205 if (RT_SUCCESS(rc))
1206 {
1207 /* Close the main thread last. */
1208 rc = RTThreadWait(pThis->hThreadMain, RT_MS_30SEC, &rcThread);
1209 if (RT_SUCCESS(rc))
1210 rc = rcThread;
1211
1212 if (rc == VERR_TCP_SERVER_DESTROYED)
1213 rc = VINF_SUCCESS;
1214 }
1215 }
1216
1217 if (RT_SUCCESS(rc))
1218 pThis->fStarted = false;
1219
1220 LogRelFlowFuncLeaveRC(rc);
1221 return rc;
1222}
1223
1224/**
1225 * Destroys an ATS instance, internal version.
1226 *
1227 * @returns VBox status code.
1228 * @param pThis ATS instance to destroy.
1229 */
1230static int audioTestSvcDestroyInternal(PATSSERVER pThis)
1231{
1232 int rc = VINF_SUCCESS;
1233
1234 if (pThis->hPipeR != NIL_RTPIPE)
1235 {
1236 rc = RTPipeClose(pThis->hPipeR);
1237 AssertRCReturn(rc, rc);
1238 pThis->hPipeR = NIL_RTPIPE;
1239 }
1240
1241 if (pThis->hPipeW != NIL_RTPIPE)
1242 {
1243 rc = RTPipeClose(pThis->hPipeW);
1244 AssertRCReturn(rc, rc);
1245 pThis->hPipeW = NIL_RTPIPE;
1246 }
1247
1248 RTPollSetDestroy(pThis->hPollSet);
1249 pThis->hPollSet = NIL_RTPOLLSET;
1250
1251 PATSCLIENTINST pIt, pItNext;
1252 RTListForEachSafe(&pThis->LstClientsNew, pIt, pItNext, ATSCLIENTINST, NdLst)
1253 {
1254 RTListNodeRemove(&pIt->NdLst);
1255
1256 RTMemFree(pIt);
1257 pIt = NULL;
1258 }
1259
1260 if (RTCritSectIsInitialized(&pThis->CritSectClients))
1261 {
1262 rc = RTCritSectDelete(&pThis->CritSectClients);
1263 AssertRCReturn(rc, rc);
1264 }
1265
1266 return rc;
1267}
1268
1269/**
1270 * Destroys an ATS instance.
1271 *
1272 * @returns VBox status code.
1273 * @param pThis ATS instance to destroy.
1274 */
1275int AudioTestSvcDestroy(PATSSERVER pThis)
1276{
1277 LogRelFlowFuncEnter();
1278
1279 int rc = audioTestSvcDestroyInternal(pThis);
1280 if (RT_SUCCESS(rc))
1281 {
1282 if (pThis->pTransport)
1283 {
1284 if ( pThis->pTransport->pfnDestroy
1285 && pThis->pTransportInst)
1286 {
1287 pThis->pTransport->pfnDestroy(pThis->pTransportInst);
1288 pThis->pTransportInst = NULL;
1289 }
1290 }
1291 }
1292
1293 LogRelFlowFuncLeaveRC(rc);
1294 return rc;
1295}
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