VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/usb/UsbTestService.cpp@ 60279

Last change on this file since 60279 was 60279, checked in by vboxsync, 9 years ago

ValidationKit/usb: Early commit of the new remote USB test configuration and execution server. It will replace the current approach of using TXS to configure the gadgets because that approach is not suitable for multiple clients accessing the same gadget (software devices exported over USB/IP). Non functional and heavily work in progress

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.1 KB
Line 
1/* $Id: UsbTestService.cpp 60279 2016-03-31 18:57:37Z vboxsync $ */
2/** @file
3 * UsbTestService - Remote USB test configuration and execution server.
4 */
5
6/*
7 * Copyright (C) 2010-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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/list.h>
45#include <iprt/log.h>
46#include <iprt/mem.h>
47#include <iprt/message.h>
48#include <iprt/param.h>
49#include <iprt/path.h>
50#include <iprt/pipe.h>
51#include <iprt/poll.h>
52#include <iprt/process.h>
53#include <iprt/stream.h>
54#include <iprt/string.h>
55#include <iprt/thread.h>
56
57#include "UsbTestServiceInternal.h"
58
59
60
61/*********************************************************************************************************************************
62* Structures and Typedefs *
63*********************************************************************************************************************************/
64
65/**
66 * UTS client instance.
67 */
68typedef struct UTSCLIENT
69{
70 /** List node for new clients. */
71 RTLISTNODE NdLst;
72 /** Transport backend specific data. */
73 PUTSTRANSPORTCLIENT pTransportClient;
74} UTSCLIENT;
75/** Pointer to a UTS client instance. */
76typedef UTSCLIENT *PUTSCLIENT;
77
78/*********************************************************************************************************************************
79* Global Variables *
80*********************************************************************************************************************************/
81/**
82 * Transport layers.
83 */
84static const PCUTSTRANSPORT g_apTransports[] =
85{
86 &g_TcpTransport,
87 //&g_SerialTransport,
88 //&g_FileSysTransport,
89 //&g_GuestPropTransport,
90 //&g_TestDevTransport,
91};
92
93/** The select transport layer. */
94static PCUTSTRANSPORT g_pTransport;
95/** The scratch path. */
96static char g_szScratchPath[RTPATH_MAX];
97/** The default scratch path. */
98static char g_szDefScratchPath[RTPATH_MAX];
99/** The CD/DVD-ROM path. */
100static char g_szCdRomPath[RTPATH_MAX];
101/** The default CD/DVD-ROM path. */
102static char g_szDefCdRomPath[RTPATH_MAX];
103/** The operating system short name. */
104static char g_szOsShortName[16];
105/** The CPU architecture short name. */
106static char g_szArchShortName[16];
107/** The combined "OS.arch" name. */
108static char g_szOsDotArchShortName[32];
109/** The combined "OS/arch" name. */
110static char g_szOsSlashArchShortName[32];
111/** The executable suffix. */
112static char g_szExeSuff[8];
113/** The shell script suffix. */
114static char g_szScriptSuff[8];
115/** Whether to display the output of the child process or not. */
116static bool g_fDisplayOutput = true;
117/** Whether to terminate or not.
118 * @todo implement signals and stuff. */
119static bool volatile g_fTerminate = false;
120/** Pipe for communicating with the serving thread about new clients. - read end */
121static RTPIPE g_hPipeR;
122/** Pipe for communicating with the serving thread about new clients. - write end */
123static RTPIPE g_hPipeW;
124/** Thread serving connected clients. */
125static RTTHREAD g_hThreadServing;
126/** Critical section protecting the list of new clients. */
127static RTCRITSECT g_CritSectClients;
128/** List of new clients waiting to be picked up by the client worker thread. */
129static RTLISTANCHOR g_LstClientsNew;
130
131
132/**
133 * Calculates the checksum value, zero any padding space and send the packet.
134 *
135 * @returns IPRT status code.
136 * @param pClient The UTS client structure.
137 * @param pPkt The packet to send. Must point to a correctly
138 * aligned buffer.
139 */
140static int utsSendPkt(PUTSCLIENT pClient, PUTSPKTHDR pPkt)
141{
142 Assert(pPkt->cb >= sizeof(*pPkt));
143 pPkt->uCrc32 = RTCrc32(pPkt->achOpcode, pPkt->cb - RT_OFFSETOF(UTSPKTHDR, achOpcode));
144 if (pPkt->cb != RT_ALIGN_32(pPkt->cb, UTSPKT_ALIGNMENT))
145 memset((uint8_t *)pPkt + pPkt->cb, '\0', RT_ALIGN_32(pPkt->cb, UTSPKT_ALIGNMENT) - pPkt->cb);
146
147 Log(("utsSendPkt: cb=%#x opcode=%.8s\n", pPkt->cb, pPkt->achOpcode));
148 Log2(("%.*Rhxd\n", RT_MIN(pPkt->cb, 256), pPkt));
149 int rc = g_pTransport->pfnSendPkt(pClient->pTransportClient, pPkt);
150 while (RT_UNLIKELY(rc == VERR_INTERRUPTED) && !g_fTerminate)
151 rc = g_pTransport->pfnSendPkt(pClient->pTransportClient, pPkt);
152 if (RT_FAILURE(rc))
153 Log(("utsSendPkt: rc=%Rrc\n", rc));
154
155 return rc;
156}
157
158/**
159 * Sends a babble reply and disconnects the client (if applicable).
160 *
161 * @param pClient The UTS client structure.
162 * @param pszOpcode The BABBLE opcode.
163 */
164static void utsReplyBabble(PUTSCLIENT pClient, const char *pszOpcode)
165{
166 UTSPKTHDR Reply;
167 Reply.cb = sizeof(Reply);
168 Reply.uCrc32 = 0;
169 memcpy(Reply.achOpcode, pszOpcode, sizeof(Reply.achOpcode));
170
171 g_pTransport->pfnBabble(pClient->pTransportClient, &Reply, 20*1000);
172}
173
174/**
175 * Receive and validate a packet.
176 *
177 * Will send bable responses to malformed packets that results in a error status
178 * code.
179 *
180 * @returns IPRT status code.
181 * @param pClient The UTS client structure.
182 * @param ppPktHdr Where to return the packet on success. Free
183 * with RTMemFree.
184 * @param fAutoRetryOnFailure Whether to retry on error.
185 */
186static int utsRecvPkt(PUTSCLIENT pClient, PPUTSPKTHDR ppPktHdr, bool fAutoRetryOnFailure)
187{
188 for (;;)
189 {
190 PUTSPKTHDR pPktHdr;
191 int rc = g_pTransport->pfnRecvPkt(pClient->pTransportClient, &pPktHdr);
192 if (RT_SUCCESS(rc))
193 {
194 /* validate the packet. */
195 if ( pPktHdr->cb >= sizeof(UTSPKTHDR)
196 && pPktHdr->cb < UTSPKT_MAX_SIZE)
197 {
198 Log2(("utsRecvPkt: pPktHdr=%p cb=%#x crc32=%#x opcode=%.8s\n"
199 "%.*Rhxd\n",
200 pPktHdr, pPktHdr->cb, pPktHdr->uCrc32, pPktHdr->achOpcode, RT_MIN(pPktHdr->cb, 256), pPktHdr));
201 uint32_t uCrc32Calc = pPktHdr->uCrc32 != 0
202 ? RTCrc32(&pPktHdr->achOpcode[0], pPktHdr->cb - RT_OFFSETOF(UTSPKTHDR, achOpcode))
203 : 0;
204 if (pPktHdr->uCrc32 == uCrc32Calc)
205 {
206 AssertCompileMemberSize(UTSPKTHDR, achOpcode, 8);
207 if ( RT_C_IS_UPPER(pPktHdr->achOpcode[0])
208 && RT_C_IS_UPPER(pPktHdr->achOpcode[1])
209 && (RT_C_IS_UPPER(pPktHdr->achOpcode[2]) || pPktHdr->achOpcode[2] == ' ')
210 && (RT_C_IS_PRINT(pPktHdr->achOpcode[3]) || pPktHdr->achOpcode[3] == ' ')
211 && (RT_C_IS_PRINT(pPktHdr->achOpcode[4]) || pPktHdr->achOpcode[4] == ' ')
212 && (RT_C_IS_PRINT(pPktHdr->achOpcode[5]) || pPktHdr->achOpcode[5] == ' ')
213 && (RT_C_IS_PRINT(pPktHdr->achOpcode[6]) || pPktHdr->achOpcode[6] == ' ')
214 && (RT_C_IS_PRINT(pPktHdr->achOpcode[7]) || pPktHdr->achOpcode[7] == ' ')
215 )
216 {
217 Log(("utsRecvPkt: cb=%#x opcode=%.8s\n", pPktHdr->cb, pPktHdr->achOpcode));
218 *ppPktHdr = pPktHdr;
219 return rc;
220 }
221
222 rc = VERR_IO_BAD_COMMAND;
223 }
224 else
225 {
226 Log(("utsRecvPkt: cb=%#x opcode=%.8s crc32=%#x actual=%#x\n",
227 pPktHdr->cb, pPktHdr->achOpcode, pPktHdr->uCrc32, uCrc32Calc));
228 rc = VERR_IO_CRC;
229 }
230 }
231 else
232 rc = VERR_IO_BAD_LENGTH;
233
234 /* Send babble reply and disconnect the client if the transport is
235 connection oriented. */
236 if (rc == VERR_IO_BAD_LENGTH)
237 utsReplyBabble(pClient, "BABBLE L");
238 else if (rc == VERR_IO_CRC)
239 utsReplyBabble(pClient, "BABBLE C");
240 else if (rc == VERR_IO_BAD_COMMAND)
241 utsReplyBabble(pClient, "BABBLE O");
242 else
243 utsReplyBabble(pClient, "BABBLE ");
244 RTMemFree(pPktHdr);
245 }
246
247 /* Try again or return failure? */
248 if ( g_fTerminate
249 || rc != VERR_INTERRUPTED
250 || !fAutoRetryOnFailure
251 )
252 {
253 Log(("utsRecvPkt: rc=%Rrc\n", rc));
254 return rc;
255 }
256 }
257}
258
259/**
260 * Make a simple reply, only status opcode.
261 *
262 * @returns IPRT status code of the send.
263 * @param pClient The UTS client structure.
264 * @param pReply The reply packet.
265 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
266 * with space.
267 * @param cbExtra Bytes in addition to the header.
268 */
269static int utsReplyInternal(PUTSCLIENT pClient, PUTSPKTSTS pReply, const char *pszOpcode, size_t cbExtra)
270{
271 /* copy the opcode, don't be too strict in case of a padding screw up. */
272 size_t cchOpcode = strlen(pszOpcode);
273 if (RT_LIKELY(cchOpcode == sizeof(pReply->Hdr.achOpcode)))
274 memcpy(pReply->Hdr.achOpcode, pszOpcode, sizeof(pReply->Hdr.achOpcode));
275 else
276 {
277 Assert(cchOpcode == sizeof(pReply->Hdr.achOpcode));
278 while (cchOpcode > 0 && pszOpcode[cchOpcode - 1] == ' ')
279 cchOpcode--;
280 AssertMsgReturn(cchOpcode < sizeof(pReply->Hdr.achOpcode), ("%d/'%.8s'\n", cchOpcode, pszOpcode), VERR_INTERNAL_ERROR_4);
281 memcpy(pReply->Hdr.achOpcode, pszOpcode, cchOpcode);
282 memset(&pReply->Hdr.achOpcode[cchOpcode], ' ', sizeof(pReply->Hdr.achOpcode) - cchOpcode);
283 }
284
285 pReply->Hdr.cb = (uint32_t)sizeof(UTSPKTSTS) + (uint32_t)cbExtra;
286 pReply->Hdr.uCrc32 = 0;
287
288 return utsSendPkt(pClient, &pReply->Hdr);
289}
290
291/**
292 * Make a simple reply, only status opcode.
293 *
294 * @returns IPRT status code of the send.
295 * @param pClient The UTS client structure.
296 * @param pPktHdr The original packet (for future use).
297 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
298 * with space.
299 */
300static int utsReplySimple(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode)
301{
302 UTSPKTSTS Pkt;
303
304 RT_ZERO(Pkt);
305 Pkt.rcReq = VINF_SUCCESS;
306 Pkt.cchStsMsg = 0;
307 NOREF(pPktHdr);
308 return utsReplyInternal(pClient, &Pkt, pszOpcode, 0);
309}
310
311/**
312 * Acknowledges a packet with success.
313 *
314 * @returns IPRT status code of the send.
315 * @param pClient The UTS client structure.
316 * @param pPktHdr The original packet (for future use).
317 */
318static int utsReplyAck(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
319{
320 return utsReplySimple(pClient, pPktHdr, "ACK ");
321}
322
323/**
324 * Replies with a failure.
325 *
326 * @returns IPRT status code of the send.
327 * @param pClient The UTS client structure.
328 * @param pPktHdr The original packet (for future use).
329 * @param rcReq Status code.
330 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
331 * with space.
332 * @param pszDetailsFmt Longer description of the problem (format
333 * string).
334 * @param va Format arguments.
335 */
336static int utsReplyFailureV(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode, int rcReq, const char *pszDetailFmt, va_list va)
337{
338 NOREF(pPktHdr);
339 union
340 {
341 UTSPKTSTS Hdr;
342 char ach[256];
343 } uPkt;
344
345 RT_ZERO(uPkt);
346 size_t cchDetail = RTStrPrintfV(&uPkt.ach[sizeof(UTSPKTSTS)],
347 sizeof(uPkt) - sizeof(UTSPKTSTS),
348 pszDetailFmt, va);
349 uPkt.Hdr.rcReq = rcReq;
350 uPkt.Hdr.cchStsMsg = cchDetail;
351 return utsReplyInternal(pClient, &uPkt.Hdr, pszOpcode, cchDetail + 1);
352}
353
354/**
355 * Replies with a failure.
356 *
357 * @returns IPRT status code of the send.
358 * @param pClient The UTS client structure.
359 * @param pPktHdr The original packet (for future use).
360 * @param rcReq Status code.
361 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
362 * with space.
363 * @param pszDetails Longer description of the problem (format
364 * string).
365 * @param ... Format arguments.
366 */
367static int utsReplyFailure(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode, int rcReq, const char *pszDetailFmt, ...)
368{
369 va_list va;
370 va_start(va, pszDetailFmt);
371 int rc = utsReplyFailureV(pClient, pPktHdr, pszOpcode, rcReq, pszDetailFmt, va);
372 va_end(va);
373 return rc;
374}
375
376/**
377 * Replies according to the return code.
378 *
379 * @returns IPRT status code of the send.
380 * @param pClient The UTS client structure.
381 * @param pPktHdr The packet to reply to.
382 * @param rcOperation The status code to report.
383 * @param pszOperationFmt The operation that failed. Typically giving the
384 * function call with important arguments.
385 * @param ... Arguments to the format string.
386 */
387static int utsReplyRC(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, int rcOperation, const char *pszOperationFmt, ...)
388{
389 if (RT_SUCCESS(rcOperation))
390 return utsReplyAck(pClient, pPktHdr);
391
392 char szOperation[128];
393 va_list va;
394 va_start(va, pszOperationFmt);
395 RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va);
396 va_end(va);
397
398 return utsReplyFailure(pClient, pPktHdr, "FAILED ", rcOperation, "%s failed with rc=%Rrc (opcode '%.8s')",
399 rcOperation, szOperation, rcOperation, pPktHdr->achOpcode);
400}
401
402/**
403 * Signal a bad packet minum size.
404 *
405 * @returns IPRT status code of the send.
406 * @param pClient The UTS client structure.
407 * @param pPktHdr The packet to reply to.
408 * @param cbMin The minimum size.
409 */
410static int utsReplyBadMinSize(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, size_t cbMin)
411{
412 return utsReplyFailure(pClient, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at least %zu bytes, got %u (opcode '%.8s')",
413 cbMin, pPktHdr->cb, pPktHdr->achOpcode);
414}
415
416/**
417 * Signal a bad packet exact size.
418 *
419 * @returns IPRT status code of the send.
420 * @param pClient The UTS client structure.
421 * @param pPktHdr The packet to reply to.
422 * @param cb The wanted size.
423 */
424static int utsReplyBadSize(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, size_t cb)
425{
426 return utsReplyFailure(pClient, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at %zu bytes, got %u (opcode '%.8s')",
427 cb, pPktHdr->cb, pPktHdr->achOpcode);
428}
429
430/**
431 * Deals with a command that isn't implemented yet.
432 * @returns IPRT status code of the send.
433 * @param pClient The UTS client structure.
434 * @param pPktHdr The packet which opcode isn't implemented.
435 */
436static int utsReplyNotImplemented(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
437{
438 return utsReplyFailure(pClient, pPktHdr, "NOT IMPL", VERR_NOT_IMPLEMENTED, "Opcode '%.8s' is not implemented", pPktHdr->achOpcode);
439}
440
441/**
442 * Deals with a unknown command.
443 * @returns IPRT status code of the send.
444 * @param pClient The UTS client structure.
445 * @param pPktHdr The packet to reply to.
446 */
447static int utsReplyUnknown(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
448{
449 return utsReplyFailure(pClient, pPktHdr, "UNKNOWN ", VERR_NOT_FOUND, "Opcode '%.8s' is not known", pPktHdr->achOpcode);
450}
451
452/**
453 * Checks if the two opcodes match.
454 *
455 * @returns true on match, false on mismatch.
456 * @param pPktHdr The packet header.
457 * @param pszOpcode2 The opcode we're comparing with. Does not have
458 * to be the whole 8 chars long.
459 */
460DECLINLINE(bool) utsIsSameOpcode(PCUTSPKTHDR pPktHdr, const char *pszOpcode2)
461{
462 if (pPktHdr->achOpcode[0] != pszOpcode2[0])
463 return false;
464 if (pPktHdr->achOpcode[1] != pszOpcode2[1])
465 return false;
466
467 unsigned i = 2;
468 while ( i < RT_SIZEOFMEMB(UTSPKTHDR, achOpcode)
469 && pszOpcode2[i] != '\0')
470 {
471 if (pPktHdr->achOpcode[i] != pszOpcode2[i])
472 break;
473 i++;
474 }
475
476 if ( i < RT_SIZEOFMEMB(UTSPKTHDR, achOpcode)
477 && pszOpcode2[i] == '\0')
478 {
479 while ( i < RT_SIZEOFMEMB(UTSPKTHDR, achOpcode)
480 && pPktHdr->achOpcode[i] == ' ')
481 i++;
482 }
483
484 return i == RT_SIZEOFMEMB(UTSPKTHDR, achOpcode);
485}
486
487/**
488 * Verifies and acknowledges a "BYE" request.
489 *
490 * @returns IPRT status code.
491 * @param pClient The UTS client structure.
492 * @param pPktHdr The howdy packet.
493 */
494static int utsDoBye(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
495{
496 int rc;
497 if (pPktHdr->cb == sizeof(UTSPKTHDR))
498 rc = utsReplyAck(pClient, pPktHdr);
499 else
500 rc = utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTHDR));
501 g_pTransport->pfnNotifyBye(pClient->pTransportClient);
502 return rc;
503}
504
505/**
506 * Verifies and acknowledges a "HOWDY" request.
507 *
508 * @returns IPRT status code.
509 * @param pClient The UTS client structure.
510 * @param pPktHdr The howdy packet.
511 */
512static int utsDoHowdy(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
513{
514 if (pPktHdr->cb != sizeof(UTSPKTHDR))
515 return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTHDR));
516 int rc = utsReplyAck(pClient, pPktHdr);
517 if (RT_SUCCESS(rc))
518 {
519 g_pTransport->pfnNotifyHowdy(pClient->pTransportClient);
520 RTDirRemoveRecursive(g_szScratchPath, RTDIRRMREC_F_CONTENT_ONLY);
521 }
522 return rc;
523}
524
525/**
526 * Main request processing routine for each client.
527 *
528 * @returns IPRT status code.
529 * @param pClient The UTS client structure sending the request.
530 */
531static int utsClientReqProcess(PUTSCLIENT pClient)
532{
533 /*
534 * Read client command packet and process it.
535 */
536 PUTSPKTHDR pPktHdr;
537 int rc = utsRecvPkt(pClient, &pPktHdr, true /*fAutoRetryOnFailure*/);
538 if (RT_FAILURE(rc))
539 return rc;
540
541 /*
542 * Do a string switch on the opcode bit.
543 */
544 /* Connection: */
545 if ( utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_HOWDY))
546 rc = utsDoHowdy(pClient, pPktHdr);
547 else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_BYE))
548 rc = utsDoBye(pClient, pPktHdr);
549 /* Misc: */
550 else
551 rc = utsReplyUnknown(pClient, pPktHdr);
552
553 RTMemFree(pPktHdr);
554
555 return rc;
556}
557
558/**
559 * Destroys a client instance.
560 *
561 * @returns nothing.
562 * @param pClient The UTS client structure.
563 */
564static void utsClientDestroy(PUTSCLIENT pClient)
565{
566 RTMemFree(pClient);
567}
568
569/**
570 * The main thread worker serving the clients.
571 */
572static DECLCALLBACK(int) utsClientWorker(RTTHREAD hThread, void *pvUser)
573{
574 unsigned cClientsMax = 0;
575 unsigned cClientsCur = 0;
576 PUTSCLIENT *papClients = NULL;
577 RTPOLLSET hPollSet;
578
579 int rc = RTPollSetCreate(&hPollSet);
580 if (RT_FAILURE(rc))
581 return rc;
582
583 /* Add the pipe to the poll set. */
584 rc = RTPollSetAddPipe(hPollSet, g_hPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 0);
585 if (RT_SUCCESS(rc))
586 {
587 while (!g_fTerminate)
588 {
589 uint32_t fEvts;
590 uint32_t uId;
591 rc = RTPoll(hPollSet, RT_INDEFINITE_WAIT, &fEvts, &uId);
592 if (RT_SUCCESS(rc))
593 {
594 if (uId == 0)
595 {
596 if (fEvts & RTPOLL_EVT_ERROR)
597 break;
598
599 /* We got woken up because of a new client. */
600 Assert(fEvts & RTPOLL_EVT_READ);
601
602 uint8_t bRead;
603 rc = RTPipeRead(g_hPipeR, &bRead, 1, NULL);
604 AssertRC(rc);
605
606 RTCritSectEnter(&g_CritSectClients);
607 /* Walk the list and add all new clients. */
608 PUTSCLIENT pIt, pItNext;
609 RTListForEachSafe(&g_LstClientsNew, pIt, pItNext, UTSCLIENT, NdLst)
610 {
611 RTListNodeRemove(&pIt->NdLst);
612 Assert(cClientsCur <= cClientsMax);
613 if (cClientsCur == cClientsMax)
614 {
615 /* Realloc to accommodate for the new clients. */
616 PUTSCLIENT *papClientsNew = (PUTSCLIENT *)RTMemRealloc(papClients, (cClientsMax + 10) * sizeof(PUTSCLIENT));
617 if (RT_LIKELY(papClientsNew))
618 {
619 cClientsMax += 10;
620 papClients = papClientsNew;
621 }
622 }
623
624 if (cClientsCur < cClientsMax)
625 {
626 /* Find a free slot in the client array. */
627 unsigned idxSlt = 0;
628 while ( idxSlt < cClientsMax
629 && papClients[idxSlt] != NULL)
630 idxSlt++;
631
632 rc = g_pTransport->pfnPollSetAdd(hPollSet, pIt->pTransportClient, idxSlt + 1);
633 if (RT_SUCCESS(rc))
634 {
635 cClientsCur++;
636 papClients[idxSlt] = pIt;
637 }
638 else
639 {
640 g_pTransport->pfnNotifyBye(pIt->pTransportClient);
641 utsClientDestroy(pIt);
642 }
643 }
644 else
645 {
646 g_pTransport->pfnNotifyBye(pIt->pTransportClient);
647 utsClientDestroy(pIt);
648 }
649 }
650 RTCritSectLeave(&g_CritSectClients);
651 }
652 else
653 {
654 /* Client sends a request, pick the right client and process it. */
655 PUTSCLIENT pClient = papClients[uId - 1];
656 if (fEvts & RTPOLL_EVT_READ)
657 rc = utsClientReqProcess(pClient);
658
659 if ( (fEvts & RTPOLL_EVT_ERROR)
660 || RT_FAILURE(rc))
661 {
662 /* Close connection and remove client from array. */
663 g_pTransport->pfnNotifyBye(pClient->pTransportClient);
664 papClients[uId - 1] = NULL;
665 cClientsCur--;
666 utsClientDestroy(pClient);
667 }
668 }
669 }
670 }
671 }
672
673 RTPollSetDestroy(hPollSet);
674
675 return rc;
676}
677
678/**
679 * The main loop.
680 *
681 * @returns exit code.
682 */
683static RTEXITCODE utsMainLoop(void)
684{
685 RTEXITCODE enmExitCode = RTEXITCODE_SUCCESS;
686 while (!g_fTerminate)
687 {
688 /*
689 * Wait for new connection and spin off a new thread
690 * for every new client.
691 */
692 PUTSTRANSPORTCLIENT pTransportClient;
693 int rc = g_pTransport->pfnWaitForConnect(&pTransportClient);
694 if (RT_FAILURE(rc))
695 continue;
696
697 /*
698 * New connection, create new client structure and spin of
699 * the request handling thread.
700 */
701 PUTSCLIENT pClient = (PUTSCLIENT)RTMemAllocZ(sizeof(PUTSCLIENT));
702 if (RT_LIKELY(pClient))
703 {
704 pClient->pTransportClient = pTransportClient;
705 }
706 else
707 {
708 RTMsgError("Creating new client structure failed with out of memory error\n");
709 g_pTransport->pfnNotifyBye(pTransportClient);
710 }
711
712
713 }
714
715 return enmExitCode;
716}
717
718/**
719 * Initializes the global UTS state.
720 *
721 * @returns IPRT status code.
722 */
723static int utsInit(void)
724{
725 int rc = VINF_SUCCESS;
726
727 RTListInit(&g_LstClientsNew);
728
729 rc = RTCritSectInit(&g_CritSectClients);
730 if (RT_SUCCESS(rc))
731 {
732 rc = RTPipeCreate(&g_hPipeR, &g_hPipeW, 0);
733 if (RT_SUCCESS(rc))
734 {
735 /* Spin off the thread serving connections. */
736 rc = RTThreadCreate(&g_hThreadServing, utsClientWorker, NULL, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
737 "USBTSTSRV");
738 if (RT_SUCCESS(rc))
739 return VINF_SUCCESS;
740 else
741 RTMsgError("Creating the client worker thread failed with %Rrc\n", rc);
742
743 RTPipeClose(g_hPipeR);
744 RTPipeClose(g_hPipeW);
745 }
746 else
747 RTMsgError("Creating communications pipe failed with %Rrc\n", rc);
748
749 RTCritSectDelete(&g_CritSectClients);
750 }
751 else
752 RTMsgError("Creating global critical section failed with %Rrc\n", rc);
753
754 return rc;
755}
756
757/**
758 * Determines the default configuration.
759 */
760static void utsSetDefaults(void)
761{
762 /*
763 * OS and ARCH.
764 */
765 AssertCompile(sizeof(KBUILD_TARGET) <= sizeof(g_szOsShortName));
766 strcpy(g_szOsShortName, KBUILD_TARGET);
767
768 AssertCompile(sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szArchShortName));
769 strcpy(g_szArchShortName, KBUILD_TARGET_ARCH);
770
771 AssertCompile(sizeof(KBUILD_TARGET) + sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szOsDotArchShortName));
772 strcpy(g_szOsDotArchShortName, KBUILD_TARGET);
773 g_szOsDotArchShortName[sizeof(KBUILD_TARGET) - 1] = '.';
774 strcpy(&g_szOsDotArchShortName[sizeof(KBUILD_TARGET)], KBUILD_TARGET_ARCH);
775
776 AssertCompile(sizeof(KBUILD_TARGET) + sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szOsSlashArchShortName));
777 strcpy(g_szOsSlashArchShortName, KBUILD_TARGET);
778 g_szOsSlashArchShortName[sizeof(KBUILD_TARGET) - 1] = '/';
779 strcpy(&g_szOsSlashArchShortName[sizeof(KBUILD_TARGET)], KBUILD_TARGET_ARCH);
780
781#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
782 strcpy(g_szExeSuff, ".exe");
783 strcpy(g_szScriptSuff, ".cmd");
784#else
785 strcpy(g_szExeSuff, "");
786 strcpy(g_szScriptSuff, ".sh");
787#endif
788
789 /*
790 * The CD/DVD-ROM location.
791 */
792 /** @todo do a better job here :-) */
793#ifdef RT_OS_WINDOWS
794 strcpy(g_szDefCdRomPath, "D:/");
795#elif defined(RT_OS_OS2)
796 strcpy(g_szDefCdRomPath, "D:/");
797#else
798 if (RTDirExists("/media"))
799 strcpy(g_szDefCdRomPath, "/media/cdrom");
800 else
801 strcpy(g_szDefCdRomPath, "/mnt/cdrom");
802#endif
803 strcpy(g_szCdRomPath, g_szDefCdRomPath);
804
805 /*
806 * Temporary directory.
807 */
808 int rc = RTPathTemp(g_szDefScratchPath, sizeof(g_szDefScratchPath));
809 if (RT_SUCCESS(rc))
810#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(RT_OS_DOS)
811 rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "uts-XXXX.tmp");
812#else
813 rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "uts-XXXXXXXXX.tmp");
814#endif
815 if (RT_FAILURE(rc))
816 {
817 RTMsgError("RTPathTemp/Append failed when constructing scratch path: %Rrc\n", rc);
818 strcpy(g_szDefScratchPath, "/tmp/uts-XXXX.tmp");
819 }
820 strcpy(g_szScratchPath, g_szDefScratchPath);
821
822 /*
823 * The default transporter is the first one.
824 */
825 g_pTransport = g_apTransports[0];
826}
827
828/**
829 * Prints the usage.
830 *
831 * @param pStrm Where to print it.
832 * @param pszArgv0 The program name (argv[0]).
833 */
834static void utsUsage(PRTSTREAM pStrm, const char *argv0)
835{
836 RTStrmPrintf(pStrm,
837 "Usage: %Rbn [options]\n"
838 "\n"
839 "Options:\n"
840 " --cdrom <path>\n"
841 " Where the CD/DVD-ROM will be mounted.\n"
842 " Default: %s\n"
843 " --scratch <path>\n"
844 " Where to put scratch files.\n"
845 " Default: %s \n"
846 ,
847 argv0,
848 g_szDefCdRomPath,
849 g_szDefScratchPath);
850 RTStrmPrintf(pStrm,
851 " --transport <name>\n"
852 " Use the specified transport layer, one of the following:\n");
853 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
854 RTStrmPrintf(pStrm, " %s - %s\n", g_apTransports[i]->szName, g_apTransports[i]->pszDesc);
855 RTStrmPrintf(pStrm, " Default: %s\n", g_pTransport->szName);
856 RTStrmPrintf(pStrm,
857 " --display-output, --no-display-output\n"
858 " Display the output and the result of all child processes.\n");
859 RTStrmPrintf(pStrm,
860 " --foreground\n"
861 " Don't daemonize, run in the foreground.\n");
862 RTStrmPrintf(pStrm,
863 " --help, -h, -?\n"
864 " Display this message and exit.\n"
865 " --version, -V\n"
866 " Display the version and exit.\n");
867
868 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
869 if (g_apTransports[i]->cOpts)
870 {
871 RTStrmPrintf(pStrm,
872 "\n"
873 "Options for %s:\n", g_apTransports[i]->szName);
874 g_apTransports[i]->pfnUsage(g_pStdOut);
875 }
876}
877
878/**
879 * Parses the arguments.
880 *
881 * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.
882 * @param argc The number of arguments.
883 * @param argv The argument vector.
884 * @param pfExit For indicating exit when the exit code is zero.
885 */
886static RTEXITCODE utsParseArgv(int argc, char **argv, bool *pfExit)
887{
888 *pfExit = false;
889
890 /*
891 * Storage for locally handled options.
892 */
893 bool fDaemonize = true;
894 bool fDaemonized = false;
895
896 /*
897 * Combine the base and transport layer option arrays.
898 */
899 static const RTGETOPTDEF s_aBaseOptions[] =
900 {
901 { "--transport", 't', RTGETOPT_REQ_STRING },
902 { "--cdrom", 'c', RTGETOPT_REQ_STRING },
903 { "--scratch", 's', RTGETOPT_REQ_STRING },
904 { "--display-output", 'd', RTGETOPT_REQ_NOTHING },
905 { "--no-display-output",'D', RTGETOPT_REQ_NOTHING },
906 { "--foreground", 'f', RTGETOPT_REQ_NOTHING },
907 { "--daemonized", 'Z', RTGETOPT_REQ_NOTHING },
908 };
909
910 size_t cOptions = RT_ELEMENTS(s_aBaseOptions);
911 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
912 cOptions += g_apTransports[i]->cOpts;
913
914 PRTGETOPTDEF paOptions = (PRTGETOPTDEF)alloca(cOptions * sizeof(RTGETOPTDEF));
915 if (!paOptions)
916 return RTMsgErrorExit(RTEXITCODE_FAILURE, "alloca failed\n");
917
918 memcpy(paOptions, s_aBaseOptions, sizeof(s_aBaseOptions));
919 cOptions = RT_ELEMENTS(s_aBaseOptions);
920 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
921 {
922 memcpy(&paOptions[cOptions], g_apTransports[i]->paOpts, g_apTransports[i]->cOpts * sizeof(RTGETOPTDEF));
923 cOptions += g_apTransports[i]->cOpts;
924 }
925
926 /*
927 * Parse the arguments.
928 */
929 RTGETOPTSTATE GetState;
930 int rc = RTGetOptInit(&GetState, argc, argv, paOptions, cOptions, 1, 0 /* fFlags */);
931 AssertRC(rc);
932
933 int ch;
934 RTGETOPTUNION Val;
935 while ((ch = RTGetOpt(&GetState, &Val)))
936 {
937 switch (ch)
938 {
939 case 'c':
940 rc = RTStrCopy(g_szCdRomPath, sizeof(g_szCdRomPath), Val.psz);
941 if (RT_FAILURE(rc))
942 return RTMsgErrorExit(RTEXITCODE_FAILURE, "CD/DVD-ROM is path too long (%Rrc)\n", rc);
943 break;
944
945 case 'd':
946 g_fDisplayOutput = true;
947 break;
948
949 case 'D':
950 g_fDisplayOutput = false;
951 break;
952
953 case 'f':
954 fDaemonize = false;
955 break;
956
957 case 'h':
958 utsUsage(g_pStdOut, argv[0]);
959 *pfExit = true;
960 return RTEXITCODE_SUCCESS;
961
962 case 's':
963 rc = RTStrCopy(g_szScratchPath, sizeof(g_szScratchPath), Val.psz);
964 if (RT_FAILURE(rc))
965 return RTMsgErrorExit(RTEXITCODE_FAILURE, "scratch path is too long (%Rrc)\n", rc);
966 break;
967
968 case 't':
969 {
970 PCUTSTRANSPORT pTransport = NULL;
971 for (size_t i = 0; RT_ELEMENTS(g_apTransports); i++)
972 if (!strcmp(g_apTransports[i]->szName, Val.psz))
973 {
974 pTransport = g_apTransports[i];
975 break;
976 }
977 if (!pTransport)
978 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown transport layer name '%s'\n", Val.psz);
979 g_pTransport = pTransport;
980 break;
981 }
982
983 case 'V':
984 RTPrintf("$Revision: 60279 $\n");
985 *pfExit = true;
986 return RTEXITCODE_SUCCESS;
987
988 case 'Z':
989 fDaemonized = true;
990 fDaemonize = false;
991 break;
992
993 default:
994 {
995 rc = VERR_TRY_AGAIN;
996 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
997 if (g_apTransports[i]->cOpts)
998 {
999 rc = g_apTransports[i]->pfnOption(ch, &Val);
1000 if (RT_SUCCESS(rc))
1001 break;
1002 if (rc != VERR_TRY_AGAIN)
1003 {
1004 *pfExit = true;
1005 return RTEXITCODE_SYNTAX;
1006 }
1007 }
1008 if (rc == VERR_TRY_AGAIN)
1009 {
1010 *pfExit = true;
1011 return RTGetOptPrintError(ch, &Val);
1012 }
1013 break;
1014 }
1015 }
1016 }
1017
1018 /*
1019 * Daemonize ourselves if asked to.
1020 */
1021 if (fDaemonize && !*pfExit)
1022 {
1023 rc = RTProcDaemonize(argv, "--daemonized");
1024 if (RT_FAILURE(rc))
1025 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize: %Rrc\n", rc);
1026 *pfExit = true;
1027 }
1028
1029 return RTEXITCODE_SUCCESS;
1030}
1031
1032
1033int main(int argc, char **argv)
1034{
1035 /*
1036 * Initialize the runtime.
1037 */
1038 int rc = RTR3InitExe(argc, &argv, 0);
1039 if (RT_FAILURE(rc))
1040 return RTMsgInitFailure(rc);
1041
1042 /*
1043 * Determine defaults and parse the arguments.
1044 */
1045 utsSetDefaults();
1046 bool fExit;
1047 RTEXITCODE rcExit = utsParseArgv(argc, argv, &fExit);
1048 if (rcExit != RTEXITCODE_SUCCESS || fExit)
1049 return rcExit;
1050
1051 /*
1052 * Initialize global state.
1053 */
1054 rc = utsInit();
1055 if (RT_FAILURE(rc))
1056 return RTEXITCODE_FAILURE;
1057
1058 /*
1059 * Initialize the transport layer.
1060 */
1061 rc = g_pTransport->pfnInit();
1062 if (RT_FAILURE(rc))
1063 return RTEXITCODE_FAILURE;
1064
1065 /*
1066 * Ok, start working
1067 */
1068 rcExit = utsMainLoop();
1069
1070 /*
1071 * Cleanup.
1072 */
1073 g_pTransport->pfnTerm();
1074
1075 return rcExit;
1076}
1077
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette