VirtualBox

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

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

ValidationKit/usb: More fixes for the server and client side

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

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