VirtualBox

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

Last change on this file since 77994 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.2 KB
Line 
1/* $Id: UsbTestService.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * UsbTestService - Remote USB test configuration and execution server.
4 */
5
6/*
7 * Copyright (C) 2010-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_DEFAULT
32#include <iprt/alloca.h>
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#include <iprt/critsect.h>
36#include <iprt/crc.h>
37#include <iprt/ctype.h>
38#include <iprt/dir.h>
39#include <iprt/env.h>
40#include <iprt/err.h>
41#include <iprt/getopt.h>
42#include <iprt/handle.h>
43#include <iprt/initterm.h>
44#include <iprt/json.h>
45#include <iprt/list.h>
46#include <iprt/log.h>
47#include <iprt/mem.h>
48#include <iprt/message.h>
49#include <iprt/param.h>
50#include <iprt/path.h>
51#include <iprt/pipe.h>
52#include <iprt/poll.h>
53#include <iprt/process.h>
54#include <iprt/stream.h>
55#include <iprt/string.h>
56#include <iprt/thread.h>
57
58#include "UsbTestServiceInternal.h"
59#include "UsbTestServiceGadget.h"
60#include "UsbTestServicePlatform.h"
61
62
63
64/*********************************************************************************************************************************
65* Structures and Typedefs *
66*********************************************************************************************************************************/
67
68#define UTS_USBIP_PORT_FIRST 3240
69#define UTS_USBIP_PORT_LAST 3340
70
71/**
72 * UTS client state.
73 */
74typedef enum UTSCLIENTSTATE
75{
76 /** Invalid client state. */
77 UTSCLIENTSTATE_INVALID = 0,
78 /** Client is initialising, only the HOWDY and BYE packets are allowed. */
79 UTSCLIENTSTATE_INITIALISING,
80 /** Client is in fully cuntional state and ready to process all requests. */
81 UTSCLIENTSTATE_READY,
82 /** Client is destroying. */
83 UTSCLIENTSTATE_DESTROYING,
84 /** 32bit hack. */
85 UTSCLIENTSTATE_32BIT_HACK = 0x7fffffff
86} UTSCLIENTSTATE;
87
88/**
89 * UTS client instance.
90 */
91typedef struct UTSCLIENT
92{
93 /** List node for new clients. */
94 RTLISTNODE NdLst;
95 /** The current client state. */
96 UTSCLIENTSTATE enmState;
97 /** Transport backend specific data. */
98 PUTSTRANSPORTCLIENT pTransportClient;
99 /** Client hostname. */
100 char *pszHostname;
101 /** Gadget host handle. */
102 UTSGADGETHOST hGadgetHost;
103 /** Handle fo the current configured gadget. */
104 UTSGADGET hGadget;
105} UTSCLIENT;
106/** Pointer to a UTS client instance. */
107typedef UTSCLIENT *PUTSCLIENT;
108
109
110/*********************************************************************************************************************************
111* Global Variables *
112*********************************************************************************************************************************/
113/**
114 * Transport layers.
115 */
116static const PCUTSTRANSPORT g_apTransports[] =
117{
118 &g_TcpTransport,
119 //&g_SerialTransport,
120 //&g_FileSysTransport,
121 //&g_GuestPropTransport,
122 //&g_TestDevTransport,
123};
124
125/** The select transport layer. */
126static PCUTSTRANSPORT g_pTransport;
127/** The config path. */
128static char g_szCfgPath[RTPATH_MAX];
129/** The scratch path. */
130static char g_szScratchPath[RTPATH_MAX];
131/** The default scratch path. */
132static char g_szDefScratchPath[RTPATH_MAX];
133/** The CD/DVD-ROM path. */
134static char g_szCdRomPath[RTPATH_MAX];
135/** The default CD/DVD-ROM path. */
136static char g_szDefCdRomPath[RTPATH_MAX];
137/** The operating system short name. */
138static char g_szOsShortName[16];
139/** The CPU architecture short name. */
140static char g_szArchShortName[16];
141/** The combined "OS.arch" name. */
142static char g_szOsDotArchShortName[32];
143/** The combined "OS/arch" name. */
144static char g_szOsSlashArchShortName[32];
145/** The executable suffix. */
146static char g_szExeSuff[8];
147/** The shell script suffix. */
148static char g_szScriptSuff[8];
149/** Whether to display the output of the child process or not. */
150static bool g_fDisplayOutput = true;
151/** Whether to terminate or not.
152 * @todo implement signals and stuff. */
153static bool volatile g_fTerminate = false;
154/** Configuration AST. */
155static RTJSONVAL g_hCfgJson = NIL_RTJSONVAL;
156/** Pipe for communicating with the serving thread about new clients. - read end */
157static RTPIPE g_hPipeR;
158/** Pipe for communicating with the serving thread about new clients. - write end */
159static RTPIPE g_hPipeW;
160/** Thread serving connected clients. */
161static RTTHREAD g_hThreadServing;
162/** Critical section protecting the list of new clients. */
163static RTCRITSECT g_CritSectClients;
164/** List of new clients waiting to be picked up by the client worker thread. */
165static RTLISTANCHOR g_LstClientsNew;
166/** First USB/IP port we can use. */
167static uint16_t g_uUsbIpPortFirst = UTS_USBIP_PORT_FIRST;
168/** Last USB/IP port we can use. */
169static uint16_t g_uUsbIpPortLast = UTS_USBIP_PORT_LAST;
170/** Next free port. */
171static uint16_t g_uUsbIpPortNext = UTS_USBIP_PORT_FIRST;
172
173
174
175/**
176 * Returns the string represenation of the given state.
177 */
178static const char *utsClientStateStringify(UTSCLIENTSTATE enmState)
179{
180 switch (enmState)
181 {
182 case UTSCLIENTSTATE_INVALID:
183 return "INVALID";
184 case UTSCLIENTSTATE_INITIALISING:
185 return "INITIALISING";
186 case UTSCLIENTSTATE_READY:
187 return "READY";
188 case UTSCLIENTSTATE_DESTROYING:
189 return "DESTROYING";
190 case UTSCLIENTSTATE_32BIT_HACK:
191 default:
192 break;
193 }
194
195 AssertMsgFailed(("Unknown state %#x\n", enmState));
196 return "UNKNOWN";
197}
198
199/**
200 * Calculates the checksum value, zero any padding space and send the packet.
201 *
202 * @returns IPRT status code.
203 * @param pClient The UTS client structure.
204 * @param pPkt The packet to send. Must point to a correctly
205 * aligned buffer.
206 */
207static int utsSendPkt(PUTSCLIENT pClient, PUTSPKTHDR pPkt)
208{
209 Assert(pPkt->cb >= sizeof(*pPkt));
210 pPkt->uCrc32 = RTCrc32(pPkt->achOpcode, pPkt->cb - RT_UOFFSETOF(UTSPKTHDR, achOpcode));
211 if (pPkt->cb != RT_ALIGN_32(pPkt->cb, UTSPKT_ALIGNMENT))
212 memset((uint8_t *)pPkt + pPkt->cb, '\0', RT_ALIGN_32(pPkt->cb, UTSPKT_ALIGNMENT) - pPkt->cb);
213
214 Log(("utsSendPkt: cb=%#x opcode=%.8s\n", pPkt->cb, pPkt->achOpcode));
215 Log2(("%.*Rhxd\n", RT_MIN(pPkt->cb, 256), pPkt));
216 int rc = g_pTransport->pfnSendPkt(pClient->pTransportClient, pPkt);
217 while (RT_UNLIKELY(rc == VERR_INTERRUPTED) && !g_fTerminate)
218 rc = g_pTransport->pfnSendPkt(pClient->pTransportClient, pPkt);
219 if (RT_FAILURE(rc))
220 Log(("utsSendPkt: rc=%Rrc\n", rc));
221
222 return rc;
223}
224
225/**
226 * Sends a babble reply and disconnects the client (if applicable).
227 *
228 * @param pClient The UTS client structure.
229 * @param pszOpcode The BABBLE opcode.
230 */
231static void utsReplyBabble(PUTSCLIENT pClient, const char *pszOpcode)
232{
233 UTSPKTHDR Reply;
234 Reply.cb = sizeof(Reply);
235 Reply.uCrc32 = 0;
236 memcpy(Reply.achOpcode, pszOpcode, sizeof(Reply.achOpcode));
237
238 g_pTransport->pfnBabble(pClient->pTransportClient, &Reply, 20*1000);
239}
240
241/**
242 * Receive and validate a packet.
243 *
244 * Will send bable responses to malformed packets that results in a error status
245 * code.
246 *
247 * @returns IPRT status code.
248 * @param pClient The UTS client structure.
249 * @param ppPktHdr Where to return the packet on success. Free
250 * with RTMemFree.
251 * @param fAutoRetryOnFailure Whether to retry on error.
252 */
253static int utsRecvPkt(PUTSCLIENT pClient, PPUTSPKTHDR ppPktHdr, bool fAutoRetryOnFailure)
254{
255 for (;;)
256 {
257 PUTSPKTHDR pPktHdr;
258 int rc = g_pTransport->pfnRecvPkt(pClient->pTransportClient, &pPktHdr);
259 if (RT_SUCCESS(rc))
260 {
261 /* validate the packet. */
262 if ( pPktHdr->cb >= sizeof(UTSPKTHDR)
263 && pPktHdr->cb < UTSPKT_MAX_SIZE)
264 {
265 Log2(("utsRecvPkt: pPktHdr=%p cb=%#x crc32=%#x opcode=%.8s\n"
266 "%.*Rhxd\n",
267 pPktHdr, pPktHdr->cb, pPktHdr->uCrc32, pPktHdr->achOpcode, RT_MIN(pPktHdr->cb, 256), pPktHdr));
268 uint32_t uCrc32Calc = pPktHdr->uCrc32 != 0
269 ? RTCrc32(&pPktHdr->achOpcode[0], pPktHdr->cb - RT_UOFFSETOF(UTSPKTHDR, achOpcode))
270 : 0;
271 if (pPktHdr->uCrc32 == uCrc32Calc)
272 {
273 AssertCompileMemberSize(UTSPKTHDR, achOpcode, 8);
274 if ( RT_C_IS_UPPER(pPktHdr->achOpcode[0])
275 && RT_C_IS_UPPER(pPktHdr->achOpcode[1])
276 && (RT_C_IS_UPPER(pPktHdr->achOpcode[2]) || pPktHdr->achOpcode[2] == ' ')
277 && (RT_C_IS_PRINT(pPktHdr->achOpcode[3]) || pPktHdr->achOpcode[3] == ' ')
278 && (RT_C_IS_PRINT(pPktHdr->achOpcode[4]) || pPktHdr->achOpcode[4] == ' ')
279 && (RT_C_IS_PRINT(pPktHdr->achOpcode[5]) || pPktHdr->achOpcode[5] == ' ')
280 && (RT_C_IS_PRINT(pPktHdr->achOpcode[6]) || pPktHdr->achOpcode[6] == ' ')
281 && (RT_C_IS_PRINT(pPktHdr->achOpcode[7]) || pPktHdr->achOpcode[7] == ' ')
282 )
283 {
284 Log(("utsRecvPkt: cb=%#x opcode=%.8s\n", pPktHdr->cb, pPktHdr->achOpcode));
285 *ppPktHdr = pPktHdr;
286 return rc;
287 }
288
289 rc = VERR_IO_BAD_COMMAND;
290 }
291 else
292 {
293 Log(("utsRecvPkt: cb=%#x opcode=%.8s crc32=%#x actual=%#x\n",
294 pPktHdr->cb, pPktHdr->achOpcode, pPktHdr->uCrc32, uCrc32Calc));
295 rc = VERR_IO_CRC;
296 }
297 }
298 else
299 rc = VERR_IO_BAD_LENGTH;
300
301 /* Send babble reply and disconnect the client if the transport is
302 connection oriented. */
303 if (rc == VERR_IO_BAD_LENGTH)
304 utsReplyBabble(pClient, "BABBLE L");
305 else if (rc == VERR_IO_CRC)
306 utsReplyBabble(pClient, "BABBLE C");
307 else if (rc == VERR_IO_BAD_COMMAND)
308 utsReplyBabble(pClient, "BABBLE O");
309 else
310 utsReplyBabble(pClient, "BABBLE ");
311 RTMemFree(pPktHdr);
312 }
313
314 /* Try again or return failure? */
315 if ( g_fTerminate
316 || rc != VERR_INTERRUPTED
317 || !fAutoRetryOnFailure
318 )
319 {
320 Log(("utsRecvPkt: rc=%Rrc\n", rc));
321 return rc;
322 }
323 }
324}
325
326/**
327 * Make a simple reply, only status opcode.
328 *
329 * @returns IPRT status code of the send.
330 * @param pClient The UTS client structure.
331 * @param pReply The reply packet.
332 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
333 * with space.
334 * @param cbExtra Bytes in addition to the header.
335 */
336static int utsReplyInternal(PUTSCLIENT pClient, PUTSPKTSTS pReply, const char *pszOpcode, size_t cbExtra)
337{
338 /* copy the opcode, don't be too strict in case of a padding screw up. */
339 size_t cchOpcode = strlen(pszOpcode);
340 if (RT_LIKELY(cchOpcode == sizeof(pReply->Hdr.achOpcode)))
341 memcpy(pReply->Hdr.achOpcode, pszOpcode, sizeof(pReply->Hdr.achOpcode));
342 else
343 {
344 Assert(cchOpcode == sizeof(pReply->Hdr.achOpcode));
345 while (cchOpcode > 0 && pszOpcode[cchOpcode - 1] == ' ')
346 cchOpcode--;
347 AssertMsgReturn(cchOpcode < sizeof(pReply->Hdr.achOpcode), ("%d/'%.8s'\n", cchOpcode, pszOpcode), VERR_INTERNAL_ERROR_4);
348 memcpy(pReply->Hdr.achOpcode, pszOpcode, cchOpcode);
349 memset(&pReply->Hdr.achOpcode[cchOpcode], ' ', sizeof(pReply->Hdr.achOpcode) - cchOpcode);
350 }
351
352 pReply->Hdr.cb = (uint32_t)sizeof(UTSPKTSTS) + (uint32_t)cbExtra;
353 pReply->Hdr.uCrc32 = 0;
354
355 return utsSendPkt(pClient, &pReply->Hdr);
356}
357
358/**
359 * Make a simple reply, only status opcode.
360 *
361 * @returns IPRT status code of the send.
362 * @param pClient The UTS client structure.
363 * @param pPktHdr The original packet (for future use).
364 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
365 * with space.
366 */
367static int utsReplySimple(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode)
368{
369 UTSPKTSTS Pkt;
370
371 RT_ZERO(Pkt);
372 Pkt.rcReq = VINF_SUCCESS;
373 Pkt.cchStsMsg = 0;
374 NOREF(pPktHdr);
375 return utsReplyInternal(pClient, &Pkt, pszOpcode, 0);
376}
377
378/**
379 * Acknowledges a packet with success.
380 *
381 * @returns IPRT status code of the send.
382 * @param pClient The UTS client structure.
383 * @param pPktHdr The original packet (for future use).
384 */
385static int utsReplyAck(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
386{
387 return utsReplySimple(pClient, pPktHdr, "ACK ");
388}
389
390/**
391 * Replies with a failure.
392 *
393 * @returns IPRT status code of the send.
394 * @param pClient The UTS client structure.
395 * @param pPktHdr The original packet (for future use).
396 * @param rcReq Status code.
397 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
398 * with space.
399 * @param rcReq The status code of the request.
400 * @param pszDetailFmt Longer description of the problem (format string).
401 * @param va Format arguments.
402 */
403static int utsReplyFailureV(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode, int rcReq, const char *pszDetailFmt, va_list va)
404{
405 NOREF(pPktHdr);
406 union
407 {
408 UTSPKTSTS Hdr;
409 char ach[256];
410 } uPkt;
411
412 RT_ZERO(uPkt);
413 size_t cchDetail = RTStrPrintfV(&uPkt.ach[sizeof(UTSPKTSTS)],
414 sizeof(uPkt) - sizeof(UTSPKTSTS),
415 pszDetailFmt, va);
416 uPkt.Hdr.rcReq = rcReq;
417 uPkt.Hdr.cchStsMsg = cchDetail;
418 return utsReplyInternal(pClient, &uPkt.Hdr, pszOpcode, cchDetail + 1);
419}
420
421/**
422 * Replies with a failure.
423 *
424 * @returns IPRT status code of the send.
425 * @param pClient The UTS client structure.
426 * @param pPktHdr The original packet (for future use).
427 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
428 * with space.
429 * @param rcReq Status code.
430 * @param pszDetailFmt Longer description of the problem (format string).
431 * @param ... Format arguments.
432 */
433static int utsReplyFailure(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode, int rcReq, const char *pszDetailFmt, ...)
434{
435 va_list va;
436 va_start(va, pszDetailFmt);
437 int rc = utsReplyFailureV(pClient, pPktHdr, pszOpcode, rcReq, pszDetailFmt, va);
438 va_end(va);
439 return rc;
440}
441
442/**
443 * Replies according to the return code.
444 *
445 * @returns IPRT status code of the send.
446 * @param pClient The UTS client structure.
447 * @param pPktHdr The packet to reply to.
448 * @param rcOperation The status code to report.
449 * @param pszOperationFmt The operation that failed. Typically giving the
450 * function call with important arguments.
451 * @param ... Arguments to the format string.
452 */
453static int utsReplyRC(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, int rcOperation, const char *pszOperationFmt, ...)
454{
455 if (RT_SUCCESS(rcOperation))
456 return utsReplyAck(pClient, pPktHdr);
457
458 char szOperation[128];
459 va_list va;
460 va_start(va, pszOperationFmt);
461 RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va);
462 va_end(va);
463
464 return utsReplyFailure(pClient, pPktHdr, "FAILED ", rcOperation, "%s failed with rc=%Rrc (opcode '%.8s')",
465 szOperation, rcOperation, pPktHdr->achOpcode);
466}
467
468#if 0 /* unused */
469/**
470 * Signal a bad packet minum size.
471 *
472 * @returns IPRT status code of the send.
473 * @param pClient The UTS client structure.
474 * @param pPktHdr The packet to reply to.
475 * @param cbMin The minimum size.
476 */
477static int utsReplyBadMinSize(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, size_t cbMin)
478{
479 return utsReplyFailure(pClient, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at least %zu bytes, got %u (opcode '%.8s')",
480 cbMin, pPktHdr->cb, pPktHdr->achOpcode);
481}
482#endif
483
484/**
485 * Signal a bad packet exact size.
486 *
487 * @returns IPRT status code of the send.
488 * @param pClient The UTS client structure.
489 * @param pPktHdr The packet to reply to.
490 * @param cb The wanted size.
491 */
492static int utsReplyBadSize(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, size_t cb)
493{
494 return utsReplyFailure(pClient, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at %zu bytes, got %u (opcode '%.8s')",
495 cb, pPktHdr->cb, pPktHdr->achOpcode);
496}
497
498#if 0 /* unused */
499/**
500 * Deals with a command that isn't implemented yet.
501 * @returns IPRT status code of the send.
502 * @param pClient The UTS client structure.
503 * @param pPktHdr The packet which opcode isn't implemented.
504 */
505static int utsReplyNotImplemented(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
506{
507 return utsReplyFailure(pClient, pPktHdr, "NOT IMPL", VERR_NOT_IMPLEMENTED, "Opcode '%.8s' is not implemented", pPktHdr->achOpcode);
508}
509#endif
510
511/**
512 * Deals with a unknown command.
513 * @returns IPRT status code of the send.
514 * @param pClient The UTS client structure.
515 * @param pPktHdr The packet to reply to.
516 */
517static int utsReplyUnknown(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
518{
519 return utsReplyFailure(pClient, pPktHdr, "UNKNOWN ", VERR_NOT_FOUND, "Opcode '%.8s' is not known", pPktHdr->achOpcode);
520}
521
522/**
523 * Deals with a command which contains an unterminated string.
524 *
525 * @returns IPRT status code of the send.
526 * @param pClient The UTS client structure.
527 * @param pPktHdr The packet containing the unterminated string.
528 */
529static int utsReplyBadStrTermination(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
530{
531 return utsReplyFailure(pClient, pPktHdr, "BAD TERM", VERR_INVALID_PARAMETER, "Opcode '%.8s' contains an unterminated string", pPktHdr->achOpcode);
532}
533
534/**
535 * Deals with a command sent in an invalid client state.
536 *
537 * @returns IPRT status code of the send.
538 * @param pClient The UTS client structure.
539 * @param pPktHdr The packet containing the unterminated string.
540 */
541static int utsReplyInvalidState(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
542{
543 return utsReplyFailure(pClient, pPktHdr, "INVSTATE", VERR_INVALID_STATE, "Opcode '%.8s' is not supported at client state '%s",
544 pPktHdr->achOpcode, utsClientStateStringify(pClient->enmState));
545}
546
547/**
548 * Parses an unsigned integer from the given value string.
549 *
550 * @returns IPRT status code.
551 * @retval VERR_OUT_OF_RANGE if the parsed value exceeds the given maximum.
552 * @param pszVal The value string.
553 * @param uMax The maximum value.
554 * @param pu64 Where to store the parsed number on success.
555 */
556static int utsDoGadgetCreateCfgParseUInt(const char *pszVal, uint64_t uMax, uint64_t *pu64)
557{
558 int rc = RTStrToUInt64Ex(pszVal, NULL, 0, pu64);
559 if (RT_SUCCESS(rc))
560 {
561 if (*pu64 > uMax)
562 rc = VERR_OUT_OF_RANGE;
563 }
564
565 return rc;
566}
567
568/**
569 * Parses a signed integer from the given value string.
570 *
571 * @returns IPRT status code.
572 * @retval VERR_OUT_OF_RANGE if the parsed value exceeds the given range.
573 * @param pszVal The value string.
574 * @param iMin The minimum value.
575 * @param iMax The maximum value.
576 * @param pi64 Where to store the parsed number on success.
577 */
578static int utsDoGadgetCreateCfgParseInt(const char *pszVal, int64_t iMin, int64_t iMax, int64_t *pi64)
579{
580 int rc = RTStrToInt64Ex(pszVal, NULL, 0, pi64);
581 if (RT_SUCCESS(rc))
582 {
583 if ( *pi64 < iMin
584 || *pi64 > iMax)
585 rc = VERR_OUT_OF_RANGE;
586 }
587
588 return rc;
589}
590
591/**
592 * Parses the given config item and fills in the value according to the given type.
593 *
594 * @returns IPRT status code.
595 * @param pCfgItem The config item to parse.
596 * @param u32Type The config type.
597 * @param pszVal The value encoded as a string.
598 */
599static int utsDoGadgetCreateCfgParseItem(PUTSGADGETCFGITEM pCfgItem, uint32_t u32Type,
600 const char *pszVal)
601{
602 int rc = VINF_SUCCESS;
603
604 switch (u32Type)
605 {
606 case UTSPKT_GDGT_CFG_ITEM_TYPE_BOOLEAN:
607 {
608 pCfgItem->Val.enmType = UTSGADGETCFGTYPE_BOOLEAN;
609 if ( RTStrICmp(pszVal, "enabled")
610 || RTStrICmp(pszVal, "1")
611 || RTStrICmp(pszVal, "true"))
612 pCfgItem->Val.u.f = true;
613 else if ( RTStrICmp(pszVal, "disabled")
614 || RTStrICmp(pszVal, "0")
615 || RTStrICmp(pszVal, "false"))
616 pCfgItem->Val.u.f = false;
617 else
618 rc = VERR_INVALID_PARAMETER;
619 break;
620 }
621 case UTSPKT_GDGT_CFG_ITEM_TYPE_STRING:
622 {
623 pCfgItem->Val.enmType = UTSGADGETCFGTYPE_STRING;
624 pCfgItem->Val.u.psz = RTStrDup(pszVal);
625 if (!pCfgItem->Val.u.psz)
626 rc = VERR_NO_STR_MEMORY;
627 break;
628 }
629 case UTSPKT_GDGT_CFG_ITEM_TYPE_UINT8:
630 {
631 pCfgItem->Val.enmType = UTSGADGETCFGTYPE_UINT8;
632
633 uint64_t u64;
634 rc = utsDoGadgetCreateCfgParseUInt(pszVal, UINT8_MAX, &u64);
635 if (RT_SUCCESS(rc))
636 pCfgItem->Val.u.u8 = (uint8_t)u64;
637 break;
638 }
639 case UTSPKT_GDGT_CFG_ITEM_TYPE_UINT16:
640 {
641 pCfgItem->Val.enmType = UTSGADGETCFGTYPE_UINT16;
642
643 uint64_t u64;
644 rc = utsDoGadgetCreateCfgParseUInt(pszVal, UINT16_MAX, &u64);
645 if (RT_SUCCESS(rc))
646 pCfgItem->Val.u.u16 = (uint16_t)u64;
647 break;
648 }
649 case UTSPKT_GDGT_CFG_ITEM_TYPE_UINT32:
650 {
651 pCfgItem->Val.enmType = UTSGADGETCFGTYPE_UINT32;
652
653 uint64_t u64;
654 rc = utsDoGadgetCreateCfgParseUInt(pszVal, UINT32_MAX, &u64);
655 if (RT_SUCCESS(rc))
656 pCfgItem->Val.u.u32 = (uint32_t)u64;
657 break;
658 }
659 case UTSPKT_GDGT_CFG_ITEM_TYPE_UINT64:
660 {
661 pCfgItem->Val.enmType = UTSGADGETCFGTYPE_UINT64;
662 rc = utsDoGadgetCreateCfgParseUInt(pszVal, UINT64_MAX, &pCfgItem->Val.u.u64);
663 break;
664 }
665 case UTSPKT_GDGT_CFG_ITEM_TYPE_INT8:
666 {
667 pCfgItem->Val.enmType = UTSGADGETCFGTYPE_INT8;
668
669 int64_t i64;
670 rc = utsDoGadgetCreateCfgParseInt(pszVal, INT8_MIN, INT8_MAX, &i64);
671 if (RT_SUCCESS(rc))
672 pCfgItem->Val.u.i8 = (int8_t)i64;
673 break;
674 }
675 case UTSPKT_GDGT_CFG_ITEM_TYPE_INT16:
676 {
677 pCfgItem->Val.enmType = UTSGADGETCFGTYPE_INT16;
678
679 int64_t i64;
680 rc = utsDoGadgetCreateCfgParseInt(pszVal, INT16_MIN, INT16_MAX, &i64);
681 if (RT_SUCCESS(rc))
682 pCfgItem->Val.u.i16 = (int16_t)i64;
683 break;
684 }
685 case UTSPKT_GDGT_CFG_ITEM_TYPE_INT32:
686 {
687 pCfgItem->Val.enmType = UTSGADGETCFGTYPE_INT32;
688
689 int64_t i64;
690 rc = utsDoGadgetCreateCfgParseInt(pszVal, INT32_MIN, INT32_MAX, &i64);
691 if (RT_SUCCESS(rc))
692 pCfgItem->Val.u.i32 = (int32_t)i64;
693 break;
694 }
695 case UTSPKT_GDGT_CFG_ITEM_TYPE_INT64:
696 {
697 pCfgItem->Val.enmType = UTSGADGETCFGTYPE_INT64;
698 rc = utsDoGadgetCreateCfgParseInt(pszVal, INT64_MIN, INT64_MAX, &pCfgItem->Val.u.i64);
699 break;
700 }
701 default:
702 rc = VERR_INVALID_PARAMETER;
703 }
704
705 return rc;
706}
707
708/**
709 * Creates the configuration from the given GADGET CREATE packet.
710 *
711 * @returns IPRT status code.
712 * @param pCfgItem The first config item header in the request packet.
713 * @param cCfgItems Number of config items in the packet to parse.
714 * @param cbPkt Number of bytes left in the packet for the config data.
715 * @param paCfg The array of configuration items to fill.
716 */
717static int utsDoGadgetCreateFillCfg(PUTSPKTREQGDGTCTORCFGITEM pCfgItem, unsigned cCfgItems,
718 size_t cbPkt, PUTSGADGETCFGITEM paCfg)
719{
720 int rc = VINF_SUCCESS;
721 unsigned idxCfg = 0;
722
723 while ( RT_SUCCESS(rc)
724 && cCfgItems
725 && cbPkt)
726 {
727 if (cbPkt >= sizeof(UTSPKTREQGDGTCTORCFGITEM))
728 {
729 cbPkt -= sizeof(UTSPKTREQGDGTCTORCFGITEM);
730 if (pCfgItem->u32KeySize + pCfgItem->u32ValSize >= cbPkt)
731 {
732 const char *pszKey = (const char *)(pCfgItem + 1);
733 const char *pszVal = pszKey + pCfgItem->u32KeySize;
734
735 /* Validate termination. */
736 if ( *(pszKey + pCfgItem->u32KeySize - 1) != '\0'
737 || *(pszVal + pCfgItem->u32ValSize - 1) != '\0')
738 rc = VERR_INVALID_PARAMETER;
739 else
740 {
741 paCfg[idxCfg].pszKey = RTStrDup(pszKey);
742
743 rc = utsDoGadgetCreateCfgParseItem(&paCfg[idxCfg], pCfgItem->u32Type, pszVal);
744 if (RT_SUCCESS(rc))
745 {
746 cbPkt -= pCfgItem->u32KeySize + pCfgItem->u32ValSize;
747 cCfgItems--;
748 idxCfg++;
749 pCfgItem = (PUTSPKTREQGDGTCTORCFGITEM)(pszVal + pCfgItem->u32ValSize);
750 }
751 }
752 }
753 else
754 rc = VERR_INVALID_PARAMETER;
755 }
756 else
757 rc = VERR_INVALID_PARAMETER;
758 }
759
760 return rc;
761}
762
763/**
764 * Verifies and acknowledges a "BYE" request.
765 *
766 * @returns IPRT status code.
767 * @param pClient The UTS client structure.
768 * @param pPktHdr The howdy packet.
769 */
770static int utsDoBye(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
771{
772 int rc;
773 if (pPktHdr->cb == sizeof(UTSPKTHDR))
774 rc = utsReplyAck(pClient, pPktHdr);
775 else
776 rc = utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTHDR));
777 return rc;
778}
779
780/**
781 * Verifies and acknowledges a "HOWDY" request.
782 *
783 * @returns IPRT status code.
784 * @param pClient The UTS client structure.
785 * @param pPktHdr The howdy packet.
786 */
787static int utsDoHowdy(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
788{
789 int rc = VINF_SUCCESS;
790
791 if (pPktHdr->cb != sizeof(UTSPKTREQHOWDY))
792 return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTREQHOWDY));
793
794 if (pClient->enmState != UTSCLIENTSTATE_INITIALISING)
795 return utsReplyInvalidState(pClient, pPktHdr);
796
797 PUTSPKTREQHOWDY pReq = (PUTSPKTREQHOWDY)pPktHdr;
798
799 if (pReq->uVersion != UTS_PROTOCOL_VS)
800 return utsReplyRC(pClient, pPktHdr, VERR_VERSION_MISMATCH, "The given version %#x is not supported", pReq->uVersion);
801
802 /* Verify hostname string. */
803 if (pReq->cchHostname >= sizeof(pReq->achHostname))
804 return utsReplyBadSize(pClient, pPktHdr, sizeof(pReq->achHostname) - 1);
805
806 if (pReq->achHostname[pReq->cchHostname] != '\0')
807 return utsReplyBadStrTermination(pClient, pPktHdr);
808
809 /* Extract string. */
810 pClient->pszHostname = RTStrDup(&pReq->achHostname[0]);
811 if (!pClient->pszHostname)
812 return utsReplyRC(pClient, pPktHdr, VERR_NO_MEMORY, "Failed to allocate memory for the hostname string");
813
814 if (pReq->fUsbConn & UTSPKT_HOWDY_CONN_F_PHYSICAL)
815 return utsReplyRC(pClient, pPktHdr, VERR_NOT_SUPPORTED, "Physical connections are not yet supported");
816
817 if (pReq->fUsbConn & UTSPKT_HOWDY_CONN_F_USBIP)
818 {
819 /* Set up the USB/IP server, find an unused port we can start the server on. */
820 UTSGADGETCFGITEM aCfg[2];
821
822 uint16_t uPort = g_uUsbIpPortNext;
823
824 if (g_uUsbIpPortNext == g_uUsbIpPortLast)
825 g_uUsbIpPortNext = g_uUsbIpPortFirst;
826 else
827 g_uUsbIpPortNext++;
828
829 aCfg[0].pszKey = "UsbIp/Port";
830 aCfg[0].Val.enmType = UTSGADGETCFGTYPE_UINT16;
831 aCfg[0].Val.u.u16 = uPort;
832 aCfg[1].pszKey = NULL;
833
834 rc = utsGadgetHostCreate(UTSGADGETHOSTTYPE_USBIP, &aCfg[0], &pClient->hGadgetHost);
835 if (RT_SUCCESS(rc))
836 {
837 /* Send the reply with the configured USB/IP port. */
838 UTSPKTREPHOWDY Rep;
839
840 RT_ZERO(Rep);
841
842 Rep.uVersion = UTS_PROTOCOL_VS;
843 Rep.fUsbConn = UTSPKT_HOWDY_CONN_F_USBIP;
844 Rep.uUsbIpPort = uPort;
845 Rep.cUsbIpDevices = 1;
846 Rep.cPhysicalDevices = 0;
847
848 rc = utsReplyInternal(pClient, &Rep.Sts, "ACK ", sizeof(Rep) - sizeof(UTSPKTSTS));
849 if (RT_SUCCESS(rc))
850 {
851 g_pTransport->pfnNotifyHowdy(pClient->pTransportClient);
852 pClient->enmState = UTSCLIENTSTATE_READY;
853 RTDirRemoveRecursive(g_szScratchPath, RTDIRRMREC_F_CONTENT_ONLY);
854 }
855 }
856 else
857 return utsReplyRC(pClient, pPktHdr, rc, "Creating the USB/IP gadget host failed");
858 }
859 else
860 return utsReplyRC(pClient, pPktHdr, VERR_INVALID_PARAMETER, "No access method requested");
861
862 return rc;
863}
864
865/**
866 * Verifies and processes a "GADGET CREATE" request.
867 *
868 * @returns IPRT status code.
869 * @param pClient The UTS client structure.
870 * @param pPktHdr The gadget create packet.
871 */
872static int utsDoGadgetCreate(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
873{
874 int rc = VINF_SUCCESS;
875
876 if (pPktHdr->cb < sizeof(UTSPKTREQGDGTCTOR))
877 return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTREQGDGTCTOR));
878
879 if ( pClient->enmState != UTSCLIENTSTATE_READY
880 || pClient->hGadgetHost == NIL_UTSGADGETHOST)
881 return utsReplyInvalidState(pClient, pPktHdr);
882
883 PUTSPKTREQGDGTCTOR pReq = (PUTSPKTREQGDGTCTOR)pPktHdr;
884
885 if (pReq->u32GdgtType != UTSPKT_GDGT_CREATE_TYPE_TEST)
886 return utsReplyRC(pClient, pPktHdr, VERR_INVALID_PARAMETER, "The given gadget type is not supported");
887
888 if (pReq->u32GdgtAccess != UTSPKT_GDGT_CREATE_ACCESS_USBIP)
889 return utsReplyRC(pClient, pPktHdr, VERR_INVALID_PARAMETER, "The given gadget access method is not supported");
890
891 PUTSGADGETCFGITEM paCfg = NULL;
892 if (pReq->u32CfgItems > 0)
893 {
894 paCfg = (PUTSGADGETCFGITEM)RTMemAllocZ((pReq->u32CfgItems + 1) * sizeof(UTSGADGETCFGITEM));
895 if (RT_UNLIKELY(!paCfg))
896 return utsReplyRC(pClient, pPktHdr, VERR_NO_MEMORY, "Failed to allocate memory for configration items");
897
898 rc = utsDoGadgetCreateFillCfg((PUTSPKTREQGDGTCTORCFGITEM)(pReq + 1), pReq->u32CfgItems,
899 pPktHdr->cb - sizeof(UTSPKTREQGDGTCTOR), paCfg);
900 if (RT_FAILURE(rc))
901 {
902 RTMemFree(paCfg);
903 return utsReplyRC(pClient, pPktHdr, rc, "Failed to parse configuration");
904 }
905 }
906
907 rc = utsGadgetCreate(pClient->hGadgetHost, UTSGADGETCLASS_TEST, paCfg, &pClient->hGadget);
908 if (RT_SUCCESS(rc))
909 {
910 UTSPKTREPGDGTCTOR Rep;
911 RT_ZERO(Rep);
912
913 Rep.idGadget = 0;
914 Rep.u32BusId = utsGadgetGetBusId(pClient->hGadget);
915 Rep.u32DevId = utsGadgetGetDevId(pClient->hGadget);
916 rc = utsReplyInternal(pClient, &Rep.Sts, "ACK ", sizeof(Rep) - sizeof(UTSPKTSTS));
917 }
918 else
919 rc = utsReplyRC(pClient, pPktHdr, rc, "Failed to create gadget with %Rrc\n", rc);
920
921 return rc;
922}
923
924/**
925 * Verifies and processes a "GADGET DESTROY" request.
926 *
927 * @returns IPRT status code.
928 * @param pClient The UTS client structure.
929 * @param pPktHdr The gadget destroy packet.
930 */
931static int utsDoGadgetDestroy(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
932{
933 if (pPktHdr->cb != sizeof(UTSPKTREQGDGTDTOR))
934 return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTREQGDGTDTOR));
935
936 if ( pClient->enmState != UTSCLIENTSTATE_READY
937 || pClient->hGadgetHost == NIL_UTSGADGETHOST)
938 return utsReplyInvalidState(pClient, pPktHdr);
939
940 PUTSPKTREQGDGTDTOR pReq = (PUTSPKTREQGDGTDTOR)pPktHdr;
941
942 if (pReq->idGadget != 0)
943 return utsReplyRC(pClient, pPktHdr, VERR_INVALID_HANDLE, "The given gadget handle is invalid");
944 if (pClient->hGadget == NIL_UTSGADGET)
945 return utsReplyRC(pClient, pPktHdr, VERR_INVALID_STATE, "The gadget is not set up");
946
947 utsGadgetRelease(pClient->hGadget);
948 pClient->hGadget = NIL_UTSGADGET;
949
950 return utsReplyAck(pClient, pPktHdr);
951}
952
953/**
954 * Verifies and processes a "GADGET CONNECT" request.
955 *
956 * @returns IPRT status code.
957 * @param pClient The UTS client structure.
958 * @param pPktHdr The gadget connect packet.
959 */
960static int utsDoGadgetConnect(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
961{
962 if (pPktHdr->cb != sizeof(UTSPKTREQGDGTCNCT))
963 return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTREQGDGTCNCT));
964
965 if ( pClient->enmState != UTSCLIENTSTATE_READY
966 || pClient->hGadgetHost == NIL_UTSGADGETHOST)
967 return utsReplyInvalidState(pClient, pPktHdr);
968
969 PUTSPKTREQGDGTCNCT pReq = (PUTSPKTREQGDGTCNCT)pPktHdr;
970
971 if (pReq->idGadget != 0)
972 return utsReplyRC(pClient, pPktHdr, VERR_INVALID_HANDLE, "The given gadget handle is invalid");
973 if (pClient->hGadget == NIL_UTSGADGET)
974 return utsReplyRC(pClient, pPktHdr, VERR_INVALID_STATE, "The gadget is not set up");
975
976 int rc = utsGadgetConnect(pClient->hGadget);
977 if (RT_SUCCESS(rc))
978 rc = utsReplyAck(pClient, pPktHdr);
979 else
980 rc = utsReplyRC(pClient, pPktHdr, rc, "Failed to connect the gadget");
981
982 return rc;
983}
984
985/**
986 * Verifies and processes a "GADGET DISCONNECT" request.
987 *
988 * @returns IPRT status code.
989 * @param pClient The UTS client structure.
990 * @param pPktHdr The gadget disconnect packet.
991 */
992static int utsDoGadgetDisconnect(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
993{
994 if (pPktHdr->cb != sizeof(UTSPKTREQGDGTDCNT))
995 return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTREQGDGTDCNT));
996
997 if ( pClient->enmState != UTSCLIENTSTATE_READY
998 || pClient->hGadgetHost == NIL_UTSGADGETHOST)
999 return utsReplyInvalidState(pClient, pPktHdr);
1000
1001 PUTSPKTREQGDGTDCNT pReq = (PUTSPKTREQGDGTDCNT)pPktHdr;
1002
1003 if (pReq->idGadget != 0)
1004 return utsReplyRC(pClient, pPktHdr, VERR_INVALID_HANDLE, "The given gadget handle is invalid");
1005 if (pClient->hGadget == NIL_UTSGADGET)
1006 return utsReplyRC(pClient, pPktHdr, VERR_INVALID_STATE, "The gadget is not set up");
1007
1008 int rc = utsGadgetDisconnect(pClient->hGadget);
1009 if (RT_SUCCESS(rc))
1010 rc = utsReplyAck(pClient, pPktHdr);
1011 else
1012 rc = utsReplyRC(pClient, pPktHdr, rc, "Failed to disconnect the gadget");
1013
1014 return rc;
1015}
1016
1017/**
1018 * Main request processing routine for each client.
1019 *
1020 * @returns IPRT status code.
1021 * @param pClient The UTS client structure sending the request.
1022 */
1023static int utsClientReqProcess(PUTSCLIENT pClient)
1024{
1025 /*
1026 * Read client command packet and process it.
1027 */
1028 PUTSPKTHDR pPktHdr = NULL;
1029 int rc = utsRecvPkt(pClient, &pPktHdr, true /*fAutoRetryOnFailure*/);
1030 if (RT_FAILURE(rc))
1031 return rc;
1032
1033 /*
1034 * Do a string switch on the opcode bit.
1035 */
1036 /* Connection: */
1037 if ( utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_HOWDY))
1038 rc = utsDoHowdy(pClient, pPktHdr);
1039 else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_BYE))
1040 rc = utsDoBye(pClient, pPktHdr);
1041 /* Gadget API. */
1042 else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_GADGET_CREATE))
1043 rc = utsDoGadgetCreate(pClient, pPktHdr);
1044 else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_GADGET_DESTROY))
1045 rc = utsDoGadgetDestroy(pClient, pPktHdr);
1046 else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_GADGET_CONNECT))
1047 rc = utsDoGadgetConnect(pClient, pPktHdr);
1048 else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_GADGET_DISCONNECT))
1049 rc = utsDoGadgetDisconnect(pClient, pPktHdr);
1050 /* Misc: */
1051 else
1052 rc = utsReplyUnknown(pClient, pPktHdr);
1053
1054 RTMemFree(pPktHdr);
1055
1056 return rc;
1057}
1058
1059/**
1060 * Destroys a client instance.
1061 *
1062 * @returns nothing.
1063 * @param pClient The UTS client structure.
1064 */
1065static void utsClientDestroy(PUTSCLIENT pClient)
1066{
1067 if (pClient->pszHostname)
1068 RTStrFree(pClient->pszHostname);
1069 if (pClient->hGadget != NIL_UTSGADGET)
1070 utsGadgetRelease(pClient->hGadget);
1071 if (pClient->hGadgetHost != NIL_UTSGADGETHOST)
1072 utsGadgetHostRelease(pClient->hGadgetHost);
1073 RTMemFree(pClient);
1074}
1075
1076/**
1077 * The main thread worker serving the clients.
1078 */
1079static DECLCALLBACK(int) utsClientWorker(RTTHREAD hThread, void *pvUser)
1080{
1081 RT_NOREF2(hThread, pvUser);
1082 unsigned cClientsMax = 0;
1083 unsigned cClientsCur = 0;
1084 PUTSCLIENT *papClients = NULL;
1085 RTPOLLSET hPollSet;
1086
1087 int rc = RTPollSetCreate(&hPollSet);
1088 if (RT_FAILURE(rc))
1089 return rc;
1090
1091 /* Add the pipe to the poll set. */
1092 rc = RTPollSetAddPipe(hPollSet, g_hPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 0);
1093 if (RT_SUCCESS(rc))
1094 {
1095 while (!g_fTerminate)
1096 {
1097 uint32_t fEvts;
1098 uint32_t uId;
1099 rc = RTPoll(hPollSet, RT_INDEFINITE_WAIT, &fEvts, &uId);
1100 if (RT_SUCCESS(rc))
1101 {
1102 if (uId == 0)
1103 {
1104 if (fEvts & RTPOLL_EVT_ERROR)
1105 break;
1106
1107 /* We got woken up because of a new client. */
1108 Assert(fEvts & RTPOLL_EVT_READ);
1109
1110 uint8_t bRead;
1111 size_t cbRead = 0;
1112 rc = RTPipeRead(g_hPipeR, &bRead, 1, &cbRead);
1113 AssertRC(rc);
1114
1115 RTCritSectEnter(&g_CritSectClients);
1116 /* Walk the list and add all new clients. */
1117 PUTSCLIENT pIt, pItNext;
1118 RTListForEachSafe(&g_LstClientsNew, pIt, pItNext, UTSCLIENT, NdLst)
1119 {
1120 RTListNodeRemove(&pIt->NdLst);
1121 Assert(cClientsCur <= cClientsMax);
1122 if (cClientsCur == cClientsMax)
1123 {
1124 /* Realloc to accommodate for the new clients. */
1125 PUTSCLIENT *papClientsNew = (PUTSCLIENT *)RTMemRealloc(papClients, (cClientsMax + 10) * sizeof(PUTSCLIENT));
1126 if (RT_LIKELY(papClientsNew))
1127 {
1128 cClientsMax += 10;
1129 papClients = papClientsNew;
1130 }
1131 }
1132
1133 if (cClientsCur < cClientsMax)
1134 {
1135 /* Find a free slot in the client array. */
1136 unsigned idxSlt = 0;
1137 while ( idxSlt < cClientsMax
1138 && papClients[idxSlt] != NULL)
1139 idxSlt++;
1140
1141 rc = g_pTransport->pfnPollSetAdd(hPollSet, pIt->pTransportClient, idxSlt + 1);
1142 if (RT_SUCCESS(rc))
1143 {
1144 cClientsCur++;
1145 papClients[idxSlt] = pIt;
1146 }
1147 else
1148 {
1149 g_pTransport->pfnNotifyBye(pIt->pTransportClient);
1150 utsClientDestroy(pIt);
1151 }
1152 }
1153 else
1154 {
1155 g_pTransport->pfnNotifyBye(pIt->pTransportClient);
1156 utsClientDestroy(pIt);
1157 }
1158 }
1159 RTCritSectLeave(&g_CritSectClients);
1160 }
1161 else
1162 {
1163 /* Client sends a request, pick the right client and process it. */
1164 PUTSCLIENT pClient = papClients[uId - 1];
1165 AssertPtr(pClient);
1166 if (fEvts & RTPOLL_EVT_READ)
1167 rc = utsClientReqProcess(pClient);
1168
1169 if ( (fEvts & RTPOLL_EVT_ERROR)
1170 || RT_FAILURE(rc))
1171 {
1172 /* Close connection and remove client from array. */
1173 rc = g_pTransport->pfnPollSetRemove(hPollSet, pClient->pTransportClient, uId);
1174 AssertRC(rc);
1175
1176 g_pTransport->pfnNotifyBye(pClient->pTransportClient);
1177 papClients[uId - 1] = NULL;
1178 cClientsCur--;
1179 utsClientDestroy(pClient);
1180 }
1181 }
1182 }
1183 }
1184 }
1185
1186 RTPollSetDestroy(hPollSet);
1187
1188 return rc;
1189}
1190
1191/**
1192 * The main loop.
1193 *
1194 * @returns exit code.
1195 */
1196static RTEXITCODE utsMainLoop(void)
1197{
1198 RTEXITCODE enmExitCode = RTEXITCODE_SUCCESS;
1199 while (!g_fTerminate)
1200 {
1201 /*
1202 * Wait for new connection and spin off a new thread
1203 * for every new client.
1204 */
1205 PUTSTRANSPORTCLIENT pTransportClient;
1206 int rc = g_pTransport->pfnWaitForConnect(&pTransportClient);
1207 if (RT_FAILURE(rc))
1208 continue;
1209
1210 /*
1211 * New connection, create new client structure and spin of
1212 * the request handling thread.
1213 */
1214 PUTSCLIENT pClient = (PUTSCLIENT)RTMemAllocZ(sizeof(UTSCLIENT));
1215 if (RT_LIKELY(pClient))
1216 {
1217 pClient->enmState = UTSCLIENTSTATE_INITIALISING;
1218 pClient->pTransportClient = pTransportClient;
1219 pClient->pszHostname = NULL;
1220 pClient->hGadgetHost = NIL_UTSGADGETHOST;
1221 pClient->hGadget = NIL_UTSGADGET;
1222
1223 /* Add client to the new list and inform the worker thread. */
1224 RTCritSectEnter(&g_CritSectClients);
1225 RTListAppend(&g_LstClientsNew, &pClient->NdLst);
1226 RTCritSectLeave(&g_CritSectClients);
1227
1228 size_t cbWritten = 0;
1229 rc = RTPipeWrite(g_hPipeW, "", 1, &cbWritten);
1230 if (RT_FAILURE(rc))
1231 RTMsgError("Failed to inform worker thread of a new client");
1232 }
1233 else
1234 {
1235 RTMsgError("Creating new client structure failed with out of memory error\n");
1236 g_pTransport->pfnNotifyBye(pTransportClient);
1237 }
1238
1239
1240 }
1241
1242 return enmExitCode;
1243}
1244
1245/**
1246 * Initializes the global UTS state.
1247 *
1248 * @returns IPRT status code.
1249 */
1250static int utsInit(void)
1251{
1252 int rc = VINF_SUCCESS;
1253 PRTERRINFO pErrInfo = NULL;
1254
1255 RTListInit(&g_LstClientsNew);
1256
1257 rc = RTJsonParseFromFile(&g_hCfgJson, g_szCfgPath, pErrInfo);
1258 if (RT_SUCCESS(rc))
1259 {
1260 rc = utsPlatformInit();
1261 if (RT_SUCCESS(rc))
1262 {
1263 rc = RTCritSectInit(&g_CritSectClients);
1264 if (RT_SUCCESS(rc))
1265 {
1266 rc = RTPipeCreate(&g_hPipeR, &g_hPipeW, 0);
1267 if (RT_SUCCESS(rc))
1268 {
1269 /* Spin off the thread serving connections. */
1270 rc = RTThreadCreate(&g_hThreadServing, utsClientWorker, NULL, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
1271 "USBTSTSRV");
1272 if (RT_SUCCESS(rc))
1273 return VINF_SUCCESS;
1274 else
1275 RTMsgError("Creating the client worker thread failed with %Rrc\n", rc);
1276
1277 RTPipeClose(g_hPipeR);
1278 RTPipeClose(g_hPipeW);
1279 }
1280 else
1281 RTMsgError("Creating communications pipe failed with %Rrc\n", rc);
1282
1283 RTCritSectDelete(&g_CritSectClients);
1284 }
1285 else
1286 RTMsgError("Creating global critical section failed with %Rrc\n", rc);
1287
1288 RTJsonValueRelease(g_hCfgJson);
1289 }
1290 else
1291 RTMsgError("Initializing the platform failed with %Rrc\n", rc);
1292 }
1293 else
1294 {
1295 if (RTErrInfoIsSet(pErrInfo))
1296 {
1297 RTMsgError("Failed to parse config with detailed error: %s (%Rrc)\n",
1298 pErrInfo->pszMsg, pErrInfo->rc);
1299 RTErrInfoFree(pErrInfo);
1300 }
1301 else
1302 RTMsgError("Failed to parse config with unknown error (%Rrc)\n", rc);
1303 }
1304
1305 return rc;
1306}
1307
1308/**
1309 * Determines the default configuration.
1310 */
1311static void utsSetDefaults(void)
1312{
1313 /*
1314 * OS and ARCH.
1315 */
1316 AssertCompile(sizeof(KBUILD_TARGET) <= sizeof(g_szOsShortName));
1317 strcpy(g_szOsShortName, KBUILD_TARGET);
1318
1319 AssertCompile(sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szArchShortName));
1320 strcpy(g_szArchShortName, KBUILD_TARGET_ARCH);
1321
1322 AssertCompile(sizeof(KBUILD_TARGET) + sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szOsDotArchShortName));
1323 strcpy(g_szOsDotArchShortName, KBUILD_TARGET);
1324 g_szOsDotArchShortName[sizeof(KBUILD_TARGET) - 1] = '.';
1325 strcpy(&g_szOsDotArchShortName[sizeof(KBUILD_TARGET)], KBUILD_TARGET_ARCH);
1326
1327 AssertCompile(sizeof(KBUILD_TARGET) + sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szOsSlashArchShortName));
1328 strcpy(g_szOsSlashArchShortName, KBUILD_TARGET);
1329 g_szOsSlashArchShortName[sizeof(KBUILD_TARGET) - 1] = '/';
1330 strcpy(&g_szOsSlashArchShortName[sizeof(KBUILD_TARGET)], KBUILD_TARGET_ARCH);
1331
1332#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1333 strcpy(g_szExeSuff, ".exe");
1334 strcpy(g_szScriptSuff, ".cmd");
1335#else
1336 strcpy(g_szExeSuff, "");
1337 strcpy(g_szScriptSuff, ".sh");
1338#endif
1339
1340 /*
1341 * The CD/DVD-ROM location.
1342 */
1343 /** @todo do a better job here :-) */
1344#ifdef RT_OS_WINDOWS
1345 strcpy(g_szDefCdRomPath, "D:/");
1346#elif defined(RT_OS_OS2)
1347 strcpy(g_szDefCdRomPath, "D:/");
1348#else
1349 if (RTDirExists("/media"))
1350 strcpy(g_szDefCdRomPath, "/media/cdrom");
1351 else
1352 strcpy(g_szDefCdRomPath, "/mnt/cdrom");
1353#endif
1354 strcpy(g_szCdRomPath, g_szDefCdRomPath);
1355
1356 /*
1357 * Temporary directory.
1358 */
1359 int rc = RTPathTemp(g_szDefScratchPath, sizeof(g_szDefScratchPath));
1360 if (RT_SUCCESS(rc))
1361#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(RT_OS_DOS)
1362 rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "uts-XXXX.tmp");
1363#else
1364 rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "uts-XXXXXXXXX.tmp");
1365#endif
1366 if (RT_FAILURE(rc))
1367 {
1368 RTMsgError("RTPathTemp/Append failed when constructing scratch path: %Rrc\n", rc);
1369 strcpy(g_szDefScratchPath, "/tmp/uts-XXXX.tmp");
1370 }
1371 strcpy(g_szScratchPath, g_szDefScratchPath);
1372
1373 /*
1374 * Config file location.
1375 */
1376 /** @todo Improve */
1377#if !defined(RT_OS_WINDOWS)
1378 strcpy(g_szCfgPath, "/etc/uts.conf");
1379#else
1380 strcpy(g_szCfgPath, "");
1381#endif
1382
1383 /*
1384 * The default transporter is the first one.
1385 */
1386 g_pTransport = g_apTransports[0];
1387}
1388
1389/**
1390 * Prints the usage.
1391 *
1392 * @param pStrm Where to print it.
1393 * @param pszArgv0 The program name (argv[0]).
1394 */
1395static void utsUsage(PRTSTREAM pStrm, const char *pszArgv0)
1396{
1397 RTStrmPrintf(pStrm,
1398 "Usage: %Rbn [options]\n"
1399 "\n"
1400 "Options:\n"
1401 " --config <path>\n"
1402 " Where to load the config from\n"
1403 " --cdrom <path>\n"
1404 " Where the CD/DVD-ROM will be mounted.\n"
1405 " Default: %s\n"
1406 " --scratch <path>\n"
1407 " Where to put scratch files.\n"
1408 " Default: %s \n"
1409 ,
1410 pszArgv0,
1411 g_szDefCdRomPath,
1412 g_szDefScratchPath);
1413 RTStrmPrintf(pStrm,
1414 " --transport <name>\n"
1415 " Use the specified transport layer, one of the following:\n");
1416 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
1417 RTStrmPrintf(pStrm, " %s - %s\n", g_apTransports[i]->szName, g_apTransports[i]->pszDesc);
1418 RTStrmPrintf(pStrm, " Default: %s\n", g_pTransport->szName);
1419 RTStrmPrintf(pStrm,
1420 " --display-output, --no-display-output\n"
1421 " Display the output and the result of all child processes.\n");
1422 RTStrmPrintf(pStrm,
1423 " --foreground\n"
1424 " Don't daemonize, run in the foreground.\n");
1425 RTStrmPrintf(pStrm,
1426 " --help, -h, -?\n"
1427 " Display this message and exit.\n"
1428 " --version, -V\n"
1429 " Display the version and exit.\n");
1430
1431 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
1432 if (g_apTransports[i]->cOpts)
1433 {
1434 RTStrmPrintf(pStrm,
1435 "\n"
1436 "Options for %s:\n", g_apTransports[i]->szName);
1437 g_apTransports[i]->pfnUsage(g_pStdOut);
1438 }
1439}
1440
1441/**
1442 * Parses the arguments.
1443 *
1444 * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.
1445 * @param argc The number of arguments.
1446 * @param argv The argument vector.
1447 * @param pfExit For indicating exit when the exit code is zero.
1448 */
1449static RTEXITCODE utsParseArgv(int argc, char **argv, bool *pfExit)
1450{
1451 *pfExit = false;
1452
1453 /*
1454 * Storage for locally handled options.
1455 */
1456 bool fDaemonize = true;
1457 bool fDaemonized = false;
1458
1459 /*
1460 * Combine the base and transport layer option arrays.
1461 */
1462 static const RTGETOPTDEF s_aBaseOptions[] =
1463 {
1464 { "--config", 'C', RTGETOPT_REQ_STRING },
1465 { "--transport", 't', RTGETOPT_REQ_STRING },
1466 { "--cdrom", 'c', RTGETOPT_REQ_STRING },
1467 { "--scratch", 's', RTGETOPT_REQ_STRING },
1468 { "--display-output", 'd', RTGETOPT_REQ_NOTHING },
1469 { "--no-display-output",'D', RTGETOPT_REQ_NOTHING },
1470 { "--foreground", 'f', RTGETOPT_REQ_NOTHING },
1471 { "--daemonized", 'Z', RTGETOPT_REQ_NOTHING },
1472 };
1473
1474 size_t cOptions = RT_ELEMENTS(s_aBaseOptions);
1475 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
1476 cOptions += g_apTransports[i]->cOpts;
1477
1478 PRTGETOPTDEF paOptions = (PRTGETOPTDEF)alloca(cOptions * sizeof(RTGETOPTDEF));
1479 if (!paOptions)
1480 return RTMsgErrorExit(RTEXITCODE_FAILURE, "alloca failed\n");
1481
1482 memcpy(paOptions, s_aBaseOptions, sizeof(s_aBaseOptions));
1483 cOptions = RT_ELEMENTS(s_aBaseOptions);
1484 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
1485 {
1486 memcpy(&paOptions[cOptions], g_apTransports[i]->paOpts, g_apTransports[i]->cOpts * sizeof(RTGETOPTDEF));
1487 cOptions += g_apTransports[i]->cOpts;
1488 }
1489
1490 /*
1491 * Parse the arguments.
1492 */
1493 RTGETOPTSTATE GetState;
1494 int rc = RTGetOptInit(&GetState, argc, argv, paOptions, cOptions, 1, 0 /* fFlags */);
1495 AssertRC(rc);
1496
1497 int ch;
1498 RTGETOPTUNION Val;
1499 while ((ch = RTGetOpt(&GetState, &Val)))
1500 {
1501 switch (ch)
1502 {
1503 case 'C':
1504 rc = RTStrCopy(g_szCfgPath, sizeof(g_szCfgPath), Val.psz);
1505 if (RT_FAILURE(rc))
1506 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Config file path is path too long (%Rrc)\n", rc);
1507 break;
1508
1509 case 'c':
1510 rc = RTStrCopy(g_szCdRomPath, sizeof(g_szCdRomPath), Val.psz);
1511 if (RT_FAILURE(rc))
1512 return RTMsgErrorExit(RTEXITCODE_FAILURE, "CD/DVD-ROM is path too long (%Rrc)\n", rc);
1513 break;
1514
1515 case 'd':
1516 g_fDisplayOutput = true;
1517 break;
1518
1519 case 'D':
1520 g_fDisplayOutput = false;
1521 break;
1522
1523 case 'f':
1524 fDaemonize = false;
1525 break;
1526
1527 case 'h':
1528 utsUsage(g_pStdOut, argv[0]);
1529 *pfExit = true;
1530 return RTEXITCODE_SUCCESS;
1531
1532 case 's':
1533 rc = RTStrCopy(g_szScratchPath, sizeof(g_szScratchPath), Val.psz);
1534 if (RT_FAILURE(rc))
1535 return RTMsgErrorExit(RTEXITCODE_FAILURE, "scratch path is too long (%Rrc)\n", rc);
1536 break;
1537
1538 case 't':
1539 {
1540 PCUTSTRANSPORT pTransport = NULL;
1541 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
1542 if (!strcmp(g_apTransports[i]->szName, Val.psz))
1543 {
1544 pTransport = g_apTransports[i];
1545 break;
1546 }
1547 if (!pTransport)
1548 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown transport layer name '%s'\n", Val.psz);
1549 g_pTransport = pTransport;
1550 break;
1551 }
1552
1553 case 'V':
1554 RTPrintf("$Revision: 76553 $\n");
1555 *pfExit = true;
1556 return RTEXITCODE_SUCCESS;
1557
1558 case 'Z':
1559 fDaemonized = true;
1560 fDaemonize = false;
1561 break;
1562
1563 default:
1564 {
1565 rc = VERR_TRY_AGAIN;
1566 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
1567 if (g_apTransports[i]->cOpts)
1568 {
1569 rc = g_apTransports[i]->pfnOption(ch, &Val);
1570 if (RT_SUCCESS(rc))
1571 break;
1572 if (rc != VERR_TRY_AGAIN)
1573 {
1574 *pfExit = true;
1575 return RTEXITCODE_SYNTAX;
1576 }
1577 }
1578 if (rc == VERR_TRY_AGAIN)
1579 {
1580 *pfExit = true;
1581 return RTGetOptPrintError(ch, &Val);
1582 }
1583 break;
1584 }
1585 }
1586 }
1587
1588 /*
1589 * Daemonize ourselves if asked to.
1590 */
1591 if (fDaemonize && !*pfExit)
1592 {
1593 rc = RTProcDaemonize(argv, "--daemonized");
1594 if (RT_FAILURE(rc))
1595 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize: %Rrc\n", rc);
1596 *pfExit = true;
1597 }
1598
1599 return RTEXITCODE_SUCCESS;
1600}
1601
1602
1603int main(int argc, char **argv)
1604{
1605 /*
1606 * Initialize the runtime.
1607 */
1608 int rc = RTR3InitExe(argc, &argv, 0);
1609 if (RT_FAILURE(rc))
1610 return RTMsgInitFailure(rc);
1611
1612 /*
1613 * Determine defaults and parse the arguments.
1614 */
1615 utsSetDefaults();
1616 bool fExit;
1617 RTEXITCODE rcExit = utsParseArgv(argc, argv, &fExit);
1618 if (rcExit != RTEXITCODE_SUCCESS || fExit)
1619 return rcExit;
1620
1621 /*
1622 * Initialize global state.
1623 */
1624 rc = utsInit();
1625 if (RT_FAILURE(rc))
1626 return RTEXITCODE_FAILURE;
1627
1628 /*
1629 * Initialize the transport layer.
1630 */
1631 rc = g_pTransport->pfnInit();
1632 if (RT_FAILURE(rc))
1633 return RTEXITCODE_FAILURE;
1634
1635 /*
1636 * Ok, start working
1637 */
1638 rcExit = utsMainLoop();
1639
1640 /*
1641 * Cleanup.
1642 */
1643 g_pTransport->pfnTerm();
1644
1645 utsPlatformTerm();
1646
1647 return rcExit;
1648}
1649
Note: See TracBrowser for help on using the repository browser.

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