VirtualBox

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

Last change on this file since 93628 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.8 KB
Line 
1/* $Id: AudioTestService.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * AudioTestService - Audio test execution server.
4 */
5
6/*
7 * Copyright (C) 2021-2022 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
359 ATSPKTREPFAIL Rep;
360 RT_ZERO(Rep);
361
362 size_t cchDetail = RTStrPrintfV(Rep.ach, sizeof(Rep.ach), pszDetailFmt, va);
363
364 Rep.rc = rcReq;
365
366 return atsReplyInternal(pThis, pInst, &Rep.Hdr, pszOpcode, sizeof(Rep.rc) + cchDetail + 1);
367}
368
369/**
370 * Replies with a failure.
371 *
372 * @returns IPRT status code of the send.
373 * @param pThis The ATS instance.
374 * @param pInst The opaque ATS instance structure.
375 * @param pPktHdr The original packet (for future use).
376 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
377 * with space.
378 * @param rcReq Status code.
379 * @param pszDetailFmt Longer description of the problem (format string).
380 * @param ... Format arguments.
381 */
382static int atsReplyFailure(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr,
383 const char *pszOpcode, int rcReq, const char *pszDetailFmt, ...)
384{
385 va_list va;
386 va_start(va, pszDetailFmt);
387 int rc = atsReplyFailureV(pThis, pInst, pPktHdr, pszOpcode, rcReq, pszDetailFmt, va);
388 va_end(va);
389 return rc;
390}
391
392/**
393 * Replies according to the return code.
394 *
395 * @returns IPRT status code of the send.
396 * @param pThis The ATS instance.
397 * @param pInst The opaque ATS instance structure.
398 * @param pPktHdr The packet to reply to.
399 * @param rcOperation The status code to report.
400 * @param pszOperationFmt The operation that failed. Typically giving the
401 * function call with important arguments.
402 * @param ... Arguments to the format string.
403 */
404static int atsReplyRC(PATSSERVER pThis,
405 PATSCLIENTINST pInst, PATSPKTHDR pPktHdr, int rcOperation, const char *pszOperationFmt, ...)
406{
407 if (RT_SUCCESS(rcOperation))
408 return atsReplyAck(pThis, pInst, pPktHdr);
409
410 char szOperation[128];
411 va_list va;
412 va_start(va, pszOperationFmt);
413 RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va);
414 va_end(va);
415
416 return atsReplyFailure(pThis, pInst, pPktHdr, "FAILED ", rcOperation, "%s failed with rc=%Rrc (opcode '%.8s')",
417 szOperation, rcOperation, pPktHdr->achOpcode);
418}
419
420/**
421 * Signal a bad packet exact size.
422 *
423 * @returns IPRT status code of the send.
424 * @param pThis The ATS instance.
425 * @param pInst The opaque ATS instance structure.
426 * @param pPktHdr The packet to reply to.
427 * @param cb The wanted size.
428 */
429static int atsReplyBadSize(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr, size_t cb)
430{
431 return atsReplyFailure(pThis, pInst, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at %zu bytes, got %u (opcode '%.8s')",
432 cb, pPktHdr->cb, pPktHdr->achOpcode);
433}
434
435/**
436 * Deals with a unknown command.
437 *
438 * @returns IPRT status code of the send.
439 * @param pThis The ATS instance.
440 * @param pInst The opaque ATS instance structure.
441 * @param pPktHdr The packet to reply to.
442 */
443static int atsReplyUnknown(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
444{
445 return atsReplyFailure(pThis, pInst, pPktHdr, "UNKNOWN ", VERR_NOT_FOUND, "Opcode '%.8s' is not known", pPktHdr->achOpcode);
446}
447
448/**
449 * Deals with a command sent in an invalid client state.
450 *
451 * @returns IPRT status code of the send.
452 * @param pThis The ATS instance.
453 * @param pInst The opaque ATS instance structure.
454 * @param pPktHdr The packet containing the unterminated string.
455 */
456static int atsReplyInvalidState(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
457{
458 return atsReplyFailure(pThis, pInst, pPktHdr, "INVSTATE", VERR_INVALID_STATE, "Opcode '%.8s' is not supported at client state '%s",
459 pPktHdr->achOpcode, atsClientStateStringify(pInst->enmState));
460}
461
462/**
463 * Verifies and acknowledges a "BYE" request.
464 *
465 * @returns IPRT status code.
466 * @param pThis The ATS instance.
467 * @param pInst The opaque ATS instance structure.
468 * @param pPktHdr The bye packet.
469 */
470static int atsDoBye(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
471{
472 int rc;
473 if (pPktHdr->cb == sizeof(ATSPKTHDR))
474 {
475 if (pThis->Callbacks.pfnBye)
476 {
477 rc = pThis->Callbacks.pfnBye(pThis->Callbacks.pvUser);
478 }
479 else
480 rc = VINF_SUCCESS;
481
482 if (RT_SUCCESS(rc))
483 {
484 rc = atsReplyAck(pThis, pInst, pPktHdr);
485 }
486 else
487 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Disconnecting client failed");
488 }
489 else
490 rc = atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTHDR));
491 return rc;
492}
493
494/**
495 * Verifies and acknowledges a "HOWDY" request.
496 *
497 * @returns IPRT status code.
498 * @param pThis The ATS instance.
499 * @param pInst The opaque ATS instance structure.
500 * @param pPktHdr The howdy packet.
501 */
502static int atsDoHowdy(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
503{
504 int rc = VINF_SUCCESS;
505
506 if (pPktHdr->cb != sizeof(ATSPKTREQHOWDY))
507 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQHOWDY));
508
509 if (pInst->enmState != ATSCLIENTSTATE_INITIALISING)
510 return atsReplyInvalidState(pThis, pInst, pPktHdr);
511
512 PATSPKTREQHOWDY pReq = (PATSPKTREQHOWDY)pPktHdr;
513
514 if (pReq->uVersion != ATS_PROTOCOL_VS)
515 return atsReplyRC(pThis, pInst, pPktHdr, VERR_VERSION_MISMATCH, "The given version %#x is not supported", pReq->uVersion);
516
517 ATSPKTREPHOWDY Rep;
518 RT_ZERO(Rep);
519
520 Rep.uVersion = ATS_PROTOCOL_VS;
521
522 rc = atsReplyInternal(pThis, pInst, &Rep.Hdr, "ACK ", sizeof(Rep) - sizeof(ATSPKTHDR));
523 if (RT_SUCCESS(rc))
524 {
525 pThis->pTransport->pfnNotifyHowdy(pThis->pTransportInst, pInst->pTransportClient);
526
527 if (pThis->Callbacks.pfnHowdy)
528 rc = pThis->Callbacks.pfnHowdy(pThis->Callbacks.pvUser);
529
530 if (RT_SUCCESS(rc))
531 pInst->enmState = ATSCLIENTSTATE_READY;
532 }
533
534 return rc;
535}
536
537/**
538 * Verifies and acknowledges a "TSET BEG" request.
539 *
540 * @returns IPRT status code.
541 * @param pThis The ATS instance.
542 * @param pInst The opaque ATS instance structure.
543 * @param pPktHdr The test set begin packet.
544 */
545static int atsDoTestSetBegin(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
546{
547 if (pPktHdr->cb != sizeof(ATSPKTREQTSETBEG))
548 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTSETBEG));
549
550 PATSPKTREQTSETBEG pReq = (PATSPKTREQTSETBEG)pPktHdr;
551
552 int rc = VINF_SUCCESS;
553
554 if (pThis->Callbacks.pfnTestSetBegin)
555 rc = pThis->Callbacks.pfnTestSetBegin(pThis->Callbacks.pvUser, pReq->szTag);
556
557 if (RT_SUCCESS(rc))
558 rc = atsReplyAck(pThis, pInst, pPktHdr);
559 else
560 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Beginning test set failed");
561 return rc;
562}
563
564/**
565 * Verifies and acknowledges a "TSET END" request.
566 *
567 * @returns IPRT status code.
568 * @param pThis The ATS instance.
569 * @param pInst The opaque ATS instance structure.
570 * @param pPktHdr The test set end packet.
571 */
572static int atsDoTestSetEnd(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
573{
574 if (pPktHdr->cb != sizeof(ATSPKTREQTSETEND))
575 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTSETEND));
576
577 PATSPKTREQTSETEND pReq = (PATSPKTREQTSETEND)pPktHdr;
578
579 int rc = VINF_SUCCESS;
580
581 if (pThis->Callbacks.pfnTestSetEnd)
582 rc = pThis->Callbacks.pfnTestSetEnd(pThis->Callbacks.pvUser, pReq->szTag);
583
584 if (RT_SUCCESS(rc))
585 rc = atsReplyAck(pThis, pInst, pPktHdr);
586 else
587 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Ending test set failed");
588 return rc;
589}
590
591/**
592 * Used by atsDoTestSetSend to wait for a reply ACK from the client.
593 *
594 * @returns VINF_SUCCESS on ACK, VERR_GENERAL_FAILURE on NACK,
595 * VERR_NET_NOT_CONNECTED on unknown response (sending a bable reply),
596 * or whatever atsRecvPkt returns.
597 * @param pThis The ATS instance.
598 * @param pInst The opaque ATS instance structure.
599 * @param pPktHdr The original packet (for future use).
600 */
601static int atsWaitForAck(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
602{
603 RT_NOREF(pPktHdr);
604 /** @todo timeout? */
605 PATSPKTHDR pReply;
606 int rc = atsRecvPkt(pThis, pInst, &pReply, false /*fAutoRetryOnFailure*/);
607 if (RT_SUCCESS(rc))
608 {
609 if (atsIsSameOpcode(pReply, "ACK"))
610 rc = VINF_SUCCESS;
611 else if (atsIsSameOpcode(pReply, "NACK"))
612 rc = VERR_GENERAL_FAILURE;
613 else
614 {
615 atsReplyBabble(pThis, pInst, "BABBLE ");
616 rc = VERR_NET_NOT_CONNECTED;
617 }
618 RTMemFree(pReply);
619 }
620 return rc;
621}
622
623/**
624 * Verifies and acknowledges a "TSET SND" request.
625 *
626 * @returns IPRT status code.
627 * @param pThis The ATS instance.
628 * @param pInst The opaque ATS instance structure.
629 * @param pPktHdr The test set end packet.
630 */
631static int atsDoTestSetSend(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
632{
633 if (pPktHdr->cb != sizeof(ATSPKTREQTSETSND))
634 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTSETSND));
635
636 PATSPKTREQTSETSND pReq = (PATSPKTREQTSETSND)pPktHdr;
637
638 int rc = VINF_SUCCESS;
639
640 if (!pThis->Callbacks.pfnTestSetSendRead)
641 return atsReplyRC(pThis, pInst, pPktHdr, VERR_NOT_SUPPORTED, "Sending test set not implemented");
642
643 if (pThis->Callbacks.pfnTestSetSendBegin)
644 {
645 rc = pThis->Callbacks.pfnTestSetSendBegin(pThis->Callbacks.pvUser, pReq->szTag);
646 if (RT_FAILURE(rc))
647 return atsReplyRC(pThis, pInst, pPktHdr, rc, "Beginning sending test set '%s' failed", pReq->szTag);
648 }
649
650 for (;;)
651 {
652 uint32_t uMyCrc32 = RTCrc32Start();
653 struct
654 {
655 ATSPKTHDR Hdr;
656 uint32_t uCrc32;
657 char ab[_64K];
658 char abPadding[ATSPKT_ALIGNMENT];
659 } Pkt;
660#ifdef DEBUG
661 RT_ZERO(Pkt);
662#endif
663 size_t cbRead = 0;
664 rc = pThis->Callbacks.pfnTestSetSendRead(pThis->Callbacks.pvUser, pReq->szTag, &Pkt.ab, sizeof(Pkt.ab), &cbRead);
665 if ( RT_FAILURE(rc)
666 || cbRead == 0)
667 {
668 if ( rc == VERR_EOF
669 || (RT_SUCCESS(rc) && cbRead == 0))
670 {
671 Pkt.uCrc32 = RTCrc32Finish(uMyCrc32);
672 rc = atsReplyInternal(pThis, pInst, &Pkt.Hdr, "DATA EOF", sizeof(uint32_t) /* uCrc32 */);
673 if (RT_SUCCESS(rc))
674 rc = atsWaitForAck(pThis, pInst, &Pkt.Hdr);
675 }
676 else
677 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Sending data for test set '%s' failed", pReq->szTag);
678 break;
679 }
680
681 uMyCrc32 = RTCrc32Process(uMyCrc32, &Pkt.ab[0], cbRead);
682 Pkt.uCrc32 = RTCrc32Finish(uMyCrc32);
683
684 Log2Func(("cbRead=%zu -> uCrc32=%#x\n", cbRead, Pkt.uCrc32));
685
686 Assert(cbRead <= sizeof(Pkt.ab));
687
688 rc = atsReplyInternal(pThis, pInst, &Pkt.Hdr, "DATA ", sizeof(uint32_t) /* uCrc32 */ + cbRead);
689 if (RT_FAILURE(rc))
690 break;
691
692 rc = atsWaitForAck(pThis, pInst, &Pkt.Hdr);
693 if (RT_FAILURE(rc))
694 break;
695 }
696
697 if (pThis->Callbacks.pfnTestSetSendEnd)
698 {
699 int rc2 = pThis->Callbacks.pfnTestSetSendEnd(pThis->Callbacks.pvUser, pReq->szTag);
700 if (RT_FAILURE(rc2))
701 return atsReplyRC(pThis, pInst, pPktHdr, rc2, "Ending sending test set '%s' failed", pReq->szTag);
702 }
703
704 return rc;
705}
706
707/**
708 * Verifies and processes a "TN PLY" request.
709 *
710 * @returns IPRT status code.
711 * @param pThis The ATS instance.
712 * @param pInst The opaque ATS instance structure.
713 * @param pPktHdr The packet header.
714 */
715static int atsDoTonePlay(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
716{
717 if (pPktHdr->cb < sizeof(ATSPKTREQTONEPLAY))
718 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTONEPLAY));
719
720 if (pInst->enmState != ATSCLIENTSTATE_READY)
721 return atsReplyInvalidState(pThis, pInst, pPktHdr);
722
723 int rc = VINF_SUCCESS;
724
725 PATSPKTREQTONEPLAY pReq = (PATSPKTREQTONEPLAY)pPktHdr;
726
727 if (pThis->Callbacks.pfnTonePlay)
728 rc = pThis->Callbacks.pfnTonePlay(pThis->Callbacks.pvUser, &pReq->ToneParms);
729
730 if (RT_SUCCESS(rc))
731 rc = atsReplyAck(pThis, pInst, pPktHdr);
732 else
733 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Playing test tone failed");
734 return rc;
735}
736
737/**
738 * Verifies and processes a "TN REC" request.
739 *
740 * @returns IPRT status code.
741 * @param pThis The ATS instance.
742 * @param pInst The opaque ATS instance structure.
743 * @param pPktHdr The packet header.
744 */
745static int atsDoToneRecord(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
746{
747 if (pPktHdr->cb < sizeof(ATSPKTREQTONEREC))
748 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTONEREC));
749
750 if (pInst->enmState != ATSCLIENTSTATE_READY)
751 return atsReplyInvalidState(pThis, pInst, pPktHdr);
752
753 int rc = VINF_SUCCESS;
754
755 PATSPKTREQTONEREC pReq = (PATSPKTREQTONEREC)pPktHdr;
756
757 if (pThis->Callbacks.pfnToneRecord)
758 rc = pThis->Callbacks.pfnToneRecord(pThis->Callbacks.pvUser, &pReq->ToneParms);
759
760 if (RT_SUCCESS(rc))
761 rc = atsReplyAck(pThis, pInst, pPktHdr);
762 else
763 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Recording test tone failed");
764 return rc;
765}
766
767/**
768 * Main request processing routine for each client.
769 *
770 * @returns IPRT status code.
771 * @param pThis The ATS instance.
772 * @param pInst The ATS client structure sending the request.
773 * @param pfDisconnect Where to return whether to disconnect the client on success or not.
774 */
775static int atsClientReqProcess(PATSSERVER pThis, PATSCLIENTINST pInst, bool *pfDisconnect)
776{
777 LogRelFlowFuncEnter();
778
779 /*
780 * Read client command packet and process it.
781 */
782 PATSPKTHDR pPktHdr = NULL;
783 int rc = atsRecvPkt(pThis, pInst, &pPktHdr, true /*fAutoRetryOnFailure*/);
784 if (RT_FAILURE(rc))
785 return rc;
786
787 /*
788 * Do a string switch on the opcode bit.
789 */
790 /* Connection: */
791 if ( atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_HOWDY))
792 rc = atsDoHowdy(pThis, pInst, pPktHdr);
793 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_BYE))
794 {
795 rc = atsDoBye(pThis, pInst, pPktHdr);
796 if (RT_SUCCESS(rc))
797 *pfDisconnect = true;
798 }
799 /* Test set handling: */
800 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TESTSET_BEGIN))
801 rc = atsDoTestSetBegin(pThis, pInst, pPktHdr);
802 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TESTSET_END))
803 rc = atsDoTestSetEnd(pThis, pInst, pPktHdr);
804 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TESTSET_SEND))
805 rc = atsDoTestSetSend(pThis, pInst, pPktHdr);
806 /* Audio testing: */
807 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TONE_PLAY))
808 rc = atsDoTonePlay(pThis, pInst, pPktHdr);
809 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TONE_RECORD))
810 rc = atsDoToneRecord(pThis, pInst, pPktHdr);
811 /* Misc: */
812 else
813 rc = atsReplyUnknown(pThis, pInst, pPktHdr);
814
815 RTMemFree(pPktHdr);
816
817 LogRelFlowFuncLeaveRC(rc);
818 return rc;
819}
820
821/**
822 * Disconnects a client.
823 *
824 * @returns VBox status code.
825 * @param pThis The ATS instance.
826 * @param pInst The ATS client to disconnect.
827 */
828static int atsClientDisconnect(PATSSERVER pThis, PATSCLIENTINST pInst)
829{
830 AssertReturn(pInst->enmState != ATSCLIENTSTATE_DESTROYING, VERR_WRONG_ORDER);
831
832 pInst->enmState = ATSCLIENTSTATE_DESTROYING;
833
834 if ( pThis->pTransportInst
835 && pInst->pTransportClient)
836 {
837 if (pThis->pTransport->pfnNotifyBye)
838 pThis->pTransport->pfnNotifyBye(pThis->pTransportInst, pInst->pTransportClient);
839
840 pThis->pTransport->pfnDisconnect(pThis->pTransportInst, pInst->pTransportClient);
841 /* Pointer is now invalid due to the call above. */
842 pInst->pTransportClient = NULL;
843 }
844
845 return VINF_SUCCESS;
846}
847
848/**
849 * Free's (destroys) a client instance.
850 *
851 * @returns nothing.
852 * @param pInst The opaque ATS instance structure.
853 */
854static void atsClientFree(PATSCLIENTINST pInst)
855{
856 if (!pInst)
857 return;
858
859 /* Make sure that there is no transport client associated with it anymore. */
860 AssertReturnVoid(pInst->enmState == ATSCLIENTSTATE_DESTROYING);
861 AssertReturnVoid(pInst->pTransportClient == NULL);
862
863 if (pInst->pszHostname)
864 {
865 RTStrFree(pInst->pszHostname);
866 pInst->pszHostname = NULL;
867 }
868
869 RTMemFree(pInst);
870 pInst = NULL;
871}
872
873/**
874 * The main thread worker serving the clients.
875 */
876static DECLCALLBACK(int) atsClientWorker(RTTHREAD hThread, void *pvUser)
877{
878 RT_NOREF(hThread);
879
880 PATSSERVER pThis = (PATSSERVER)pvUser;
881 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
882
883 unsigned cClientsMax = 0;
884 unsigned cClientsCur = 0;
885 PATSCLIENTINST *papInsts = NULL;
886
887 /* Add the pipe to the poll set. */
888 int rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 0);
889 if (RT_SUCCESS(rc))
890 {
891 while (!pThis->fTerminate)
892 {
893 uint32_t fEvts;
894 uint32_t uId;
895 rc = RTPoll(pThis->hPollSet, RT_INDEFINITE_WAIT, &fEvts, &uId);
896 LogRelFlowFunc(("RTPoll(...) returned fEvts=#%x, uId=%RU32 -> %Rrc\n", fEvts, uId, rc));
897 if (RT_SUCCESS(rc))
898 {
899 if (uId == 0)
900 {
901 if (fEvts & RTPOLL_EVT_ERROR)
902 break;
903
904 /* We got woken up because of a new client. */
905 Assert(fEvts & RTPOLL_EVT_READ);
906
907 uint8_t bRead;
908 size_t cbRead = 0;
909 rc = RTPipeRead(pThis->hPipeR, &bRead, 1, &cbRead);
910 AssertRC(rc);
911
912 RTCritSectEnter(&pThis->CritSectClients);
913 /* Walk the list and add all new clients. */
914 PATSCLIENTINST pIt, pItNext;
915 RTListForEachSafe(&pThis->LstClientsNew, pIt, pItNext, ATSCLIENTINST, NdLst)
916 {
917 RTListNodeRemove(&pIt->NdLst);
918 Assert(cClientsCur <= cClientsMax);
919 if (cClientsCur == cClientsMax)
920 {
921 /* Realloc to accommodate for the new clients. */
922 PATSCLIENTINST *papInstsNew = (PATSCLIENTINST *)RTMemReallocZ(papInsts, cClientsMax * sizeof(PATSCLIENTINST), (cClientsMax + 10) * sizeof(PATSCLIENTINST));
923 if (RT_LIKELY(papInstsNew))
924 {
925 cClientsMax += 10;
926 papInsts = papInstsNew;
927 }
928 }
929 if (cClientsCur < cClientsMax)
930 {
931 /* Find a free slot in the client array. */
932 unsigned idxSlt = 0;
933 while ( idxSlt < cClientsMax
934 && papInsts[idxSlt] != NULL)
935 idxSlt++;
936
937 rc = pThis->pTransport->pfnPollSetAdd(pThis->pTransportInst, pThis->hPollSet, pIt->pTransportClient, idxSlt + 1);
938 if (RT_SUCCESS(rc))
939 {
940 cClientsCur++;
941 papInsts[idxSlt] = pIt;
942 }
943 else
944 {
945 atsClientDisconnect(pThis, pIt);
946 atsClientFree(pIt);
947 pIt = NULL;
948 }
949 }
950 else
951 {
952 atsClientDisconnect(pThis, pIt);
953 atsClientFree(pIt);
954 pIt = NULL;
955 }
956 }
957 RTCritSectLeave(&pThis->CritSectClients);
958 }
959 else
960 {
961 bool fDisconnect = false;
962
963 /* Client sends a request, pick the right client and process it. */
964 PATSCLIENTINST pInst = papInsts[uId - 1];
965 AssertPtr(pInst);
966 if (fEvts & RTPOLL_EVT_READ)
967 rc = atsClientReqProcess(pThis, pInst, &fDisconnect);
968
969 if ( (fEvts & RTPOLL_EVT_ERROR)
970 || RT_FAILURE(rc)
971 || fDisconnect)
972 {
973 /* Close connection and remove client from array. */
974 int rc2 = pThis->pTransport->pfnPollSetRemove(pThis->pTransportInst, pThis->hPollSet, pInst->pTransportClient, uId);
975 AssertRC(rc2);
976
977 atsClientDisconnect(pThis, pInst);
978 atsClientFree(pInst);
979 pInst = NULL;
980
981 papInsts[uId - 1] = NULL;
982 Assert(cClientsCur);
983 cClientsCur--;
984 }
985 }
986 }
987 }
988 }
989
990 if (papInsts)
991 {
992 for (size_t i = 0; i < cClientsMax; i++)
993 RTMemFree(papInsts[i]);
994 RTMemFree(papInsts);
995 }
996
997 return rc;
998}
999
1000/**
1001 * The main thread waiting for new client connections.
1002 *
1003 * @returns VBox status code.
1004 */
1005static DECLCALLBACK(int) atsMainThread(RTTHREAD hThread, void *pvUser)
1006{
1007 RT_NOREF(hThread);
1008
1009 LogRelFlowFuncEnter();
1010
1011 PATSSERVER pThis = (PATSSERVER)pvUser;
1012 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1013
1014 int rc = RTThreadUserSignal(hThread);
1015 AssertRCReturn(rc, rc);
1016
1017 while (!pThis->fTerminate)
1018 {
1019 /*
1020 * Wait for new connection and spin off a new thread
1021 * for every new client.
1022 */
1023 bool fFromServer;
1024 PATSTRANSPORTCLIENT pTransportClient;
1025 rc = pThis->pTransport->pfnWaitForConnect(pThis->pTransportInst, 1000 /* msTimeout */, &fFromServer, &pTransportClient);
1026 if (RT_FAILURE(rc))
1027 continue;
1028
1029 /*
1030 * New connection, create new client structure and spin off
1031 * the request handling thread.
1032 */
1033 PATSCLIENTINST pInst = (PATSCLIENTINST)RTMemAllocZ(sizeof(ATSCLIENTINST));
1034 if (RT_LIKELY(pInst))
1035 {
1036 pInst->enmState = ATSCLIENTSTATE_INITIALISING;
1037 pInst->pTransportClient = pTransportClient;
1038 pInst->pszHostname = NULL;
1039
1040 /* Add client to the new list and inform the worker thread. */
1041 RTCritSectEnter(&pThis->CritSectClients);
1042 RTListAppend(&pThis->LstClientsNew, &pInst->NdLst);
1043 RTCritSectLeave(&pThis->CritSectClients);
1044
1045 size_t cbWritten = 0;
1046 rc = RTPipeWrite(pThis->hPipeW, "", 1, &cbWritten);
1047 if (RT_FAILURE(rc))
1048 LogRelFunc(("Failed to inform worker thread of a new client, rc=%Rrc\n", rc));
1049 }
1050 else
1051 {
1052 LogRelFunc(("Creating new client structure failed with out of memory error\n"));
1053 pThis->pTransport->pfnNotifyBye(pThis->pTransportInst, pTransportClient);
1054 rc = VERR_NO_MEMORY;
1055 break; /* This is fatal, break out of the loop. */
1056 }
1057
1058 if (RT_SUCCESS(rc))
1059 {
1060 LogRelFunc(("New connection established (%s)\n", fFromServer ? "from server" : "as client"));
1061
1062 /**
1063 * If the new client is not from our server but from a remote server (also called a reverse connection),
1064 * exit this loop and stop trying to connect to the remote server.
1065 *
1066 * Otherwise we would connect lots and lots of clients without any real use.
1067 *
1068 ** @todo Improve this handling -- there might be a better / more elegant solution.
1069 */
1070 if (!fFromServer)
1071 break;
1072 }
1073 }
1074
1075 LogRelFlowFuncLeaveRC(rc);
1076 return rc;
1077}
1078
1079/**
1080 * Initializes an ATS instance.
1081 *
1082 * @note This does *not* start the server!
1083 *
1084 * @returns VBox status code.
1085 * @param pThis The ATS instance.
1086 * @param pCallbacks The callbacks table to use.
1087 */
1088int AudioTestSvcInit(PATSSERVER pThis, PCATSCALLBACKS pCallbacks)
1089{
1090 LogRelFlowFuncEnter();
1091
1092 RT_BZERO(pThis, sizeof(ATSSERVER));
1093
1094 pThis->hPipeR = NIL_RTPIPE;
1095 pThis->hPipeW = NIL_RTPIPE;
1096
1097 RTListInit(&pThis->LstClientsNew);
1098
1099 /* Copy callback table. */
1100 memcpy(&pThis->Callbacks, pCallbacks, sizeof(ATSCALLBACKS));
1101
1102 int rc = RTCritSectInit(&pThis->CritSectClients);
1103 if (RT_SUCCESS(rc))
1104 {
1105 rc = RTPollSetCreate(&pThis->hPollSet);
1106 if (RT_SUCCESS(rc))
1107 {
1108 rc = RTPipeCreate(&pThis->hPipeR, &pThis->hPipeW, 0);
1109 if (RT_SUCCESS(rc))
1110 {
1111 /*
1112 * The default transporter is the first one.
1113 */
1114 pThis->pTransport = g_apTransports[0]; /** @todo Make this dynamic. */
1115
1116 rc = pThis->pTransport->pfnCreate(&pThis->pTransportInst);
1117 if (RT_SUCCESS(rc))
1118 return VINF_SUCCESS;
1119
1120 RTPipeClose(pThis->hPipeR);
1121 RTPipeClose(pThis->hPipeW);
1122 }
1123 else
1124 LogRel(("Creating communications pipe failed with %Rrc\n", rc));
1125
1126 RTPollSetDestroy(pThis->hPollSet);
1127 }
1128 else
1129 LogRel(("Creating pollset failed with %Rrc\n", rc));
1130
1131 RTCritSectDelete(&pThis->CritSectClients);
1132 }
1133 else
1134 LogRel(("Creating critical section failed with %Rrc\n", rc));
1135
1136 if (RT_FAILURE(rc))
1137 LogRel(("Creating server failed with %Rrc\n", rc));
1138
1139 LogRelFlowFuncLeaveRC(rc);
1140 return rc;
1141}
1142
1143/**
1144 * Handles a command line option.
1145 *
1146 * @returns VBox status code.
1147 * @param pThis The ATS instance to handle option for.
1148 * @param ch Option (short) to handle.
1149 * @param pVal Option union to store the result in on success.
1150 */
1151int AudioTestSvcHandleOption(PATSSERVER pThis, int ch, PCRTGETOPTUNION pVal)
1152{
1153 AssertPtrReturn(pThis->pTransport, VERR_WRONG_ORDER); /* Must be creatd first. */
1154 if (!pThis->pTransport->pfnOption)
1155 return VERR_GETOPT_UNKNOWN_OPTION;
1156 return pThis->pTransport->pfnOption(pThis->pTransportInst, ch, pVal);
1157}
1158
1159/**
1160 * Starts a formerly initialized ATS instance.
1161 *
1162 * @returns VBox status code.
1163 * @param pThis The ATS instance to start.
1164 */
1165int AudioTestSvcStart(PATSSERVER pThis)
1166{
1167 LogRelFlowFuncEnter();
1168
1169 /* Spin off the thread serving connections. */
1170 int rc = RTThreadCreate(&pThis->hThreadServing, atsClientWorker, pThis, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
1171 "ATSCLWORK");
1172 if (RT_FAILURE(rc))
1173 {
1174 LogRel(("Creating the client worker thread failed with %Rrc\n", rc));
1175 return rc;
1176 }
1177
1178 rc = pThis->pTransport->pfnStart(pThis->pTransportInst);
1179 if (RT_SUCCESS(rc))
1180 {
1181 /* Spin off the connection thread. */
1182 rc = RTThreadCreate(&pThis->hThreadMain, atsMainThread, pThis, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
1183 "ATSMAIN");
1184 if (RT_SUCCESS(rc))
1185 {
1186 rc = RTThreadUserWait(pThis->hThreadMain, RT_MS_30SEC);
1187 if (RT_SUCCESS(rc))
1188 pThis->fStarted = true;
1189 }
1190 }
1191
1192 LogRelFlowFuncLeaveRC(rc);
1193 return rc;
1194}
1195
1196/**
1197 * Stops (shuts down) a formerly started ATS instance.
1198 *
1199 * @returns VBox status code.
1200 * @param pThis The ATS instance.
1201 */
1202int AudioTestSvcStop(PATSSERVER pThis)
1203{
1204 if (!pThis->fStarted)
1205 return VINF_SUCCESS;
1206
1207 LogRelFlowFuncEnter();
1208
1209 ASMAtomicXchgBool(&pThis->fTerminate, true);
1210
1211 if (pThis->pTransport)
1212 pThis->pTransport->pfnStop(pThis->pTransportInst);
1213
1214 size_t cbWritten;
1215 int rc = RTPipeWrite(pThis->hPipeW, "", 1, &cbWritten);
1216 AssertRCReturn(rc, rc);
1217
1218 /* First close serving thread. */
1219 int rcThread;
1220 rc = RTThreadWait(pThis->hThreadServing, RT_MS_30SEC, &rcThread);
1221 if (RT_SUCCESS(rc))
1222 {
1223 rc = rcThread;
1224 if (RT_SUCCESS(rc))
1225 {
1226 /* Close the main thread last. */
1227 rc = RTThreadWait(pThis->hThreadMain, RT_MS_30SEC, &rcThread);
1228 if (RT_SUCCESS(rc))
1229 rc = rcThread;
1230
1231 if (rc == VERR_TCP_SERVER_DESTROYED)
1232 rc = VINF_SUCCESS;
1233 }
1234 }
1235
1236 if (RT_SUCCESS(rc))
1237 pThis->fStarted = false;
1238
1239 LogRelFlowFuncLeaveRC(rc);
1240 return rc;
1241}
1242
1243/**
1244 * Destroys an ATS instance, internal version.
1245 *
1246 * @returns VBox status code.
1247 * @param pThis ATS instance to destroy.
1248 */
1249static int audioTestSvcDestroyInternal(PATSSERVER pThis)
1250{
1251 int rc = VINF_SUCCESS;
1252
1253 if (pThis->hPipeR != NIL_RTPIPE)
1254 {
1255 rc = RTPipeClose(pThis->hPipeR);
1256 AssertRCReturn(rc, rc);
1257 pThis->hPipeR = NIL_RTPIPE;
1258 }
1259
1260 if (pThis->hPipeW != NIL_RTPIPE)
1261 {
1262 rc = RTPipeClose(pThis->hPipeW);
1263 AssertRCReturn(rc, rc);
1264 pThis->hPipeW = NIL_RTPIPE;
1265 }
1266
1267 RTPollSetDestroy(pThis->hPollSet);
1268 pThis->hPollSet = NIL_RTPOLLSET;
1269
1270 PATSCLIENTINST pIt, pItNext;
1271 RTListForEachSafe(&pThis->LstClientsNew, pIt, pItNext, ATSCLIENTINST, NdLst)
1272 {
1273 RTListNodeRemove(&pIt->NdLst);
1274 atsClientDisconnect(pThis, pIt);
1275 atsClientFree(pIt);
1276 }
1277
1278 if (RTCritSectIsInitialized(&pThis->CritSectClients))
1279 {
1280 rc = RTCritSectDelete(&pThis->CritSectClients);
1281 AssertRCReturn(rc, rc);
1282 }
1283
1284 return rc;
1285}
1286
1287/**
1288 * Destroys an ATS instance.
1289 *
1290 * @returns VBox status code.
1291 * @param pThis ATS instance to destroy.
1292 */
1293int AudioTestSvcDestroy(PATSSERVER pThis)
1294{
1295 LogRelFlowFuncEnter();
1296
1297 int rc = audioTestSvcDestroyInternal(pThis);
1298 if (RT_SUCCESS(rc))
1299 {
1300 if (pThis->pTransport)
1301 {
1302 if ( pThis->pTransport->pfnDestroy
1303 && pThis->pTransportInst)
1304 {
1305 pThis->pTransport->pfnDestroy(pThis->pTransportInst);
1306 pThis->pTransportInst = NULL;
1307 }
1308 }
1309 }
1310
1311 LogRelFlowFuncLeaveRC(rc);
1312 return rc;
1313}
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