VirtualBox

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

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

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

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