VirtualBox

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

Last change on this file since 105374 was 100207, checked in by vboxsync, 18 months ago

ValidationKit/UsbTestService: Fix harmless memory leak, bugref:3409

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