VirtualBox

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

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

shut up gcc

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.2 KB
Line 
1/* $Id: UsbTestService.cpp 60280 2016-03-31 18:59:42Z 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 = NULL;
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: 60280 $\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