VirtualBox

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

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

Audio/ValKit: Started working on the audio test execution service (ATS). bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.9 KB
Line 
1/* $Id: AudioTestService.cpp 89174 2021-05-19 15:05:29Z 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_DEFAULT
32#include <iprt/alloca.h>
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#include <iprt/critsect.h>
36#include <iprt/crc.h>
37#include <iprt/ctype.h>
38#include <iprt/dir.h>
39#include <iprt/env.h>
40#include <iprt/err.h>
41#include <iprt/getopt.h>
42#include <iprt/handle.h>
43#include <iprt/initterm.h>
44#include <iprt/json.h>
45#include <iprt/list.h>
46#include <iprt/log.h>
47#include <iprt/mem.h>
48#include <iprt/message.h>
49#include <iprt/param.h>
50#include <iprt/path.h>
51#include <iprt/pipe.h>
52#include <iprt/poll.h>
53#include <iprt/process.h>
54#include <iprt/stream.h>
55#include <iprt/string.h>
56#include <iprt/thread.h>
57
58#include "AudioTestService.h"
59#include "AudioTestServiceInternal.h"
60
61
62/*********************************************************************************************************************************
63* Structures and Typedefs *
64*********************************************************************************************************************************/
65
66
67/*********************************************************************************************************************************
68* Global Variables *
69*********************************************************************************************************************************/
70/**
71 * Transport layers.
72 */
73static const PCATSTRANSPORT g_apTransports[] =
74{
75 &g_TcpTransport
76};
77
78/** The select transport layer. */
79static PCATSTRANSPORT g_pTransport;
80/** Whether to terminate or not.
81 * @todo implement signals and stuff. */
82static bool volatile g_fTerminate = false;
83/** Pipe for communicating with the serving thread about new clients. - read end */
84static RTPIPE g_hPipeR;
85/** Pipe for communicating with the serving thread about new clients. - write end */
86static RTPIPE g_hPipeW;
87/** Main thread waiting for connections. */
88static RTTHREAD g_hThreadMain;
89/** Thread serving connected clients. */
90static RTTHREAD g_hThreadServing;
91/** Critical section protecting the list of new clients. */
92static RTCRITSECT g_CritSectClients;
93/** List of new clients waiting to be picked up by the client worker thread. */
94static RTLISTANCHOR g_LstClientsNew;
95
96
97/**
98 * ATS client state.
99 */
100typedef enum ATSCLIENTSTATE
101{
102 /** Invalid client state. */
103 ATSCLIENTSTATE_INVALID = 0,
104 /** Client is initialising, only the HOWDY and BYE packets are allowed. */
105 ATSCLIENTSTATE_INITIALISING,
106 /** Client is in fully cuntional state and ready to process all requests. */
107 ATSCLIENTSTATE_READY,
108 /** Client is destroying. */
109 ATSCLIENTSTATE_DESTROYING,
110 /** 32bit hack. */
111 ATSCLIENTSTATE_32BIT_HACK = 0x7fffffff
112} ATSCLIENTSTATE;
113
114/**
115 * ATS client instance.
116 */
117typedef struct ATSCLIENT
118{
119 /** List node for new clients. */
120 RTLISTNODE NdLst;
121 /** The current client state. */
122 ATSCLIENTSTATE enmState;
123 /** Transport backend specific data. */
124 PATSTRANSPORTCLIENT pTransportClient;
125 /** Client hostname. */
126 char *pszHostname;
127} ATSCLIENT;
128/** Pointer to a ATS client instance. */
129typedef ATSCLIENT *PATSCLIENT;
130
131/**
132 * Returns the string represenation of the given state.
133 */
134static const char *atsClientStateStringify(ATSCLIENTSTATE enmState)
135{
136 switch (enmState)
137 {
138 case ATSCLIENTSTATE_INVALID:
139 return "INVALID";
140 case ATSCLIENTSTATE_INITIALISING:
141 return "INITIALISING";
142 case ATSCLIENTSTATE_READY:
143 return "READY";
144 case ATSCLIENTSTATE_DESTROYING:
145 return "DESTROYING";
146 case ATSCLIENTSTATE_32BIT_HACK:
147 default:
148 break;
149 }
150
151 AssertMsgFailed(("Unknown state %#x\n", enmState));
152 return "UNKNOWN";
153}
154
155/**
156 * Calculates the checksum value, zero any padding space and send the packet.
157 *
158 * @returns IPRT status code.
159 * @param pClient The ATS client structure.
160 * @param pPkt The packet to send. Must point to a correctly
161 * aligned buffer.
162 */
163static int atsSendPkt(PATSCLIENT pClient, PATSPKTHDR pPkt)
164{
165 Assert(pPkt->cb >= sizeof(*pPkt));
166 pPkt->uCrc32 = RTCrc32(pPkt->achOpcode, pPkt->cb - RT_UOFFSETOF(ATSPKTHDR, achOpcode));
167 if (pPkt->cb != RT_ALIGN_32(pPkt->cb, ATSPKT_ALIGNMENT))
168 memset((uint8_t *)pPkt + pPkt->cb, '\0', RT_ALIGN_32(pPkt->cb, ATSPKT_ALIGNMENT) - pPkt->cb);
169
170 Log(("atsSendPkt: cb=%#x opcode=%.8s\n", pPkt->cb, pPkt->achOpcode));
171 Log2(("%.*Rhxd\n", RT_MIN(pPkt->cb, 256), pPkt));
172 int rc = g_pTransport->pfnSendPkt(pClient->pTransportClient, pPkt);
173 while (RT_UNLIKELY(rc == VERR_INTERRUPTED) && !g_fTerminate)
174 rc = g_pTransport->pfnSendPkt(pClient->pTransportClient, pPkt);
175 if (RT_FAILURE(rc))
176 Log(("atsSendPkt: rc=%Rrc\n", rc));
177
178 return rc;
179}
180
181/**
182 * Sends a babble reply and disconnects the client (if applicable).
183 *
184 * @param pClient The ATS client structure.
185 * @param pszOpcode The BABBLE opcode.
186 */
187static void atsReplyBabble(PATSCLIENT pClient, const char *pszOpcode)
188{
189 ATSPKTHDR Reply;
190 Reply.cb = sizeof(Reply);
191 Reply.uCrc32 = 0;
192 memcpy(Reply.achOpcode, pszOpcode, sizeof(Reply.achOpcode));
193
194 g_pTransport->pfnBabble(pClient->pTransportClient, &Reply, 20*1000);
195}
196
197/**
198 * Receive and validate a packet.
199 *
200 * Will send bable responses to malformed packets that results in a error status
201 * code.
202 *
203 * @returns IPRT status code.
204 * @param pClient The ATS client structure.
205 * @param ppPktHdr Where to return the packet on success. Free
206 * with RTMemFree.
207 * @param fAutoRetryOnFailure Whether to retry on error.
208 */
209static int atsRecvPkt(PATSCLIENT pClient, PPATSPKTHDR ppPktHdr, bool fAutoRetryOnFailure)
210{
211 for (;;)
212 {
213 PATSPKTHDR pPktHdr;
214 int rc = g_pTransport->pfnRecvPkt(pClient->pTransportClient, &pPktHdr);
215 if (RT_SUCCESS(rc))
216 {
217 /* validate the packet. */
218 if ( pPktHdr->cb >= sizeof(ATSPKTHDR)
219 && pPktHdr->cb < ATSPKT_MAX_SIZE)
220 {
221 Log2(("atsRecvPkt: pPktHdr=%p cb=%#x crc32=%#x opcode=%.8s\n"
222 "%.*Rhxd\n",
223 pPktHdr, pPktHdr->cb, pPktHdr->uCrc32, pPktHdr->achOpcode, RT_MIN(pPktHdr->cb, 256), pPktHdr));
224 uint32_t uCrc32Calc = pPktHdr->uCrc32 != 0
225 ? RTCrc32(&pPktHdr->achOpcode[0], pPktHdr->cb - RT_UOFFSETOF(ATSPKTHDR, achOpcode))
226 : 0;
227 if (pPktHdr->uCrc32 == uCrc32Calc)
228 {
229 AssertCompileMemberSize(ATSPKTHDR, achOpcode, 8);
230 if ( RT_C_IS_UPPER(pPktHdr->achOpcode[0])
231 && RT_C_IS_UPPER(pPktHdr->achOpcode[1])
232 && (RT_C_IS_UPPER(pPktHdr->achOpcode[2]) || pPktHdr->achOpcode[2] == ' ')
233 && (RT_C_IS_PRINT(pPktHdr->achOpcode[3]) || pPktHdr->achOpcode[3] == ' ')
234 && (RT_C_IS_PRINT(pPktHdr->achOpcode[4]) || pPktHdr->achOpcode[4] == ' ')
235 && (RT_C_IS_PRINT(pPktHdr->achOpcode[5]) || pPktHdr->achOpcode[5] == ' ')
236 && (RT_C_IS_PRINT(pPktHdr->achOpcode[6]) || pPktHdr->achOpcode[6] == ' ')
237 && (RT_C_IS_PRINT(pPktHdr->achOpcode[7]) || pPktHdr->achOpcode[7] == ' ')
238 )
239 {
240 Log(("atsRecvPkt: cb=%#x opcode=%.8s\n", pPktHdr->cb, pPktHdr->achOpcode));
241 *ppPktHdr = pPktHdr;
242 return rc;
243 }
244
245 rc = VERR_IO_BAD_COMMAND;
246 }
247 else
248 {
249 Log(("atsRecvPkt: cb=%#x opcode=%.8s crc32=%#x actual=%#x\n",
250 pPktHdr->cb, pPktHdr->achOpcode, pPktHdr->uCrc32, uCrc32Calc));
251 rc = VERR_IO_CRC;
252 }
253 }
254 else
255 rc = VERR_IO_BAD_LENGTH;
256
257 /* Send babble reply and disconnect the client if the transport is
258 connection oriented. */
259 if (rc == VERR_IO_BAD_LENGTH)
260 atsReplyBabble(pClient, "BABBLE L");
261 else if (rc == VERR_IO_CRC)
262 atsReplyBabble(pClient, "BABBLE C");
263 else if (rc == VERR_IO_BAD_COMMAND)
264 atsReplyBabble(pClient, "BABBLE O");
265 else
266 atsReplyBabble(pClient, "BABBLE ");
267 RTMemFree(pPktHdr);
268 }
269
270 /* Try again or return failure? */
271 if ( g_fTerminate
272 || rc != VERR_INTERRUPTED
273 || !fAutoRetryOnFailure
274 )
275 {
276 Log(("atsRecvPkt: rc=%Rrc\n", rc));
277 return rc;
278 }
279 }
280}
281
282/**
283 * Make a simple reply, only status opcode.
284 *
285 * @returns IPRT status code of the send.
286 * @param pClient The ATS client structure.
287 * @param pReply The reply packet.
288 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
289 * with space.
290 * @param cbExtra Bytes in addition to the header.
291 */
292static int atsReplyInternal(PATSCLIENT pClient, PATSPKTSTS pReply, const char *pszOpcode, size_t cbExtra)
293{
294 /* copy the opcode, don't be too strict in case of a padding screw up. */
295 size_t cchOpcode = strlen(pszOpcode);
296 if (RT_LIKELY(cchOpcode == sizeof(pReply->Hdr.achOpcode)))
297 memcpy(pReply->Hdr.achOpcode, pszOpcode, sizeof(pReply->Hdr.achOpcode));
298 else
299 {
300 Assert(cchOpcode == sizeof(pReply->Hdr.achOpcode));
301 while (cchOpcode > 0 && pszOpcode[cchOpcode - 1] == ' ')
302 cchOpcode--;
303 AssertMsgReturn(cchOpcode < sizeof(pReply->Hdr.achOpcode), ("%d/'%.8s'\n", cchOpcode, pszOpcode), VERR_INTERNAL_ERROR_4);
304 memcpy(pReply->Hdr.achOpcode, pszOpcode, cchOpcode);
305 memset(&pReply->Hdr.achOpcode[cchOpcode], ' ', sizeof(pReply->Hdr.achOpcode) - cchOpcode);
306 }
307
308 pReply->Hdr.cb = (uint32_t)sizeof(ATSPKTSTS) + (uint32_t)cbExtra;
309 pReply->Hdr.uCrc32 = 0;
310
311 return atsSendPkt(pClient, &pReply->Hdr);
312}
313
314/**
315 * Make a simple reply, only status opcode.
316 *
317 * @returns IPRT status code of the send.
318 * @param pClient The ATS client 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(PATSCLIENT pClient, PCATSPKTHDR pPktHdr, const char *pszOpcode)
324{
325 ATSPKTSTS Pkt;
326
327 RT_ZERO(Pkt);
328 Pkt.rcReq = VINF_SUCCESS;
329 Pkt.cchStsMsg = 0;
330 NOREF(pPktHdr);
331 return atsReplyInternal(pClient, &Pkt, pszOpcode, 0);
332}
333
334/**
335 * Acknowledges a packet with success.
336 *
337 * @returns IPRT status code of the send.
338 * @param pClient The ATS client structure.
339 * @param pPktHdr The original packet (for future use).
340 */
341static int atsReplyAck(PATSCLIENT pClient, PCATSPKTHDR pPktHdr)
342{
343 return atsReplySimple(pClient, pPktHdr, "ACK ");
344}
345
346/**
347 * Replies with a failure.
348 *
349 * @returns IPRT status code of the send.
350 * @param pClient The ATS client structure.
351 * @param pPktHdr The original packet (for future use).
352 * @param rcReq Status code.
353 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
354 * with space.
355 * @param rcReq The status code of the request.
356 * @param pszDetailFmt Longer description of the problem (format string).
357 * @param va Format arguments.
358 */
359static int atsReplyFailureV(PATSCLIENT pClient, PCATSPKTHDR pPktHdr, const char *pszOpcode, int rcReq, const char *pszDetailFmt, va_list va)
360{
361 NOREF(pPktHdr);
362 union
363 {
364 ATSPKTSTS Hdr;
365 char ach[256];
366 } uPkt;
367
368 RT_ZERO(uPkt);
369 size_t cchDetail = RTStrPrintfV(&uPkt.ach[sizeof(ATSPKTSTS)],
370 sizeof(uPkt) - sizeof(ATSPKTSTS),
371 pszDetailFmt, va);
372 uPkt.Hdr.rcReq = rcReq;
373 uPkt.Hdr.cchStsMsg = cchDetail;
374 return atsReplyInternal(pClient, &uPkt.Hdr, pszOpcode, cchDetail + 1);
375}
376
377/**
378 * Replies with a failure.
379 *
380 * @returns IPRT status code of the send.
381 * @param pClient The ATS client structure.
382 * @param pPktHdr The original packet (for future use).
383 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
384 * with space.
385 * @param rcReq Status code.
386 * @param pszDetailFmt Longer description of the problem (format string).
387 * @param ... Format arguments.
388 */
389static int atsReplyFailure(PATSCLIENT pClient, PCATSPKTHDR pPktHdr, const char *pszOpcode, int rcReq, const char *pszDetailFmt, ...)
390{
391 va_list va;
392 va_start(va, pszDetailFmt);
393 int rc = atsReplyFailureV(pClient, 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 pClient The ATS client structure.
403 * @param pPktHdr The packet to reply to.
404 * @param rcOperation The status code to report.
405 * @param pszOperationFmt The operation that failed. Typically giving the
406 * function call with important arguments.
407 * @param ... Arguments to the format string.
408 */
409static int atsReplyRC(PATSCLIENT pClient, PCATSPKTHDR pPktHdr, int rcOperation, const char *pszOperationFmt, ...)
410{
411 if (RT_SUCCESS(rcOperation))
412 return atsReplyAck(pClient, pPktHdr);
413
414 char szOperation[128];
415 va_list va;
416 va_start(va, pszOperationFmt);
417 RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va);
418 va_end(va);
419
420 return atsReplyFailure(pClient, pPktHdr, "FAILED ", rcOperation, "%s failed with rc=%Rrc (opcode '%.8s')",
421 szOperation, rcOperation, pPktHdr->achOpcode);
422}
423
424/**
425 * Signal a bad packet exact size.
426 *
427 * @returns IPRT status code of the send.
428 * @param pClient The ATS client structure.
429 * @param pPktHdr The packet to reply to.
430 * @param cb The wanted size.
431 */
432static int atsReplyBadSize(PATSCLIENT pClient, PCATSPKTHDR pPktHdr, size_t cb)
433{
434 return atsReplyFailure(pClient, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at %zu bytes, got %u (opcode '%.8s')",
435 cb, pPktHdr->cb, pPktHdr->achOpcode);
436}
437
438/**
439 * Deals with a unknown command.
440 * @returns IPRT status code of the send.
441 * @param pClient The ATS client structure.
442 * @param pPktHdr The packet to reply to.
443 */
444static int atsReplyUnknown(PATSCLIENT pClient, PCATSPKTHDR pPktHdr)
445{
446 return atsReplyFailure(pClient, pPktHdr, "UNKNOWN ", VERR_NOT_FOUND, "Opcode '%.8s' is not known", pPktHdr->achOpcode);
447}
448
449#if 0
450/**
451 * Deals with a command which contains an unterminated string.
452 *
453 * @returns IPRT status code of the send.
454 * @param pClient The ATS client structure.
455 * @param pPktHdr The packet containing the unterminated string.
456 */
457static int atsReplyBadStrTermination(PATSCLIENT pClient, PCATSPKTHDR pPktHdr)
458{
459 return atsReplyFailure(pClient, pPktHdr, "BAD TERM", VERR_INVALID_PARAMETER, "Opcode '%.8s' contains an unterminated string", pPktHdr->achOpcode);
460}
461#endif
462
463/**
464 * Deals with a command sent in an invalid client state.
465 *
466 * @returns IPRT status code of the send.
467 * @param pClient The ATS client structure.
468 * @param pPktHdr The packet containing the unterminated string.
469 */
470static int atsReplyInvalidState(PATSCLIENT pClient, PCATSPKTHDR pPktHdr)
471{
472 return atsReplyFailure(pClient, pPktHdr, "INVSTATE", VERR_INVALID_STATE, "Opcode '%.8s' is not supported at client state '%s",
473 pPktHdr->achOpcode, atsClientStateStringify(pClient->enmState));
474}
475
476/**
477 * Verifies and acknowledges a "BYE" request.
478 *
479 * @returns IPRT status code.
480 * @param pClient The ATS client structure.
481 * @param pPktHdr The howdy packet.
482 */
483static int atsDoBye(PATSCLIENT pClient, PCATSPKTHDR pPktHdr)
484{
485 int rc;
486 if (pPktHdr->cb == sizeof(ATSPKTHDR))
487 rc = atsReplyAck(pClient, pPktHdr);
488 else
489 rc = atsReplyBadSize(pClient, pPktHdr, sizeof(ATSPKTHDR));
490 return rc;
491}
492
493/**
494 * Verifies and acknowledges a "HOWDY" request.
495 *
496 * @returns IPRT status code.
497 * @param pClient The ATS client structure.
498 * @param pPktHdr The howdy packet.
499 */
500static int atsDoHowdy(PATSCLIENT pClient, PCATSPKTHDR pPktHdr)
501{
502 int rc = VINF_SUCCESS;
503
504 if (pPktHdr->cb != sizeof(ATSPKTREQHOWDY))
505 return atsReplyBadSize(pClient, pPktHdr, sizeof(ATSPKTREQHOWDY));
506
507 if (pClient->enmState != ATSCLIENTSTATE_INITIALISING)
508 return atsReplyInvalidState(pClient, pPktHdr);
509
510 PATSPKTREQHOWDY pReq = (PATSPKTREQHOWDY)pPktHdr;
511
512 if (pReq->uVersion != ATS_PROTOCOL_VS)
513 return atsReplyRC(pClient, pPktHdr, VERR_VERSION_MISMATCH, "The given version %#x is not supported", pReq->uVersion);
514
515 return rc;
516}
517
518/**
519 * Verifies and processes a "TONE PLAY" request.
520 *
521 * @returns IPRT status code.
522 * @param pClient The ATS client structure.
523 * @param pPktHdr The packet header.
524 */
525static int atsDoTonePlay(PATSCLIENT pClient, PCATSPKTHDR pPktHdr)
526{
527 int rc = VINF_SUCCESS;
528
529 if (pPktHdr->cb < sizeof(ATSPKTREQTONEPLAY))
530 return atsReplyBadSize(pClient, pPktHdr, sizeof(ATSPKTREQTONEPLAY));
531
532 if (pClient->enmState != ATSCLIENTSTATE_READY)
533 return atsReplyInvalidState(pClient, pPktHdr);
534
535 return rc;
536}
537
538/**
539 * Main request processing routine for each client.
540 *
541 * @returns IPRT status code.
542 * @param pClient The ATS client structure sending the request.
543 */
544static int atsClientReqProcess(PATSCLIENT pClient)
545{
546 /*
547 * Read client command packet and process it.
548 */
549 PATSPKTHDR pPktHdr = NULL;
550 int rc = atsRecvPkt(pClient, &pPktHdr, true /*fAutoRetryOnFailure*/);
551 if (RT_FAILURE(rc))
552 return rc;
553
554 /*
555 * Do a string switch on the opcode bit.
556 */
557 /* Connection: */
558 if ( atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_HOWDY))
559 rc = atsDoHowdy(pClient, pPktHdr);
560 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_BYE))
561 rc = atsDoBye(pClient, pPktHdr);
562 /* Gadget API. */
563 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TONE_PLAY))
564 rc = atsDoTonePlay(pClient, pPktHdr);
565 /* Misc: */
566 else
567 rc = atsReplyUnknown(pClient, pPktHdr);
568
569 RTMemFree(pPktHdr);
570
571 return rc;
572}
573
574/**
575 * Destroys a client instance.
576 *
577 * @returns nothing.
578 * @param pClient The ATS client structure.
579 */
580static void atsClientDestroy(PATSCLIENT pClient)
581{
582 if (pClient->pszHostname)
583 RTStrFree(pClient->pszHostname);
584 RTMemFree(pClient);
585}
586
587/**
588 * The main thread worker serving the clients.
589 */
590static DECLCALLBACK(int) atsClientWorker(RTTHREAD hThread, void *pvUser)
591{
592 RT_NOREF2(hThread, pvUser);
593 unsigned cClientsMax = 0;
594 unsigned cClientsCur = 0;
595 PATSCLIENT *papClients = NULL;
596 RTPOLLSET hPollSet;
597
598 int rc = RTPollSetCreate(&hPollSet);
599 if (RT_FAILURE(rc))
600 return rc;
601
602 /* Add the pipe to the poll set. */
603 rc = RTPollSetAddPipe(hPollSet, g_hPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 0);
604 if (RT_SUCCESS(rc))
605 {
606 while (!g_fTerminate)
607 {
608 uint32_t fEvts;
609 uint32_t uId;
610 rc = RTPoll(hPollSet, RT_INDEFINITE_WAIT, &fEvts, &uId);
611 if (RT_SUCCESS(rc))
612 {
613 if (uId == 0)
614 {
615 if (fEvts & RTPOLL_EVT_ERROR)
616 break;
617
618 /* We got woken up because of a new client. */
619 Assert(fEvts & RTPOLL_EVT_READ);
620
621 uint8_t bRead;
622 size_t cbRead = 0;
623 rc = RTPipeRead(g_hPipeR, &bRead, 1, &cbRead);
624 AssertRC(rc);
625
626 RTCritSectEnter(&g_CritSectClients);
627 /* Walk the list and add all new clients. */
628 PATSCLIENT pIt, pItNext;
629 RTListForEachSafe(&g_LstClientsNew, pIt, pItNext, ATSCLIENT, NdLst)
630 {
631 RTListNodeRemove(&pIt->NdLst);
632 Assert(cClientsCur <= cClientsMax);
633 if (cClientsCur == cClientsMax)
634 {
635 /* Realloc to accommodate for the new clients. */
636 PATSCLIENT *papClientsNew = (PATSCLIENT *)RTMemRealloc(papClients, (cClientsMax + 10) * sizeof(PATSCLIENT));
637 if (RT_LIKELY(papClientsNew))
638 {
639 cClientsMax += 10;
640 papClients = papClientsNew;
641 }
642 }
643
644 if (cClientsCur < cClientsMax)
645 {
646 /* Find a free slot in the client array. */
647 unsigned idxSlt = 0;
648 while ( idxSlt < cClientsMax
649 && papClients[idxSlt] != NULL)
650 idxSlt++;
651
652 rc = g_pTransport->pfnPollSetAdd(hPollSet, pIt->pTransportClient, idxSlt + 1);
653 if (RT_SUCCESS(rc))
654 {
655 cClientsCur++;
656 papClients[idxSlt] = pIt;
657 }
658 else
659 {
660 g_pTransport->pfnNotifyBye(pIt->pTransportClient);
661 atsClientDestroy(pIt);
662 }
663 }
664 else
665 {
666 g_pTransport->pfnNotifyBye(pIt->pTransportClient);
667 atsClientDestroy(pIt);
668 }
669 }
670 RTCritSectLeave(&g_CritSectClients);
671 }
672 else
673 {
674 /* Client sends a request, pick the right client and process it. */
675 PATSCLIENT pClient = papClients[uId - 1];
676 AssertPtr(pClient);
677 if (fEvts & RTPOLL_EVT_READ)
678 rc = atsClientReqProcess(pClient);
679
680 if ( (fEvts & RTPOLL_EVT_ERROR)
681 || RT_FAILURE(rc))
682 {
683 /* Close connection and remove client from array. */
684 rc = g_pTransport->pfnPollSetRemove(hPollSet, pClient->pTransportClient, uId);
685 AssertRC(rc);
686
687 g_pTransport->pfnNotifyBye(pClient->pTransportClient);
688 papClients[uId - 1] = NULL;
689 cClientsCur--;
690 atsClientDestroy(pClient);
691 }
692 }
693 }
694 }
695 }
696
697 RTPollSetDestroy(hPollSet);
698
699 return rc;
700}
701
702/**
703 * The main thread waiting for new client connections.
704 *
705 * @returns VBox status code.
706 */
707int atsMainThread(RTTHREAD hThread, void *pvUser)
708{
709 RT_NOREF(hThread, pvUser);
710
711 int rc = VINF_SUCCESS;
712
713 while (!g_fTerminate)
714 {
715 /*
716 * Wait for new connection and spin off a new thread
717 * for every new client.
718 */
719 PATSTRANSPORTCLIENT pTransportClient;
720 rc = g_pTransport->pfnWaitForConnect(&pTransportClient);
721 if (RT_FAILURE(rc))
722 continue;
723
724 /*
725 * New connection, create new client structure and spin of
726 * the request handling thread.
727 */
728 PATSCLIENT pClient = (PATSCLIENT)RTMemAllocZ(sizeof(ATSCLIENT));
729 if (RT_LIKELY(pClient))
730 {
731 pClient->enmState = ATSCLIENTSTATE_INITIALISING;
732 pClient->pTransportClient = pTransportClient;
733 pClient->pszHostname = NULL;
734
735 /* Add client to the new list and inform the worker thread. */
736 RTCritSectEnter(&g_CritSectClients);
737 RTListAppend(&g_LstClientsNew, &pClient->NdLst);
738 RTCritSectLeave(&g_CritSectClients);
739
740 size_t cbWritten = 0;
741 rc = RTPipeWrite(g_hPipeW, "", 1, &cbWritten);
742 if (RT_FAILURE(rc))
743 RTMsgError("Failed to inform worker thread of a new client");
744 }
745 else
746 {
747 RTMsgError("Creating new client structure failed with out of memory error\n");
748 g_pTransport->pfnNotifyBye(pTransportClient);
749 }
750 }
751
752 return rc;
753}
754
755/**
756 * Initializes the global ATS state.
757 *
758 * @returns VBox status code.
759 */
760int atsInit(void)
761{
762 RTListInit(&g_LstClientsNew);
763
764 /*
765 * The default transporter is the first one.
766 */
767 g_pTransport = g_apTransports[0];
768
769 /*
770 * Initialize the transport layer.
771 */
772 int rc = g_pTransport->pfnInit();
773 if (RT_SUCCESS(rc))
774 {
775 rc = RTCritSectInit(&g_CritSectClients);
776 if (RT_SUCCESS(rc))
777 {
778 rc = RTPipeCreate(&g_hPipeR, &g_hPipeW, 0);
779 if (RT_SUCCESS(rc))
780 {
781 /* Spin off the thread serving connections. */
782 rc = RTThreadCreate(&g_hThreadServing, atsClientWorker, NULL, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
783 "AUDTSTSRVC");
784 if (RT_SUCCESS(rc))
785 return VINF_SUCCESS;
786 else
787 RTMsgError("Creating the client worker thread failed with %Rrc\n", rc);
788
789 RTPipeClose(g_hPipeR);
790 RTPipeClose(g_hPipeW);
791 }
792 else
793 RTMsgError("Creating communications pipe failed with %Rrc\n", rc);
794
795 RTCritSectDelete(&g_CritSectClients);
796 }
797 else
798 RTMsgError("Creating global critical section failed with %Rrc\n", rc);
799 }
800 else
801 RTMsgError("Initializing the platform failed with %Rrc\n", rc);
802
803 return rc;
804}
805
806int atsStart(void)
807{
808 /* Spin off the main thread. */
809 int rc = RTThreadCreate(&g_hThreadMain, atsMainThread, NULL, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
810 "AUDTSTSRVM");
811
812 return rc;
813}
814
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