VirtualBox

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

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

ValidationKit/usb: Updates to the new USB test service, add configuration file parsing. Work in progress (Backup because switching to something else)

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