Changeset 60279 in vbox for trunk/src/VBox/ValidationKit
- Timestamp:
- Mar 31, 2016 6:57:37 PM (9 years ago)
- Location:
- trunk/src/VBox/ValidationKit/utils/usb
- Files:
-
- 2 added
- 1 edited
- 2 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ValidationKit/utils/usb/Makefile.kmk
r56295 r60279 31 31 # USB Linux test frontend. 32 32 # 33 PROGRAMS += UsbTest 34 UsbTest_TEMPLATE = VBoxValidationKitR3 35 UsbTest_SOURCES = UsbTest.cpp 33 ifeq ($(KBUILD_TARGET),linux) 34 PROGRAMS += UsbTest 35 UsbTest_TEMPLATE = VBoxValidationKitR3 36 UsbTest_SOURCES = UsbTest.cpp 37 endif 38 39 PROGRAMS += UsbTestService 40 UsbTestService_TEMPLATE = VBoxValidationKitR3 41 UsbTestService_DEFS = \ 42 KBUILD_TARGET=\"$(KBUILD_TARGET)\" \ 43 KBUILD_TARGET_ARCH=\"$(KBUILD_TARGET_ARCH)\" 44 UsbTestService_SOURCES = \ 45 UsbTestService.cpp \ 46 UsbTestServiceTcp.cpp 36 47 37 48 $(evalcall def_vbox_validationkit_process_python_sources) -
trunk/src/VBox/ValidationKit/utils/usb/UsbTestService.cpp
r60266 r60279 1 1 /* $Id$ */ 2 2 /** @file 3 * TestExecServ - Basic Remote Execution Service.3 * UsbTestService - Remote USB test configuration and execution server. 4 4 */ 5 5 6 6 /* 7 * Copyright (C) 2010-201 5Oracle Corporation7 * Copyright (C) 2010-2016 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 33 33 #include <iprt/asm.h> 34 34 #include <iprt/assert.h> 35 #include <iprt/cdrom.h>36 35 #include <iprt/critsect.h> 37 36 #include <iprt/crc.h> … … 40 39 #include <iprt/env.h> 41 40 #include <iprt/err.h> 42 #include <iprt/file.h>43 41 #include <iprt/getopt.h> 44 42 #include <iprt/handle.h> 45 43 #include <iprt/initterm.h> 44 #include <iprt/list.h> 46 45 #include <iprt/log.h> 47 46 #include <iprt/mem.h> … … 54 53 #include <iprt/stream.h> 55 54 #include <iprt/string.h> 56 #include <iprt/system.h>57 55 #include <iprt/thread.h> 58 #include <iprt/time.h> 59 #include <iprt/uuid.h> 60 61 #ifndef RT_OS_WINDOWS 62 # include <iprt/zip.h> 63 #endif 64 65 #include "TestExecServiceInternal.h" 56 57 #include "UsbTestServiceInternal.h" 66 58 67 59 … … 70 62 * Structures and Typedefs * 71 63 *********************************************************************************************************************************/ 72 /** 73 * Handle IDs used by txsDoExec for the poll set. 74 */ 75 typedef enum TXSEXECHNDID 76 { 77 TXSEXECHNDID_STDIN = 0, 78 TXSEXECHNDID_STDOUT, 79 TXSEXECHNDID_STDERR, 80 TXSEXECHNDID_TESTPIPE, 81 TXSEXECHNDID_STDIN_WRITABLE, 82 TXSEXECHNDID_TRANSPORT, 83 TXSEXECHNDID_THREAD 84 } TXSEXECHNDID; 85 86 87 /** 88 * For buffering process input supplied by the client. 89 */ 90 typedef struct TXSEXECSTDINBUF 91 { 92 /** The mount of buffered data. */ 93 size_t cb; 94 /** The current data offset. */ 95 size_t off; 96 /** The data buffer. */ 97 char *pch; 98 /** The amount of allocated buffer space. */ 99 size_t cbAllocated; 100 /** Send further input into the bit bucket (stdin is dead). */ 101 bool fBitBucket; 102 /** The CRC-32 for standard input (received part). */ 103 uint32_t uCrc32; 104 } TXSEXECSTDINBUF; 105 /** Pointer to a standard input buffer. */ 106 typedef TXSEXECSTDINBUF *PTXSEXECSTDINBUF; 107 108 /** 109 * TXS child process info. 110 */ 111 typedef struct TXSEXEC 112 { 113 PCTXSPKTHDR pPktHdr; 114 RTMSINTERVAL cMsTimeout; 115 int rcReplySend; 116 117 RTPOLLSET hPollSet; 118 RTPIPE hStdInW; 119 RTPIPE hStdOutR; 120 RTPIPE hStdErrR; 121 RTPIPE hTestPipeR; 122 RTPIPE hWakeUpPipeR; 123 RTTHREAD hThreadWaiter; 124 125 /** @name For the setup phase 126 * @{ */ 127 struct StdPipe 128 { 129 RTHANDLE hChild; 130 PRTHANDLE phChild; 131 } StdIn, 132 StdOut, 133 StdErr; 134 RTPIPE hTestPipeW; 135 RTENV hEnv; 136 /** @} */ 137 138 /** For serializating some access. */ 139 RTCRITSECT CritSect; 140 /** @name Members protected by the critical section. 141 * @{ */ 142 RTPROCESS hProcess; 143 /** The process status. Only valid when fProcessAlive is cleared. */ 144 RTPROCSTATUS ProcessStatus; 145 /** Set when the process is alive, clear when dead. */ 146 bool volatile fProcessAlive; 147 /** The end of the pipe that hThreadWaiter writes to. */ 148 RTPIPE hWakeUpPipeW; 149 /** @} */ 150 } TXSEXEC; 151 /** Pointer to a the TXS child process info. */ 152 typedef TXSEXEC *PTXSEXEC; 153 64 65 /** 66 * UTS client instance. 67 */ 68 typedef struct UTSCLIENT 69 { 70 /** List node for new clients. */ 71 RTLISTNODE NdLst; 72 /** Transport backend specific data. */ 73 PUTSTRANSPORTCLIENT pTransportClient; 74 } UTSCLIENT; 75 /** Pointer to a UTS client instance. */ 76 typedef UTSCLIENT *PUTSCLIENT; 154 77 155 78 /********************************************************************************************************************************* … … 159 82 * Transport layers. 160 83 */ 161 static const PC TXSTRANSPORT g_apTransports[] =84 static const PCUTSTRANSPORT g_apTransports[] = 162 85 { 163 86 &g_TcpTransport, … … 169 92 170 93 /** The select transport layer. */ 171 static PC TXSTRANSPORT g_pTransport;94 static PCUTSTRANSPORT g_pTransport; 172 95 /** The scratch path. */ 173 96 static char g_szScratchPath[RTPATH_MAX]; … … 190 113 /** The shell script suffix. */ 191 114 static char g_szScriptSuff[8]; 192 /** UUID identifying this TXS instance. This can be used to see if TXS193 * has been restarted or not. */194 static RTUUID g_InstanceUuid;195 115 /** Whether to display the output of the child process or not. */ 196 116 static bool g_fDisplayOutput = true; … … 198 118 * @todo implement signals and stuff. */ 199 119 static bool volatile g_fTerminate = false; 120 /** Pipe for communicating with the serving thread about new clients. - read end */ 121 static RTPIPE g_hPipeR; 122 /** Pipe for communicating with the serving thread about new clients. - write end */ 123 static RTPIPE g_hPipeW; 124 /** Thread serving connected clients. */ 125 static RTTHREAD g_hThreadServing; 126 /** Critical section protecting the list of new clients. */ 127 static RTCRITSECT g_CritSectClients; 128 /** List of new clients waiting to be picked up by the client worker thread. */ 129 static RTLISTANCHOR g_LstClientsNew; 130 200 131 201 132 /** … … 203 134 * 204 135 * @returns IPRT status code. 136 * @param pClient The UTS client structure. 205 137 * @param pPkt The packet to send. Must point to a correctly 206 138 * aligned buffer. 207 139 */ 208 static int txsSendPkt(PTXSPKTHDR pPkt)140 static int utsSendPkt(PUTSCLIENT pClient, PUTSPKTHDR pPkt) 209 141 { 210 142 Assert(pPkt->cb >= sizeof(*pPkt)); 211 pPkt->uCrc32 = RTCrc32(pPkt->achOpcode, pPkt->cb - RT_OFFSETOF( TXSPKTHDR, achOpcode));212 if (pPkt->cb != RT_ALIGN_32(pPkt->cb, TXSPKT_ALIGNMENT))213 memset((uint8_t *)pPkt + pPkt->cb, '\0', RT_ALIGN_32(pPkt->cb, TXSPKT_ALIGNMENT) - pPkt->cb);214 215 Log((" txsSendPkt: cb=%#x opcode=%.8s\n", pPkt->cb, pPkt->achOpcode));143 pPkt->uCrc32 = RTCrc32(pPkt->achOpcode, pPkt->cb - RT_OFFSETOF(UTSPKTHDR, achOpcode)); 144 if (pPkt->cb != RT_ALIGN_32(pPkt->cb, UTSPKT_ALIGNMENT)) 145 memset((uint8_t *)pPkt + pPkt->cb, '\0', RT_ALIGN_32(pPkt->cb, UTSPKT_ALIGNMENT) - pPkt->cb); 146 147 Log(("utsSendPkt: cb=%#x opcode=%.8s\n", pPkt->cb, pPkt->achOpcode)); 216 148 Log2(("%.*Rhxd\n", RT_MIN(pPkt->cb, 256), pPkt)); 217 int rc = g_pTransport->pfnSendPkt(p Pkt);149 int rc = g_pTransport->pfnSendPkt(pClient->pTransportClient, pPkt); 218 150 while (RT_UNLIKELY(rc == VERR_INTERRUPTED) && !g_fTerminate) 219 rc = g_pTransport->pfnSendPkt(p Pkt);151 rc = g_pTransport->pfnSendPkt(pClient->pTransportClient, pPkt); 220 152 if (RT_FAILURE(rc)) 221 Log((" txsSendPkt: rc=%Rrc\n", rc));153 Log(("utsSendPkt: rc=%Rrc\n", rc)); 222 154 223 155 return rc; … … 227 159 * Sends a babble reply and disconnects the client (if applicable). 228 160 * 161 * @param pClient The UTS client structure. 229 162 * @param pszOpcode The BABBLE opcode. 230 163 */ 231 static void txsReplyBabble(const char *pszOpcode)232 { 233 TXSPKTHDR Reply;164 static void utsReplyBabble(PUTSCLIENT pClient, const char *pszOpcode) 165 { 166 UTSPKTHDR Reply; 234 167 Reply.cb = sizeof(Reply); 235 168 Reply.uCrc32 = 0; 236 169 memcpy(Reply.achOpcode, pszOpcode, sizeof(Reply.achOpcode)); 237 170 238 g_pTransport->pfnBabble( &Reply, 20*1000);171 g_pTransport->pfnBabble(pClient->pTransportClient, &Reply, 20*1000); 239 172 } 240 173 … … 246 179 * 247 180 * @returns IPRT status code. 181 * @param pClient The UTS client structure. 248 182 * @param ppPktHdr Where to return the packet on success. Free 249 183 * with RTMemFree. 250 184 * @param fAutoRetryOnFailure Whether to retry on error. 251 185 */ 252 static int txsRecvPkt(PPTXSPKTHDR ppPktHdr, bool fAutoRetryOnFailure)186 static int utsRecvPkt(PUTSCLIENT pClient, PPUTSPKTHDR ppPktHdr, bool fAutoRetryOnFailure) 253 187 { 254 188 for (;;) 255 189 { 256 P TXSPKTHDR pPktHdr;257 int rc = g_pTransport->pfnRecvPkt( &pPktHdr);190 PUTSPKTHDR pPktHdr; 191 int rc = g_pTransport->pfnRecvPkt(pClient->pTransportClient, &pPktHdr); 258 192 if (RT_SUCCESS(rc)) 259 193 { 260 194 /* validate the packet. */ 261 if ( pPktHdr->cb >= sizeof( TXSPKTHDR)262 && pPktHdr->cb < TXSPKT_MAX_SIZE)195 if ( pPktHdr->cb >= sizeof(UTSPKTHDR) 196 && pPktHdr->cb < UTSPKT_MAX_SIZE) 263 197 { 264 Log2((" txsRecvPkt: pPktHdr=%p cb=%#x crc32=%#x opcode=%.8s\n"198 Log2(("utsRecvPkt: pPktHdr=%p cb=%#x crc32=%#x opcode=%.8s\n" 265 199 "%.*Rhxd\n", 266 200 pPktHdr, pPktHdr->cb, pPktHdr->uCrc32, pPktHdr->achOpcode, RT_MIN(pPktHdr->cb, 256), pPktHdr)); 267 201 uint32_t uCrc32Calc = pPktHdr->uCrc32 != 0 268 ? RTCrc32(&pPktHdr->achOpcode[0], pPktHdr->cb - RT_OFFSETOF( TXSPKTHDR, achOpcode))202 ? RTCrc32(&pPktHdr->achOpcode[0], pPktHdr->cb - RT_OFFSETOF(UTSPKTHDR, achOpcode)) 269 203 : 0; 270 204 if (pPktHdr->uCrc32 == uCrc32Calc) 271 205 { 272 AssertCompileMemberSize( TXSPKTHDR, achOpcode, 8);206 AssertCompileMemberSize(UTSPKTHDR, achOpcode, 8); 273 207 if ( RT_C_IS_UPPER(pPktHdr->achOpcode[0]) 274 208 && RT_C_IS_UPPER(pPktHdr->achOpcode[1]) … … 281 215 ) 282 216 { 283 Log((" txsRecvPkt: cb=%#x opcode=%.8s\n", pPktHdr->cb, pPktHdr->achOpcode));217 Log(("utsRecvPkt: cb=%#x opcode=%.8s\n", pPktHdr->cb, pPktHdr->achOpcode)); 284 218 *ppPktHdr = pPktHdr; 285 219 return rc; … … 290 224 else 291 225 { 292 Log((" txsRecvPkt: cb=%#x opcode=%.8s crc32=%#x actual=%#x\n",226 Log(("utsRecvPkt: cb=%#x opcode=%.8s crc32=%#x actual=%#x\n", 293 227 pPktHdr->cb, pPktHdr->achOpcode, pPktHdr->uCrc32, uCrc32Calc)); 294 228 rc = VERR_IO_CRC; … … 301 235 connection oriented. */ 302 236 if (rc == VERR_IO_BAD_LENGTH) 303 txsReplyBabble("BABBLE L");237 utsReplyBabble(pClient, "BABBLE L"); 304 238 else if (rc == VERR_IO_CRC) 305 txsReplyBabble("BABBLE C");239 utsReplyBabble(pClient, "BABBLE C"); 306 240 else if (rc == VERR_IO_BAD_COMMAND) 307 txsReplyBabble("BABBLE O");241 utsReplyBabble(pClient, "BABBLE O"); 308 242 else 309 txsReplyBabble("BABBLE ");243 utsReplyBabble(pClient, "BABBLE "); 310 244 RTMemFree(pPktHdr); 311 245 } … … 317 251 ) 318 252 { 319 Log((" txsRecvPkt: rc=%Rrc\n", rc));253 Log(("utsRecvPkt: rc=%Rrc\n", rc)); 320 254 return rc; 321 255 } … … 327 261 * 328 262 * @returns IPRT status code of the send. 263 * @param pClient The UTS client structure. 329 264 * @param pReply The reply packet. 330 265 * @param pszOpcode The status opcode. Exactly 8 chars long, padd … … 332 267 * @param cbExtra Bytes in addition to the header. 333 268 */ 334 static int txsReplyInternal(PTXSPKTHDRpReply, const char *pszOpcode, size_t cbExtra)269 static int utsReplyInternal(PUTSCLIENT pClient, PUTSPKTSTS pReply, const char *pszOpcode, size_t cbExtra) 335 270 { 336 271 /* copy the opcode, don't be too strict in case of a padding screw up. */ 337 272 size_t cchOpcode = strlen(pszOpcode); 338 if (RT_LIKELY(cchOpcode == sizeof(pReply-> achOpcode)))339 memcpy(pReply-> achOpcode, pszOpcode, sizeof(pReply->achOpcode));273 if (RT_LIKELY(cchOpcode == sizeof(pReply->Hdr.achOpcode))) 274 memcpy(pReply->Hdr.achOpcode, pszOpcode, sizeof(pReply->Hdr.achOpcode)); 340 275 else 341 276 { 342 Assert(cchOpcode == sizeof(pReply-> achOpcode));277 Assert(cchOpcode == sizeof(pReply->Hdr.achOpcode)); 343 278 while (cchOpcode > 0 && pszOpcode[cchOpcode - 1] == ' ') 344 279 cchOpcode--; 345 AssertMsgReturn(cchOpcode < sizeof(pReply-> achOpcode), ("%d/'%.8s'\n", cchOpcode, pszOpcode), VERR_INTERNAL_ERROR_4);346 memcpy(pReply-> achOpcode, pszOpcode, cchOpcode);347 memset(&pReply-> achOpcode[cchOpcode], ' ', sizeof(pReply->achOpcode) - cchOpcode);348 } 349 350 pReply-> cb = (uint32_t)sizeof(TXSPKTHDR) + (uint32_t)cbExtra;351 pReply-> uCrc32 = 0;352 353 return txsSendPkt(pReply);280 AssertMsgReturn(cchOpcode < sizeof(pReply->Hdr.achOpcode), ("%d/'%.8s'\n", cchOpcode, pszOpcode), VERR_INTERNAL_ERROR_4); 281 memcpy(pReply->Hdr.achOpcode, pszOpcode, cchOpcode); 282 memset(&pReply->Hdr.achOpcode[cchOpcode], ' ', sizeof(pReply->Hdr.achOpcode) - cchOpcode); 283 } 284 285 pReply->Hdr.cb = (uint32_t)sizeof(UTSPKTSTS) + (uint32_t)cbExtra; 286 pReply->Hdr.uCrc32 = 0; 287 288 return utsSendPkt(pClient, &pReply->Hdr); 354 289 } 355 290 … … 358 293 * 359 294 * @returns IPRT status code of the send. 295 * @param pClient The UTS client structure. 360 296 * @param pPktHdr The original packet (for future use). 361 297 * @param pszOpcode The status opcode. Exactly 8 chars long, padd 362 298 * with space. 363 299 */ 364 static int txsReplySimple(PCTXSPKTHDR pPktHdr, const char *pszOpcode) 365 { 366 TXSPKTHDR Pkt; 300 static int utsReplySimple(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode) 301 { 302 UTSPKTSTS Pkt; 303 304 RT_ZERO(Pkt); 305 Pkt.rcReq = VINF_SUCCESS; 306 Pkt.cchStsMsg = 0; 367 307 NOREF(pPktHdr); 368 return txsReplyInternal(&Pkt, pszOpcode, 0);308 return utsReplyInternal(pClient, &Pkt, pszOpcode, 0); 369 309 } 370 310 … … 373 313 * 374 314 * @returns IPRT status code of the send. 315 * @param pClient The UTS client structure. 375 316 * @param pPktHdr The original packet (for future use). 376 317 */ 377 static int txsReplyAck(PCTXSPKTHDR pPktHdr)378 { 379 return txsReplySimple(pPktHdr, "ACK ");318 static int utsReplyAck(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr) 319 { 320 return utsReplySimple(pClient, pPktHdr, "ACK "); 380 321 } 381 322 … … 384 325 * 385 326 * @returns IPRT status code of the send. 327 * @param pClient The UTS client structure. 386 328 * @param pPktHdr The original packet (for future use). 329 * @param rcReq Status code. 387 330 * @param pszOpcode The status opcode. Exactly 8 chars long, padd 388 331 * with space. … … 391 334 * @param va Format arguments. 392 335 */ 393 static int txsReplyFailureV(PCTXSPKTHDR pPktHdr, const char *pszOpcode, const char *pszDetailFmt, va_list va)336 static int utsReplyFailureV(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode, int rcReq, const char *pszDetailFmt, va_list va) 394 337 { 395 338 NOREF(pPktHdr); 396 339 union 397 340 { 398 TXSPKTHDRHdr;341 UTSPKTSTS Hdr; 399 342 char ach[256]; 400 343 } uPkt; 401 344 402 size_t cchDetail = RTStrPrintfV(&uPkt.ach[sizeof(TXSPKTHDR)], 403 sizeof(uPkt) - sizeof(TXSPKTHDR), 345 RT_ZERO(uPkt); 346 size_t cchDetail = RTStrPrintfV(&uPkt.ach[sizeof(UTSPKTSTS)], 347 sizeof(uPkt) - sizeof(UTSPKTSTS), 404 348 pszDetailFmt, va); 405 return txsReplyInternal(&uPkt.Hdr, pszOpcode, cchDetail + 1); 349 uPkt.Hdr.rcReq = rcReq; 350 uPkt.Hdr.cchStsMsg = cchDetail; 351 return utsReplyInternal(pClient, &uPkt.Hdr, pszOpcode, cchDetail + 1); 406 352 } 407 353 … … 410 356 * 411 357 * @returns IPRT status code of the send. 358 * @param pClient The UTS client structure. 412 359 * @param pPktHdr The original packet (for future use). 360 * @param rcReq Status code. 413 361 * @param pszOpcode The status opcode. Exactly 8 chars long, padd 414 362 * with space. … … 417 365 * @param ... Format arguments. 418 366 */ 419 static int txsReplyFailure(PCTXSPKTHDR pPktHdr, const char *pszOpcode, const char *pszDetailFmt, ...)367 static int utsReplyFailure(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode, int rcReq, const char *pszDetailFmt, ...) 420 368 { 421 369 va_list va; 422 370 va_start(va, pszDetailFmt); 423 int rc = txsReplyFailureV(pPktHdr, pszOpcode, pszDetailFmt, va);371 int rc = utsReplyFailureV(pClient, pPktHdr, pszOpcode, rcReq, pszDetailFmt, va); 424 372 va_end(va); 425 373 return rc; … … 430 378 * 431 379 * @returns IPRT status code of the send. 380 * @param pClient The UTS client structure. 432 381 * @param pPktHdr The packet to reply to. 433 382 * @param rcOperation The status code to report. … … 436 385 * @param ... Arguments to the format string. 437 386 */ 438 static int txsReplyRC(PCTXSPKTHDR pPktHdr, int rcOperation, const char *pszOperationFmt, ...)387 static int utsReplyRC(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, int rcOperation, const char *pszOperationFmt, ...) 439 388 { 440 389 if (RT_SUCCESS(rcOperation)) 441 return txsReplyAck(pPktHdr);390 return utsReplyAck(pClient, pPktHdr); 442 391 443 392 char szOperation[128]; … … 447 396 va_end(va); 448 397 449 return txsReplyFailure(pPktHdr, "FAILED ", "%s failed with rc=%Rrc (opcode '%.8s')",450 szOperation, rcOperation, pPktHdr->achOpcode);398 return utsReplyFailure(pClient, pPktHdr, "FAILED ", rcOperation, "%s failed with rc=%Rrc (opcode '%.8s')", 399 rcOperation, szOperation, rcOperation, pPktHdr->achOpcode); 451 400 } 452 401 … … 455 404 * 456 405 * @returns IPRT status code of the send. 406 * @param pClient The UTS client structure. 457 407 * @param pPktHdr The packet to reply to. 458 408 * @param cbMin The minimum size. 459 409 */ 460 static int txsReplyBadMinSize(PCTXSPKTHDR pPktHdr, size_t cbMin)461 { 462 return txsReplyFailure(pPktHdr, "BAD SIZE", "Expected at least %zu bytes, got %u (opcode '%.8s')",410 static int utsReplyBadMinSize(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, size_t cbMin) 411 { 412 return utsReplyFailure(pClient, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at least %zu bytes, got %u (opcode '%.8s')", 463 413 cbMin, pPktHdr->cb, pPktHdr->achOpcode); 464 414 } … … 468 418 * 469 419 * @returns IPRT status code of the send. 420 * @param pClient The UTS client structure. 470 421 * @param pPktHdr The packet to reply to. 471 422 * @param cb The wanted size. 472 423 */ 473 static int txsReplyBadSize(PCTXSPKTHDR pPktHdr, size_t cb)474 { 475 return txsReplyFailure(pPktHdr, "BAD SIZE", "Expected at %zu bytes, got %u (opcode '%.8s')",424 static int utsReplyBadSize(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, size_t cb) 425 { 426 return utsReplyFailure(pClient, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at %zu bytes, got %u (opcode '%.8s')", 476 427 cb, pPktHdr->cb, pPktHdr->achOpcode); 477 428 } … … 480 431 * Deals with a command that isn't implemented yet. 481 432 * @returns IPRT status code of the send. 433 * @param pClient The UTS client structure. 482 434 * @param pPktHdr The packet which opcode isn't implemented. 483 435 */ 484 static int txsReplyNotImplemented(PCTXSPKTHDR pPktHdr)485 { 486 return txsReplyFailure(pPktHdr, "NOT IMPL", "Opcode '%.8s' is not implemented", pPktHdr->achOpcode);436 static int utsReplyNotImplemented(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr) 437 { 438 return utsReplyFailure(pClient, pPktHdr, "NOT IMPL", VERR_NOT_IMPLEMENTED, "Opcode '%.8s' is not implemented", pPktHdr->achOpcode); 487 439 } 488 440 … … 490 442 * Deals with a unknown command. 491 443 * @returns IPRT status code of the send. 444 * @param pClient The UTS client structure. 492 445 * @param pPktHdr The packet to reply to. 493 446 */ 494 static int txsReplyUnknown(PCTXSPKTHDR pPktHdr) 495 { 496 return txsReplyFailure(pPktHdr, "UNKNOWN ", "Opcode '%.8s' is not known", pPktHdr->achOpcode); 497 } 498 499 /** 500 * Replaces a variable with its value. 501 * 502 * @returns VINF_SUCCESS or VERR_NO_STR_MEMORY. 503 * @param ppszNew In/Out. 504 * @param pcchNew In/Out. (Messed up on failure.) 505 * @param offVar Variable offset. 506 * @param cchVar Variable length. 507 * @param pszValue The value. 508 * @param cchValue Value length. 509 */ 510 static int txsReplaceStringVariable(char **ppszNew, size_t *pcchNew, size_t offVar, size_t cchVar, 511 const char *pszValue, size_t cchValue) 512 { 513 size_t const cchAfter = *pcchNew - offVar - cchVar; 514 if (cchVar < cchValue) 515 { 516 *pcchNew += cchValue - cchVar; 517 int rc = RTStrRealloc(ppszNew, *pcchNew + 1); 518 if (RT_FAILURE(rc)) 519 return rc; 520 } 521 522 char *pszNew = *ppszNew; 523 memmove(&pszNew[offVar + cchValue], &pszNew[offVar + cchVar], cchAfter + 1); 524 memcpy(&pszNew[offVar], pszValue, cchValue); 525 return VINF_SUCCESS; 526 } 527 528 /** 529 * Replace the variables found in the source string, returning a new string that 530 * lives on the string heap. 531 * 532 * @returns Boolean success indicator. Will reply to the client with all the 533 * gory detail on failure. 534 * @param pPktHdr The packet the string relates to. For replying 535 * on error. 536 * @param pszSrc The source string. 537 * @param ppszNew Where to return the new string. 538 * @param prcSend Where to return the status code of the send on 539 * failure. 540 */ 541 static int txsReplaceStringVariables(PCTXSPKTHDR pPktHdr, const char *pszSrc, char **ppszNew, int *prcSend) 542 { 543 /* Lazy approach that employs memmove. */ 544 size_t cchNew = strlen(pszSrc); 545 char *pszNew = RTStrDup(pszSrc); 546 char *pszDollar = pszNew; 547 while ((pszDollar = strchr(pszDollar, '$')) != NULL) 548 { 549 if (pszDollar[1] == '{') 550 { 551 const char *pszEnd = strchr(&pszDollar[2], '}'); 552 if (pszEnd) 553 { 554 #define IF_VARIABLE_DO(pszDollar, szVarExpr, pszValue) \ 555 if ( cchVar == sizeof(szVarExpr) - 1 \ 556 && !memcmp(pszDollar, szVarExpr, sizeof(szVarExpr) - 1) ) \ 557 { \ 558 size_t const cchValue = strlen(pszValue); \ 559 rc = txsReplaceStringVariable(&pszNew, &cchNew, offDollar, \ 560 sizeof(szVarExpr) - 1, pszValue, cchValue); \ 561 offDollar += cchValue; \ 562 } 563 int rc; 564 size_t const cchVar = pszEnd - pszDollar + 1; /* includes "${}" */ 565 size_t offDollar = pszDollar - pszNew; 566 IF_VARIABLE_DO(pszDollar, "${CDROM}", g_szCdRomPath) 567 else IF_VARIABLE_DO(pszDollar, "${SCRATCH}", g_szScratchPath) 568 else IF_VARIABLE_DO(pszDollar, "${ARCH}", g_szArchShortName) 569 else IF_VARIABLE_DO(pszDollar, "${OS}", g_szOsShortName) 570 else IF_VARIABLE_DO(pszDollar, "${OS.ARCH}", g_szOsDotArchShortName) 571 else IF_VARIABLE_DO(pszDollar, "${OS/ARCH}", g_szOsSlashArchShortName) 572 else IF_VARIABLE_DO(pszDollar, "${EXESUFF}", g_szExeSuff) 573 else IF_VARIABLE_DO(pszDollar, "${SCRIPTSUFF}", g_szScriptSuff) 574 else 575 { 576 RTStrFree(pszNew); 577 *prcSend = txsReplyFailure(pPktHdr, "UNKN VAR", "Unknown variable '%.*s' encountered in '%s'", 578 cchVar, pszDollar, pszSrc); 579 *ppszNew = NULL; 580 return false; 581 } 582 pszDollar = &pszNew[offDollar]; 583 584 if (RT_FAILURE(rc)) 585 { 586 RTStrFree(pszNew); 587 *prcSend = txsReplyRC(pPktHdr, rc, "RTStrRealloc"); 588 *ppszNew = NULL; 589 return false; 590 } 591 #undef IF_VARIABLE_DO 592 } 593 } 594 } 595 596 *ppszNew = pszNew; 597 *prcSend = VINF_SUCCESS; 598 return true; 599 } 600 601 /** 602 * Checks if the string is valid and returns the expanded version. 603 * 604 * @returns true if valid, false if invalid. 605 * @param pPktHdr The packet being unpacked. 606 * @param pszArgName The argument name. 607 * @param psz Pointer to the string within pPktHdr. 608 * @param ppszExp Where to return the expanded string. Must be 609 * freed by calling RTStrFree(). 610 * @param ppszNext Where to return the pointer to the next field. 611 * If NULL, then we assume this string is at the 612 * end of the packet and will make sure it has the 613 * advertised length. 614 * @param prcSend Where to return the status code of the send on 615 * failure. 616 */ 617 static bool txsIsStringValid(PCTXSPKTHDR pPktHdr, const char *pszArgName, const char *psz, 618 char **ppszExp, const char **ppszNext, int *prcSend) 619 { 620 *ppszExp = NULL; 621 if (ppszNext) 622 *ppszNext = NULL; 623 624 size_t const off = psz - (const char *)pPktHdr; 625 if (pPktHdr->cb <= off) 626 { 627 *prcSend = txsReplyFailure(pPktHdr, "STR MISS", "Missing string argument '%s' in '%.8s'", 628 pszArgName, pPktHdr->achOpcode); 629 return false; 630 } 631 632 size_t const cchMax = pPktHdr->cb - off; 633 const char *pszEnd = RTStrEnd(psz, cchMax); 634 if (!pszEnd) 635 { 636 *prcSend = txsReplyFailure(pPktHdr, "STR TERM", "The string argument '%s' in '%.8s' is unterminated", 637 pszArgName, pPktHdr->achOpcode); 638 return false; 639 } 640 641 if (!ppszNext && (size_t)(pszEnd - psz) != cchMax - 1) 642 { 643 *prcSend = txsReplyFailure(pPktHdr, "STR SHRT", "The string argument '%s' in '%.8s' is shorter than advertised", 644 pszArgName, pPktHdr->achOpcode); 645 return false; 646 } 647 648 if (!txsReplaceStringVariables(pPktHdr, psz, ppszExp, prcSend)) 649 return false; 650 if (ppszNext) 651 *ppszNext = pszEnd + 1; 652 return true; 653 } 654 655 /** 656 * Validates a packet with a single string after the header. 657 * 658 * @returns true if valid, false if invalid. 659 * @param pPktHdr The packet. 660 * @param pszArgName The argument name. 661 * @param ppszExp Where to return the string pointer. Variables 662 * will be replaced and it must therefore be freed 663 * by calling RTStrFree(). 664 * @param prcSend Where to return the status code of the send on 665 * failure. 666 */ 667 static bool txsIsStringPktValid(PCTXSPKTHDR pPktHdr, const char *pszArgName, char **ppszExp, int *prcSend) 668 { 669 if (pPktHdr->cb < sizeof(TXSPKTHDR) + 2) 670 { 671 *ppszExp = NULL; 672 *prcSend = txsReplyBadMinSize(pPktHdr, sizeof(TXSPKTHDR) + 2); 673 return false; 674 } 675 676 return txsIsStringValid(pPktHdr, pszArgName, (const char *)(pPktHdr + 1), ppszExp, NULL, prcSend); 447 static int utsReplyUnknown(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr) 448 { 449 return utsReplyFailure(pClient, pPktHdr, "UNKNOWN ", VERR_NOT_FOUND, "Opcode '%.8s' is not known", pPktHdr->achOpcode); 677 450 } 678 451 … … 685 458 * to be the whole 8 chars long. 686 459 */ 687 DECLINLINE(bool) txsIsSameOpcode(PCTXSPKTHDR pPktHdr, const char *pszOpcode2)460 DECLINLINE(bool) utsIsSameOpcode(PCUTSPKTHDR pPktHdr, const char *pszOpcode2) 688 461 { 689 462 if (pPktHdr->achOpcode[0] != pszOpcode2[0]) … … 693 466 694 467 unsigned i = 2; 695 while ( i < RT_SIZEOFMEMB( TXSPKTHDR, achOpcode)468 while ( i < RT_SIZEOFMEMB(UTSPKTHDR, achOpcode) 696 469 && pszOpcode2[i] != '\0') 697 470 { … … 701 474 } 702 475 703 if ( i < RT_SIZEOFMEMB( TXSPKTHDR, achOpcode)476 if ( i < RT_SIZEOFMEMB(UTSPKTHDR, achOpcode) 704 477 && pszOpcode2[i] == '\0') 705 478 { 706 while ( i < RT_SIZEOFMEMB( TXSPKTHDR, achOpcode)479 while ( i < RT_SIZEOFMEMB(UTSPKTHDR, achOpcode) 707 480 && pPktHdr->achOpcode[i] == ' ') 708 481 i++; 709 482 } 710 483 711 return i == RT_SIZEOFMEMB(TXSPKTHDR, achOpcode); 712 } 713 714 /** 715 * Used by txsDoGetFile to wait for a reply ACK from the client. 716 * 717 * @returns VINF_SUCCESS on ACK, VERR_GENERAL_FAILURE on NACK, 718 * VERR_NET_NOT_CONNECTED on unknown response (sending a bable reply), 719 * or whatever txsRecvPkt returns. 720 * @param pPktHdr The original packet (for future use). 721 */ 722 static int txsWaitForAck(PCTXSPKTHDR pPktHdr) 723 { 724 NOREF(pPktHdr); 725 /** @todo timeout? */ 726 PTXSPKTHDR pReply; 727 int rc = txsRecvPkt(&pReply, false /*fAutoRetryOnFailure*/); 484 return i == RT_SIZEOFMEMB(UTSPKTHDR, achOpcode); 485 } 486 487 /** 488 * Verifies and acknowledges a "BYE" request. 489 * 490 * @returns IPRT status code. 491 * @param pClient The UTS client structure. 492 * @param pPktHdr The howdy packet. 493 */ 494 static int utsDoBye(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr) 495 { 496 int rc; 497 if (pPktHdr->cb == sizeof(UTSPKTHDR)) 498 rc = utsReplyAck(pClient, pPktHdr); 499 else 500 rc = utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTHDR)); 501 g_pTransport->pfnNotifyBye(pClient->pTransportClient); 502 return rc; 503 } 504 505 /** 506 * Verifies and acknowledges a "HOWDY" request. 507 * 508 * @returns IPRT status code. 509 * @param pClient The UTS client structure. 510 * @param pPktHdr The howdy packet. 511 */ 512 static int utsDoHowdy(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr) 513 { 514 if (pPktHdr->cb != sizeof(UTSPKTHDR)) 515 return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTHDR)); 516 int rc = utsReplyAck(pClient, pPktHdr); 728 517 if (RT_SUCCESS(rc)) 729 518 { 730 if (txsIsSameOpcode(pReply, "ACK")) 731 rc = VINF_SUCCESS; 732 else if (txsIsSameOpcode(pReply, "NACK")) 733 rc = VERR_GENERAL_FAILURE; 734 else 519 g_pTransport->pfnNotifyHowdy(pClient->pTransportClient); 520 RTDirRemoveRecursive(g_szScratchPath, RTDIRRMREC_F_CONTENT_ONLY); 521 } 522 return rc; 523 } 524 525 /** 526 * Main request processing routine for each client. 527 * 528 * @returns IPRT status code. 529 * @param pClient The UTS client structure sending the request. 530 */ 531 static int utsClientReqProcess(PUTSCLIENT pClient) 532 { 533 /* 534 * Read client command packet and process it. 535 */ 536 PUTSPKTHDR pPktHdr; 537 int rc = utsRecvPkt(pClient, &pPktHdr, true /*fAutoRetryOnFailure*/); 538 if (RT_FAILURE(rc)) 539 return rc; 540 541 /* 542 * Do a string switch on the opcode bit. 543 */ 544 /* Connection: */ 545 if ( utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_HOWDY)) 546 rc = utsDoHowdy(pClient, pPktHdr); 547 else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_BYE)) 548 rc = utsDoBye(pClient, pPktHdr); 549 /* Misc: */ 550 else 551 rc = utsReplyUnknown(pClient, pPktHdr); 552 553 RTMemFree(pPktHdr); 554 555 return rc; 556 } 557 558 /** 559 * Destroys a client instance. 560 * 561 * @returns nothing. 562 * @param pClient The UTS client structure. 563 */ 564 static void utsClientDestroy(PUTSCLIENT pClient) 565 { 566 RTMemFree(pClient); 567 } 568 569 /** 570 * The main thread worker serving the clients. 571 */ 572 static DECLCALLBACK(int) utsClientWorker(RTTHREAD hThread, void *pvUser) 573 { 574 unsigned cClientsMax = 0; 575 unsigned cClientsCur = 0; 576 PUTSCLIENT *papClients = NULL; 577 RTPOLLSET hPollSet; 578 579 int rc = RTPollSetCreate(&hPollSet); 580 if (RT_FAILURE(rc)) 581 return rc; 582 583 /* Add the pipe to the poll set. */ 584 rc = RTPollSetAddPipe(hPollSet, g_hPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 0); 585 if (RT_SUCCESS(rc)) 586 { 587 while (!g_fTerminate) 735 588 { 736 txsReplyBabble("BABBLE "); 737 rc = VERR_NET_NOT_CONNECTED; 738 } 739 RTMemFree(pReply); 740 } 741 return rc; 742 } 743 744 #ifndef RT_OS_WINDOWS 745 /** 746 * Unpacks a tar file. 747 * 748 * @returns IPRT status code from send. 749 * @param pPktHdr The unpack file packet. 750 */ 751 static int txsDoUnpackFile(PCTXSPKTHDR pPktHdr) 752 { 753 int rc; 754 char *pszFile = NULL; 755 char *pszDirectory = NULL; 756 757 /* Packet cursor. */ 758 const char *pchEnd = (const char *)pPktHdr + pPktHdr->cb; 759 const char *pch = (const char *)(pPktHdr + 1); 760 761 if (txsIsStringValid(pPktHdr, "file", pch, &pszFile, &pch, &rc)) 762 { 763 if (txsIsStringValid(pPktHdr, "directory", pch, &pszDirectory, &pch, &rc)) 764 { 765 char *pszSuff = RTPathSuffix(pszFile); 766 767 const char *apszArgs[7]; 768 unsigned cArgs = 0; 769 770 apszArgs[cArgs++] = "RTTar"; 771 apszArgs[cArgs++] = "--extract"; 772 773 apszArgs[cArgs++] = "--file"; 774 apszArgs[cArgs++] = pszFile; 775 776 apszArgs[cArgs++] = "--directory"; 777 apszArgs[cArgs++] = pszDirectory; 778 779 if ( pszSuff 780 && ( !RTStrICmp(pszSuff, ".gz") 781 || !RTStrICmp(pszSuff, ".tgz"))) 782 apszArgs[cArgs++] = "--gunzip"; 783 784 RTEXITCODE rcExit = RTZipTarCmd(cArgs, (char **)apszArgs); 785 if (rcExit != RTEXITCODE_SUCCESS) 786 rc = VERR_GENERAL_FAILURE; /** @todo proper return code. */ 787 else 788 rc = VINF_SUCCESS; 789 790 rc = txsReplyRC(pPktHdr, rc, "RTZipTarCmd(\"%s\",\"%s\")", 791 pszFile, pszDirectory); 792 793 RTStrFree(pszDirectory); 794 } 795 RTStrFree(pszFile); 796 } 797 798 return rc; 799 } 800 #endif 801 802 /** 803 * Downloads a file to the client. 804 * 805 * The transfer sends a stream of DATA packets (0 or more) and ends it all with 806 * a ACK packet. If an error occurs, a FAILURE packet is sent and the transfer 807 * aborted. 808 * 809 * @returns IPRT status code from send. 810 * @param pPktHdr The get file packet. 811 */ 812 static int txsDoGetFile(PCTXSPKTHDR pPktHdr) 813 { 814 int rc; 815 char *pszPath; 816 if (!txsIsStringPktValid(pPktHdr, "file", &pszPath, &rc)) 817 return rc; 818 819 RTFILE hFile; 820 rc = RTFileOpen(&hFile, pszPath, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN); 821 if (RT_SUCCESS(rc)) 822 { 823 uint32_t uMyCrc32 = RTCrc32Start(); 824 for (;;) 825 { 826 struct 589 uint32_t fEvts; 590 uint32_t uId; 591 rc = RTPoll(hPollSet, RT_INDEFINITE_WAIT, &fEvts, &uId); 592 if (RT_SUCCESS(rc)) 827 593 { 828 TXSPKTHDR Hdr; 829 uint32_t uCrc32; 830 char ab[_64K]; 831 char abPadding[TXSPKT_ALIGNMENT]; 832 } Pkt; 833 size_t cbRead; 834 rc = RTFileRead(hFile, &Pkt.ab[0], _64K, &cbRead); 835 if (RT_FAILURE(rc) || cbRead == 0) 836 { 837 if (rc == VERR_EOF || (RT_SUCCESS(rc) && cbRead == 0)) 594 if (uId == 0) 838 595 { 839 Pkt.uCrc32 = RTCrc32Finish(uMyCrc32); 840 rc = txsReplyInternal(&Pkt.Hdr, "DATA EOF", sizeof(uint32_t)); 841 if (RT_SUCCESS(rc)) 842 rc = txsWaitForAck(&Pkt.Hdr); 843 } 844 else 845 rc = txsReplyRC(pPktHdr, rc, "RTFileRead"); 846 break; 847 } 848 849 uMyCrc32 = RTCrc32Process(uMyCrc32, &Pkt.ab[0], cbRead); 850 Pkt.uCrc32 = RTCrc32Finish(uMyCrc32); 851 rc = txsReplyInternal(&Pkt.Hdr, "DATA ", cbRead + sizeof(uint32_t)); 852 if (RT_FAILURE(rc)) 853 break; 854 rc = txsWaitForAck(&Pkt.Hdr); 855 if (RT_FAILURE(rc)) 856 break; 857 } 858 859 RTFileClose(hFile); 860 } 861 else 862 rc = txsReplyRC(pPktHdr, rc, "RTFileOpen(,\"%s\",)", pszPath); 863 864 RTStrFree(pszPath); 865 return rc; 866 } 867 868 /** 869 * Uploads a file from the client. 870 * 871 * The transfer sends a stream of DATA packets (0 or more) and ends it all with 872 * a DATA EOF packet. We ACK each of these, so that if a write error occurs we 873 * can abort the transfer straight away. 874 * 875 * @returns IPRT status code from send. 876 * @param pPktHdr The put file packet. 877 */ 878 static int txsDoPutFile(PCTXSPKTHDR pPktHdr) 879 { 880 int rc; 881 char *pszPath; 882 if (!txsIsStringPktValid(pPktHdr, "file", &pszPath, &rc)) 883 return rc; 884 885 RTFILE hFile; 886 rc = RTFileOpen(&hFile, pszPath, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE); 887 if (RT_SUCCESS(rc)) 888 { 889 bool fSuccess = false; 890 rc = txsReplyAck(pPktHdr); 891 if (RT_SUCCESS(rc)) 892 { 893 /* 894 * Read client command packets and process them. 895 */ 896 uint32_t uMyCrc32 = RTCrc32Start(); 897 for (;;) 898 { 899 PTXSPKTHDR pDataPktHdr; 900 rc = txsRecvPkt(&pDataPktHdr, false /*fAutoRetryOnFailure*/); 901 if (RT_FAILURE(rc)) 902 break; 903 904 if (txsIsSameOpcode(pDataPktHdr, "DATA")) 905 { 906 size_t const cbMin = sizeof(TXSPKTHDR) + sizeof(uint32_t); 907 if (pDataPktHdr->cb >= cbMin) 596 if (fEvts & RTPOLL_EVT_ERROR) 597 break; 598 599 /* We got woken up because of a new client. */ 600 Assert(fEvts & RTPOLL_EVT_READ); 601 602 uint8_t bRead; 603 rc = RTPipeRead(g_hPipeR, &bRead, 1, NULL); 604 AssertRC(rc); 605 606 RTCritSectEnter(&g_CritSectClients); 607 /* Walk the list and add all new clients. */ 608 PUTSCLIENT pIt, pItNext; 609 RTListForEachSafe(&g_LstClientsNew, pIt, pItNext, UTSCLIENT, NdLst) 908 610 { 909 size_t cbData = pDataPktHdr->cb - cbMin; 910 const void *pvData = (const char *)pDataPktHdr + cbMin; 911 uint32_t uCrc32 = *(uint32_t const *)(pDataPktHdr + 1); 912 913 uMyCrc32 = RTCrc32Process(uMyCrc32, pvData, cbData); 914 if (RTCrc32Finish(uMyCrc32) == uCrc32) 611 RTListNodeRemove(&pIt->NdLst); 612 Assert(cClientsCur <= cClientsMax); 613 if (cClientsCur == cClientsMax) 915 614 { 916 rc = RTFileWrite(hFile, pvData, cbData, NULL); 615 /* Realloc to accommodate for the new clients. */ 616 PUTSCLIENT *papClientsNew = (PUTSCLIENT *)RTMemRealloc(papClients, (cClientsMax + 10) * sizeof(PUTSCLIENT)); 617 if (RT_LIKELY(papClientsNew)) 618 { 619 cClientsMax += 10; 620 papClients = papClientsNew; 621 } 622 } 623 624 if (cClientsCur < cClientsMax) 625 { 626 /* Find a free slot in the client array. */ 627 unsigned idxSlt = 0; 628 while ( idxSlt < cClientsMax 629 && papClients[idxSlt] != NULL) 630 idxSlt++; 631 632 rc = g_pTransport->pfnPollSetAdd(hPollSet, pIt->pTransportClient, idxSlt + 1); 917 633 if (RT_SUCCESS(rc)) 918 634 { 919 rc = txsReplyAck(pDataPktHdr); 920 RTMemFree(pDataPktHdr); 921 continue; 635 cClientsCur++; 636 papClients[idxSlt] = pIt; 922 637 } 923 924 rc = txsReplyRC(pDataPktHdr, rc, "RTFileWrite"); 638 else 639 { 640 g_pTransport->pfnNotifyBye(pIt->pTransportClient); 641 utsClientDestroy(pIt); 642 } 925 643 } 926 644 else 927 rc = txsReplyFailure(pDataPktHdr, "BAD DCRC", "mycrc=%#x your=%#x", uMyCrc32, uCrc32); 645 { 646 g_pTransport->pfnNotifyBye(pIt->pTransportClient); 647 utsClientDestroy(pIt); 648 } 928 649 } 929 else 930 rc = txsReplyBadMinSize(pPktHdr, cbMin); 931 } 932 else if (txsIsSameOpcode(pDataPktHdr, "DATA EOF")) 933 { 934 if (pDataPktHdr->cb == sizeof(TXSPKTHDR) + sizeof(uint32_t)) 935 { 936 uint32_t uCrc32 = *(uint32_t const *)(pDataPktHdr + 1); 937 if (RTCrc32Finish(uMyCrc32) == uCrc32) 938 { 939 rc = txsReplyAck(pDataPktHdr); 940 fSuccess = RT_SUCCESS(rc); 941 } 942 else 943 rc = txsReplyFailure(pDataPktHdr, "BAD DCRC", "mycrc=%#x your=%#x", uMyCrc32, uCrc32); 944 } 945 else 946 rc = txsReplyAck(pDataPktHdr); 947 } 948 else if (txsIsSameOpcode(pDataPktHdr, "ABORT")) 949 rc = txsReplyAck(pDataPktHdr); 950 else 951 rc = txsReplyFailure(pDataPktHdr, "UNKNOWN ", "Opcode '%.8s' is not known or not recognized during PUT FILE", pDataPktHdr->achOpcode); 952 RTMemFree(pDataPktHdr); 953 break; 954 } 955 } 956 957 RTFileClose(hFile); 958 959 /* 960 * Delete the file on failure. 961 */ 962 if (!fSuccess) 963 RTFileDelete(pszPath); 964 } 965 else 966 rc = txsReplyRC(pPktHdr, rc, "RTFileOpen(,\"%s\",)", pszPath); 967 968 RTStrFree(pszPath); 969 return rc; 970 } 971 972 /** 973 * List the entries in the specified directory. 974 * 975 * @returns IPRT status code from send. 976 * @param pPktHdr The list packet. 977 */ 978 static int txsDoList(PCTXSPKTHDR pPktHdr) 979 { 980 int rc; 981 char *pszPath; 982 if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc)) 983 return rc; 984 985 rc = txsReplyNotImplemented(pPktHdr); 986 987 RTStrFree(pszPath); 988 return rc; 989 } 990 991 992 /** 993 * Get info about a file system object, following all but the symbolic links 994 * except in the final path component. 995 * 996 * @returns IPRT status code from send. 997 * @param pPktHdr The lstat packet. 998 */ 999 static int txsDoLStat(PCTXSPKTHDR pPktHdr) 1000 { 1001 int rc; 1002 char *pszPath; 1003 if (!txsIsStringPktValid(pPktHdr, "path", &pszPath, &rc)) 1004 return rc; 1005 1006 RTFSOBJINFO Info; 1007 rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK); 1008 if (RT_SUCCESS(rc)) 1009 /** @todo figure out how to format the return buffer here. */ 1010 rc = txsReplyNotImplemented(pPktHdr); 1011 else 1012 rc = txsReplyRC(pPktHdr, rc, "RTPathQueryInfoEx(\"%s\",,UNIX,ON_LINK)", pszPath); 1013 1014 RTStrFree(pszPath); 1015 return rc; 1016 } 1017 1018 /** 1019 * Get info about a file system object, following all symbolic links. 1020 * 1021 * @returns IPRT status code from send. 1022 * @param pPktHdr The stat packet. 1023 */ 1024 static int txsDoStat(PCTXSPKTHDR pPktHdr) 1025 { 1026 int rc; 1027 char *pszPath; 1028 if (!txsIsStringPktValid(pPktHdr, "path", &pszPath, &rc)) 1029 return rc; 1030 1031 RTFSOBJINFO Info; 1032 rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK); 1033 if (RT_SUCCESS(rc)) 1034 /** @todo figure out how to format the return buffer here. */ 1035 rc = txsReplyNotImplemented(pPktHdr); 1036 else 1037 rc = txsReplyRC(pPktHdr, rc, "RTPathQueryInfoEx(\"%s\",,UNIX,FOLLOW_LINK)", pszPath); 1038 1039 RTStrFree(pszPath); 1040 return rc; 1041 } 1042 1043 /** 1044 * Checks if the specified path is a symbolic link. 1045 * 1046 * @returns IPRT status code from send. 1047 * @param pPktHdr The issymlnk packet. 1048 */ 1049 static int txsDoIsSymlnk(PCTXSPKTHDR pPktHdr) 1050 { 1051 int rc; 1052 char *pszPath; 1053 if (!txsIsStringPktValid(pPktHdr, "symlink", &pszPath, &rc)) 1054 return rc; 1055 1056 RTFSOBJINFO Info; 1057 rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK); 1058 if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(Info.Attr.fMode)) 1059 rc = txsReplySimple(pPktHdr, "TRUE "); 1060 else 1061 rc = txsReplySimple(pPktHdr, "FALSE "); 1062 1063 RTStrFree(pszPath); 1064 return rc; 1065 } 1066 1067 /** 1068 * Checks if the specified path is a file or not. 1069 * 1070 * If the final path element is a symbolic link to a file, we'll return 1071 * FALSE. 1072 * 1073 * @returns IPRT status code from send. 1074 * @param pPktHdr The isfile packet. 1075 */ 1076 static int txsDoIsFile(PCTXSPKTHDR pPktHdr) 1077 { 1078 int rc; 1079 char *pszPath; 1080 if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc)) 1081 return rc; 1082 1083 RTFSOBJINFO Info; 1084 rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK); 1085 if (RT_SUCCESS(rc) && RTFS_IS_FILE(Info.Attr.fMode)) 1086 rc = txsReplySimple(pPktHdr, "TRUE "); 1087 else 1088 rc = txsReplySimple(pPktHdr, "FALSE "); 1089 1090 RTStrFree(pszPath); 1091 return rc; 1092 } 1093 1094 /** 1095 * Checks if the specified path is a directory or not. 1096 * 1097 * If the final path element is a symbolic link to a directory, we'll return 1098 * FALSE. 1099 * 1100 * @returns IPRT status code from send. 1101 * @param pPktHdr The isdir packet. 1102 */ 1103 static int txsDoIsDir(PCTXSPKTHDR pPktHdr) 1104 { 1105 int rc; 1106 char *pszPath; 1107 if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc)) 1108 return rc; 1109 1110 RTFSOBJINFO Info; 1111 rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK); 1112 if (RT_SUCCESS(rc) && RTFS_IS_DIRECTORY(Info.Attr.fMode)) 1113 rc = txsReplySimple(pPktHdr, "TRUE "); 1114 else 1115 rc = txsReplySimple(pPktHdr, "FALSE "); 1116 1117 RTStrFree(pszPath); 1118 return rc; 1119 } 1120 1121 /** 1122 * Changes the group of a file, directory of symbolic link. 1123 * 1124 * @returns IPRT status code from send. 1125 * @param pPktHdr The chmod packet. 1126 */ 1127 static int txsDoChGrp(PCTXSPKTHDR pPktHdr) 1128 { 1129 return txsReplyNotImplemented(pPktHdr); 1130 } 1131 1132 /** 1133 * Changes the owner of a file, directory of symbolic link. 1134 * 1135 * @returns IPRT status code from send. 1136 * @param pPktHdr The chmod packet. 1137 */ 1138 static int txsDoChOwn(PCTXSPKTHDR pPktHdr) 1139 { 1140 return txsReplyNotImplemented(pPktHdr); 1141 } 1142 1143 /** 1144 * Changes the mode of a file or directory. 1145 * 1146 * @returns IPRT status code from send. 1147 * @param pPktHdr The chmod packet. 1148 */ 1149 static int txsDoChMod(PCTXSPKTHDR pPktHdr) 1150 { 1151 return txsReplyNotImplemented(pPktHdr); 1152 } 1153 1154 /** 1155 * Removes a directory tree. 1156 * 1157 * @returns IPRT status code from send. 1158 * @param pPktHdr The rmtree packet. 1159 */ 1160 static int txsDoRmTree(PCTXSPKTHDR pPktHdr) 1161 { 1162 int rc; 1163 char *pszPath; 1164 if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc)) 1165 return rc; 1166 1167 rc = RTDirRemoveRecursive(pszPath, 0 /*fFlags*/); 1168 1169 rc = txsReplyRC(pPktHdr, rc, "RTDirRemoveRecusive(\"%s\",0)", pszPath); 1170 RTStrFree(pszPath); 1171 return rc; 1172 } 1173 1174 /** 1175 * Removes a symbolic link. 1176 * 1177 * @returns IPRT status code from send. 1178 * @param pPktHdr The rmsymlink packet. 1179 */ 1180 static int txsDoRmSymlnk(PCTXSPKTHDR pPktHdr) 1181 { 1182 int rc; 1183 char *pszPath; 1184 if (!txsIsStringPktValid(pPktHdr, "symlink", &pszPath, &rc)) 1185 return rc; 1186 1187 rc = VERR_NOT_IMPLEMENTED; /// @todo RTSymlinkDelete(pszPath); 1188 1189 rc = txsReplyRC(pPktHdr, rc, "RTSymlinkDelete(\"%s\")", pszPath); 1190 RTStrFree(pszPath); 1191 return rc; 1192 } 1193 1194 /** 1195 * Removes a file. 1196 * 1197 * @returns IPRT status code from send. 1198 * @param pPktHdr The rmfile packet. 1199 */ 1200 static int txsDoRmFile(PCTXSPKTHDR pPktHdr) 1201 { 1202 int rc; 1203 char *pszPath; 1204 if (!txsIsStringPktValid(pPktHdr, "file", &pszPath, &rc)) 1205 return rc; 1206 1207 rc = RTFileDelete(pszPath); 1208 1209 rc = txsReplyRC(pPktHdr, rc, "RTFileDelete(\"%s\")", pszPath); 1210 RTStrFree(pszPath); 1211 return rc; 1212 } 1213 1214 /** 1215 * Removes a directory. 1216 * 1217 * @returns IPRT status code from send. 1218 * @param pPktHdr The rmdir packet. 1219 */ 1220 static int txsDoRmDir(PCTXSPKTHDR pPktHdr) 1221 { 1222 int rc; 1223 char *pszPath; 1224 if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc)) 1225 return rc; 1226 1227 rc = RTDirRemove(pszPath); 1228 1229 rc = txsReplyRC(pPktHdr, rc, "RTDirRemove(\"%s\")", pszPath); 1230 RTStrFree(pszPath); 1231 return rc; 1232 } 1233 1234 /** 1235 * Creates a symbolic link. 1236 * 1237 * @returns IPRT status code from send. 1238 * @param pPktHdr The mksymlnk packet. 1239 */ 1240 static int txsDoMkSymlnk(PCTXSPKTHDR pPktHdr) 1241 { 1242 return txsReplyNotImplemented(pPktHdr); 1243 } 1244 1245 /** 1246 * Creates a directory and all its parents. 1247 * 1248 * @returns IPRT status code from send. 1249 * @param pPktHdr The mkdir -p packet. 1250 */ 1251 static int txsDoMkDrPath(PCTXSPKTHDR pPktHdr) 1252 { 1253 /* The same format as the MKDIR command. */ 1254 if (pPktHdr->cb < sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2) 1255 return txsReplyBadMinSize(pPktHdr, sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2); 1256 1257 int rc; 1258 char *pszPath; 1259 if (!txsIsStringValid(pPktHdr, "dir", (const char *)(pPktHdr + 1) + sizeof(RTFMODE), &pszPath, NULL, &rc)) 1260 return rc; 1261 1262 RTFMODE fMode = *(RTFMODE const *)(pPktHdr + 1); 1263 rc = RTDirCreateFullPath(pszPath, fMode); 1264 1265 rc = txsReplyRC(pPktHdr, rc, "RTDirCreateFullPath(\"%s\", %#x)", pszPath, fMode); 1266 RTStrFree(pszPath); 1267 return rc; 1268 } 1269 1270 /** 1271 * Creates a directory. 1272 * 1273 * @returns IPRT status code from send. 1274 * @param pPktHdr The mkdir packet. 1275 */ 1276 static int txsDoMkDir(PCTXSPKTHDR pPktHdr) 1277 { 1278 /* After the packet header follows a mode mask and the remainder of 1279 the packet is the zero terminated directory name. */ 1280 size_t const cbMin = sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2; 1281 if (pPktHdr->cb < cbMin) 1282 return txsReplyBadMinSize(pPktHdr, cbMin); 1283 1284 int rc; 1285 char *pszPath; 1286 if (!txsIsStringValid(pPktHdr, "dir", (const char *)(pPktHdr + 1) + sizeof(RTFMODE), &pszPath, NULL, &rc)) 1287 return rc; 1288 1289 RTFMODE fMode = *(RTFMODE const *)(pPktHdr + 1); 1290 rc = RTDirCreate(pszPath, fMode, 0); 1291 1292 rc = txsReplyRC(pPktHdr, rc, "RTDirCreate(\"%s\", %#x)", pszPath, fMode); 1293 RTStrFree(pszPath); 1294 return rc; 1295 } 1296 1297 /** 1298 * Cleans up the scratch area. 1299 * 1300 * @returns IPRT status code from send. 1301 * @param pPktHdr The shutdown packet. 1302 */ 1303 static int txsDoCleanup(PCTXSPKTHDR pPktHdr) 1304 { 1305 int rc = RTDirRemoveRecursive(g_szScratchPath, RTDIRRMREC_F_CONTENT_ONLY); 1306 return txsReplyRC(pPktHdr, rc, "RTDirRemoveRecursive(\"%s\", CONTENT_ONLY)", g_szScratchPath); 1307 } 1308 1309 /** 1310 * Ejects the specified DVD/CD drive. 1311 * 1312 * @returns IPRT status code from send. 1313 * @param pPktHdr The eject packet. 1314 */ 1315 static int txsDoCdEject(PCTXSPKTHDR pPktHdr) 1316 { 1317 /* After the packet header follows a uint32_t ordinal. */ 1318 size_t const cbExpected = sizeof(TXSPKTHDR) + sizeof(uint32_t); 1319 if (pPktHdr->cb != cbExpected) 1320 return txsReplyBadSize(pPktHdr, cbExpected); 1321 uint32_t iOrdinal = *(uint32_t const *)(pPktHdr + 1); 1322 1323 RTCDROM hCdrom; 1324 int rc = RTCdromOpenByOrdinal(iOrdinal, RTCDROM_O_CONTROL, &hCdrom); 1325 if (RT_FAILURE(rc)) 1326 return txsReplyRC(pPktHdr, rc, "RTCdromOpenByOrdinal(%u, RTCDROM_O_CONTROL, )", iOrdinal); 1327 rc = RTCdromEject(hCdrom, true /*fForce*/); 1328 RTCdromRelease(hCdrom); 1329 1330 return txsReplyRC(pPktHdr, rc, "RTCdromEject(ord=%u, fForce=true)", iOrdinal); 1331 } 1332 1333 /** 1334 * Common worker for txsDoShutdown and txsDoReboot. 1335 * 1336 * @returns IPRT status code from send. 1337 * @param pPktHdr The reboot packet. 1338 * @param fAction Which action to take. 1339 */ 1340 static int txsCommonShutdownReboot(PCTXSPKTHDR pPktHdr, uint32_t fAction) 1341 { 1342 /* 1343 * We ACK the reboot & shutdown before actually performing them, then we 1344 * terminate the transport layer. 1345 * 1346 * This is to make sure the client isn't stuck with a dead connection. The 1347 * transport layer termination also make sure we won't accept new 1348 * connections in case the client is too eager to reconnect to a rebooted 1349 * test victim. On the down side, we cannot easily report RTSystemShutdown 1350 * failures failures this way. But the client can kind of figure it out by 1351 * reconnecting and seeing that our UUID was unchanged. 1352 */ 1353 int rc; 1354 if (pPktHdr->cb != sizeof(TXSPKTHDR)) 1355 return txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR)); 1356 g_pTransport->pfnNotifyReboot(); 1357 rc = txsReplyAck(pPktHdr); 1358 RTThreadSleep(2560); /* fudge factor */ 1359 g_pTransport->pfnTerm(); 1360 1361 /* 1362 * Do the job, if it fails we'll restart the transport layer. 1363 */ 1364 #if 0 1365 rc = VINF_SUCCESS; 1366 #else 1367 rc = RTSystemShutdown(0 /*cMsDelay*/, 1368 fAction | RTSYSTEM_SHUTDOWN_PLANNED | RTSYSTEM_SHUTDOWN_FORCE, 1369 "Test Execution Service"); 1370 #endif 1371 if (RT_SUCCESS(rc)) 1372 { 1373 RTMsgInfo(fAction == RTSYSTEM_SHUTDOWN_REBOOT ? "Rebooting...\n" : "Shutting down...\n"); 1374 g_fTerminate = true; 1375 } 1376 else 1377 { 1378 RTMsgError("RTSystemShutdown w/ fAction=%#x failed: %Rrc", fAction, rc); 1379 1380 int rc2 = g_pTransport->pfnInit(); 1381 if (RT_FAILURE(rc2)) 1382 { 1383 g_fTerminate = true; 1384 rc = rc2; 1385 } 1386 } 1387 return rc; 1388 } 1389 1390 /** 1391 * Shuts down the machine, powering it off if possible. 1392 * 1393 * @returns IPRT status code from send. 1394 * @param pPktHdr The shutdown packet. 1395 */ 1396 static int txsDoShutdown(PCTXSPKTHDR pPktHdr) 1397 { 1398 return txsCommonShutdownReboot(pPktHdr, RTSYSTEM_SHUTDOWN_POWER_OFF_HALT); 1399 } 1400 1401 /** 1402 * Reboots the machine. 1403 * 1404 * @returns IPRT status code from send. 1405 * @param pPktHdr The reboot packet. 1406 */ 1407 static int txsDoReboot(PCTXSPKTHDR pPktHdr) 1408 { 1409 return txsCommonShutdownReboot(pPktHdr, RTSYSTEM_SHUTDOWN_REBOOT); 1410 } 1411 1412 /** 1413 * Verifies and acknowledges a "UUID" request. 1414 * 1415 * @returns IPRT status code. 1416 * @param pPktHdr The howdy packet. 1417 */ 1418 static int txsDoUuid(PCTXSPKTHDR pPktHdr) 1419 { 1420 if (pPktHdr->cb != sizeof(TXSPKTHDR)) 1421 return txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR)); 1422 1423 struct 1424 { 1425 TXSPKTHDR Hdr; 1426 char szUuid[RTUUID_STR_LENGTH]; 1427 char abPadding[TXSPKT_ALIGNMENT]; 1428 } Pkt; 1429 1430 int rc = RTUuidToStr(&g_InstanceUuid, Pkt.szUuid, sizeof(Pkt.szUuid)); 1431 if (RT_FAILURE(rc)) 1432 return txsReplyRC(pPktHdr, rc, "RTUuidToStr"); 1433 return txsReplyInternal(&Pkt.Hdr, "ACK UUID", strlen(Pkt.szUuid) + 1); 1434 } 1435 1436 /** 1437 * Verifies and acknowledges a "BYE" request. 1438 * 1439 * @returns IPRT status code. 1440 * @param pPktHdr The howdy packet. 1441 */ 1442 static int txsDoBye(PCTXSPKTHDR pPktHdr) 1443 { 1444 int rc; 1445 if (pPktHdr->cb == sizeof(TXSPKTHDR)) 1446 rc = txsReplyAck(pPktHdr); 1447 else 1448 rc = txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR)); 1449 g_pTransport->pfnNotifyBye(); 1450 return rc; 1451 } 1452 1453 /** 1454 * Verifies and acknowledges a "HOWDY" request. 1455 * 1456 * @returns IPRT status code. 1457 * @param pPktHdr The howdy packet. 1458 */ 1459 static int txsDoHowdy(PCTXSPKTHDR pPktHdr) 1460 { 1461 if (pPktHdr->cb != sizeof(TXSPKTHDR)) 1462 return txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR)); 1463 int rc = txsReplyAck(pPktHdr); 1464 if (RT_SUCCESS(rc)) 1465 { 1466 g_pTransport->pfnNotifyHowdy(); 1467 RTDirRemoveRecursive(g_szScratchPath, RTDIRRMREC_F_CONTENT_ONLY); 1468 } 1469 return rc; 1470 } 1471 1472 /** 1473 * Replies according to the return code. 1474 * 1475 * @returns rcOperation and pTxsExec->rcReplySend. 1476 * @param pTxsExec The TXSEXEC instance. 1477 * @param rcOperation The status code to report. 1478 * @param pszOperationFmt The operation that failed. Typically giving the 1479 * function call with important arguments. 1480 * @param ... Arguments to the format string. 1481 */ 1482 static int txsExecReplyRC(PTXSEXEC pTxsExec, int rcOperation, const char *pszOperationFmt, ...) 1483 { 1484 AssertStmt(RT_FAILURE_NP(rcOperation), rcOperation = VERR_IPE_UNEXPECTED_INFO_STATUS); 1485 1486 char szOperation[128]; 1487 va_list va; 1488 va_start(va, pszOperationFmt); 1489 RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va); 1490 va_end(va); 1491 1492 pTxsExec->rcReplySend = txsReplyFailure(pTxsExec->pPktHdr, "FAILED ", 1493 "%s failed with rc=%Rrc (opcode '%.8s')", 1494 szOperation, rcOperation, pTxsExec->pPktHdr->achOpcode); 1495 return rcOperation; 1496 } 1497 1498 1499 /** 1500 * Sends the process exit status reply to the TXS client. 1501 * 1502 * @returns IPRT status code of the send. 1503 * @param pTxsExec The TXSEXEC instance. 1504 * @param fProcessAlive Whether the process is still alive (against our 1505 * will). 1506 * @param fProcessTimedOut Whether the process timed out. 1507 * @param MsProcessKilled When the process was killed, UINT64_MAX if not. 1508 */ 1509 static int txsExecSendExitStatus(PTXSEXEC pTxsExec, bool fProcessAlive, bool fProcessTimedOut, uint64_t MsProcessKilled) 1510 { 1511 int rc; 1512 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX) 1513 { 1514 rc = txsReplySimple(pTxsExec->pPktHdr, "PROC TOK"); 1515 if (g_fDisplayOutput) 1516 RTPrintf("txs: Process timed out and was killed\n"); 1517 } 1518 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX) 1519 { 1520 rc = txsReplySimple(pTxsExec->pPktHdr, "PROC TOA"); 1521 if (g_fDisplayOutput) 1522 RTPrintf("txs: Process timed out and was not killed successfully\n"); 1523 } 1524 else if (g_fTerminate && (fProcessAlive || MsProcessKilled != UINT64_MAX)) 1525 rc = txsReplySimple(pTxsExec->pPktHdr, "PROC DWN"); 1526 else if (fProcessAlive) 1527 { 1528 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC DOO", "Doofus! process is alive when it should not"); 1529 AssertFailed(); 1530 } 1531 else if (MsProcessKilled != UINT64_MAX) 1532 { 1533 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC DOO", "Doofus! process has been killed when it should not"); 1534 AssertFailed(); 1535 } 1536 else if ( pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL 1537 && pTxsExec->ProcessStatus.iStatus == 0) 1538 { 1539 rc = txsReplySimple(pTxsExec->pPktHdr, "PROC OK "); 1540 if (g_fDisplayOutput) 1541 RTPrintf("txs: Process exited with status: 0\n"); 1542 } 1543 else if (pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL) 1544 { 1545 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC NOK", "%d", pTxsExec->ProcessStatus.iStatus); 1546 if (g_fDisplayOutput) 1547 RTPrintf("txs: Process exited with status: %d\n", pTxsExec->ProcessStatus.iStatus); 1548 } 1549 else if (pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL) 1550 { 1551 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC SIG", "%d", pTxsExec->ProcessStatus.iStatus); 1552 if (g_fDisplayOutput) 1553 RTPrintf("txs: Process exited with status: signal %d\n", pTxsExec->ProcessStatus.iStatus); 1554 } 1555 else if (pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_ABEND) 1556 { 1557 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC ABD", ""); 1558 if (g_fDisplayOutput) 1559 RTPrintf("txs: Process exited with status: abend\n"); 1560 } 1561 else 1562 { 1563 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC DOO", "enmReason=%d iStatus=%d", 1564 pTxsExec->ProcessStatus.enmReason, pTxsExec->ProcessStatus.iStatus); 1565 AssertMsgFailed(("enmReason=%d iStatus=%d", pTxsExec->ProcessStatus.enmReason, pTxsExec->ProcessStatus.iStatus)); 1566 } 1567 return rc; 1568 } 1569 1570 /** 1571 * Handle pending output data or error on standard out, standard error or the 1572 * test pipe. 1573 * 1574 * @returns IPRT status code from client send. 1575 * @param hPollSet The polling set. 1576 * @param fPollEvt The event mask returned by RTPollNoResume. 1577 * @param phPipeR The pipe handle. 1578 * @param pu32Crc The current CRC-32 of the stream. (In/Out) 1579 * @param enmHndId The handle ID. 1580 * @param pszOpcode The opcode for the data upload. 1581 * 1582 * @todo Put the last 4 parameters into a struct! 1583 */ 1584 static int txsDoExecHlpHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phPipeR, 1585 uint32_t *puCrc32, TXSEXECHNDID enmHndId, const char *pszOpcode) 1586 { 1587 Log(("txsDoExecHlpHandleOutputEvent: %s fPollEvt=%#x\n", pszOpcode, fPollEvt)); 1588 1589 /* 1590 * Try drain the pipe before acting on any errors. 1591 */ 1592 int rc = VINF_SUCCESS; 1593 struct 1594 { 1595 TXSPKTHDR Hdr; 1596 uint32_t uCrc32; 1597 char abBuf[_64K]; 1598 char abPadding[TXSPKT_ALIGNMENT]; 1599 } Pkt; 1600 size_t cbRead; 1601 int rc2 = RTPipeRead(*phPipeR, Pkt.abBuf, sizeof(Pkt.abBuf), &cbRead); 1602 if (RT_SUCCESS(rc2) && cbRead) 1603 { 1604 Log(("Crc32=%#x ", *puCrc32)); 1605 *puCrc32 = RTCrc32Process(*puCrc32, Pkt.abBuf, cbRead); 1606 Log(("cbRead=%#x Crc32=%#x \n", cbRead, *puCrc32)); 1607 Pkt.uCrc32 = RTCrc32Finish(*puCrc32); 1608 if (g_fDisplayOutput) 1609 { 1610 if (enmHndId == TXSEXECHNDID_STDOUT) 1611 RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf); 1612 else if (enmHndId == TXSEXECHNDID_STDERR) 1613 RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf); 1614 } 1615 1616 rc = txsReplyInternal(&Pkt.Hdr, pszOpcode, cbRead + sizeof(uint32_t)); 1617 1618 /* Make sure we go another poll round in case there was too much data 1619 for the buffer to hold. */ 1620 fPollEvt &= RTPOLL_EVT_ERROR; 1621 } 1622 else if (RT_FAILURE(rc2)) 1623 { 1624 fPollEvt |= RTPOLL_EVT_ERROR; 1625 AssertMsg(rc2 == VERR_BROKEN_PIPE, ("%Rrc\n", rc)); 1626 } 1627 1628 /* 1629 * If an error was raised signalled, 1630 */ 1631 if (fPollEvt & RTPOLL_EVT_ERROR) 1632 { 1633 rc2 = RTPollSetRemove(hPollSet, enmHndId); 1634 AssertRC(rc2); 1635 1636 rc2 = RTPipeClose(*phPipeR); 1637 AssertRC(rc2); 1638 *phPipeR = NIL_RTPIPE; 1639 } 1640 return rc; 1641 } 1642 1643 /** 1644 * Try write some more data to the standard input of the child. 1645 * 1646 * @returns IPRT status code. 1647 * @param pStdInBuf The standard input buffer. 1648 * @param hStdInW The standard input pipe. 1649 */ 1650 static int txsDoExecHlpWriteStdIn(PTXSEXECSTDINBUF pStdInBuf, RTPIPE hStdInW) 1651 { 1652 size_t cbToWrite = pStdInBuf->cb - pStdInBuf->off; 1653 size_t cbWritten; 1654 int rc = RTPipeWrite(hStdInW, &pStdInBuf->pch[pStdInBuf->off], cbToWrite, &cbWritten); 1655 if (RT_SUCCESS(rc)) 1656 { 1657 Assert(cbWritten == cbToWrite); 1658 pStdInBuf->off += cbWritten; 1659 } 1660 return rc; 1661 } 1662 1663 /** 1664 * Handle an error event on standard input. 1665 * 1666 * @param hPollSet The polling set. 1667 * @param fPollEvt The event mask returned by RTPollNoResume. 1668 * @param phStdInW The standard input pipe handle. 1669 * @param pStdInBuf The standard input buffer. 1670 */ 1671 static void txsDoExecHlpHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW, 1672 PTXSEXECSTDINBUF pStdInBuf) 1673 { 1674 NOREF(fPollEvt); 1675 int rc2; 1676 if (pStdInBuf->off < pStdInBuf->cb) 1677 { 1678 rc2 = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN_WRITABLE); 1679 AssertRC(rc2); 1680 } 1681 1682 rc2 = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN); 1683 AssertRC(rc2); 1684 1685 rc2 = RTPipeClose(*phStdInW); 1686 AssertRC(rc2); 1687 *phStdInW = NIL_RTPIPE; 1688 1689 RTMemFree(pStdInBuf->pch); 1690 pStdInBuf->pch = NULL; 1691 pStdInBuf->off = 0; 1692 pStdInBuf->cb = 0; 1693 pStdInBuf->cbAllocated = 0; 1694 pStdInBuf->fBitBucket = true; 1695 } 1696 1697 /** 1698 * Handle an event indicating we can write to the standard input pipe of the 1699 * child process. 1700 * 1701 * @param hPollSet The polling set. 1702 * @param fPollEvt The event mask returned by RTPollNoResume. 1703 * @param phStdInW The standard input pipe. 1704 * @param pStdInBuf The standard input buffer. 1705 */ 1706 static void txsDoExecHlpHandleStdInWritableEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW, 1707 PTXSEXECSTDINBUF pStdInBuf) 1708 { 1709 int rc; 1710 if (!(fPollEvt & RTPOLL_EVT_ERROR)) 1711 { 1712 rc = txsDoExecHlpWriteStdIn(pStdInBuf, *phStdInW); 1713 if (RT_FAILURE(rc) && rc != VERR_BAD_PIPE) 1714 { 1715 /** @todo do we need to do something about this error condition? */ 1716 AssertRC(rc); 1717 } 1718 1719 if (pStdInBuf->off < pStdInBuf->cb) 1720 { 1721 rc = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN_WRITABLE); 1722 AssertRC(rc); 1723 } 1724 } 1725 else 1726 txsDoExecHlpHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf); 1727 } 1728 1729 /** 1730 * Handle a transport event or successful pfnPollIn() call. 1731 * 1732 * @returns IPRT status code from client send. 1733 * @retval VINF_EOF indicates ABORT command. 1734 * 1735 * @param hPollSet The polling set. 1736 * @param fPollEvt The event mask returned by RTPollNoResume. 1737 * @param idPollHnd The handle ID. 1738 * @param hStdInW The standard input pipe. 1739 * @param pStdInBuf The standard input buffer. 1740 */ 1741 static int txsDoExecHlpHandleTransportEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, uint32_t idPollHnd, 1742 PRTPIPE phStdInW, PTXSEXECSTDINBUF pStdInBuf) 1743 { 1744 /* ASSUMES the transport layer will detect or clear any error condition. */ 1745 NOREF(fPollEvt); NOREF(idPollHnd); 1746 Log(("txsDoExecHlpHandleTransportEvent\n")); 1747 /** @todo Use a callback for this case? */ 1748 1749 /* 1750 * Read client command packet and process it. 1751 */ 1752 /** @todo Sometimes this hangs on windows because there isn't any data pending. 1753 * We probably get woken up at the wrong time or in the wrong way, i.e. RTPoll() 1754 * is busted for sockets. 1755 * 1756 * Temporary workaround: Poll for input before trying to read it. */ 1757 if (!g_pTransport->pfnPollIn()) 1758 { 1759 Log(("Bad transport event\n")); 1760 RTThreadYield(); 1761 return VINF_SUCCESS; 1762 } 1763 PTXSPKTHDR pPktHdr; 1764 int rc = txsRecvPkt(&pPktHdr, false /*fAutoRetryOnFailure*/); 1765 if (RT_FAILURE(rc)) 1766 return rc; 1767 Log(("Bad transport event\n")); 1768 1769 /* 1770 * The most common thing here would be a STDIN request with data 1771 * for the child process. 1772 */ 1773 if (txsIsSameOpcode(pPktHdr, "STDIN")) 1774 { 1775 if ( !pStdInBuf->fBitBucket 1776 && pPktHdr->cb >= sizeof(TXSPKTHDR) + sizeof(uint32_t)) 1777 { 1778 uint32_t uCrc32 = *(uint32_t *)(pPktHdr + 1); 1779 const char *pch = (const char *)(pPktHdr + 1) + sizeof(uint32_t); 1780 size_t cb = pPktHdr->cb - sizeof(TXSPKTHDR) - sizeof(uint32_t); 1781 1782 /* Check the CRC */ 1783 pStdInBuf->uCrc32 = RTCrc32Process(pStdInBuf->uCrc32, pch, cb); 1784 if (pStdInBuf->uCrc32 == uCrc32) 1785 { 1786 1787 /* Rewind the buffer if it's empty. */ 1788 size_t cbInBuf = pStdInBuf->cb - pStdInBuf->off; 1789 bool const fAddToSet = cbInBuf == 0; 1790 if (fAddToSet) 1791 pStdInBuf->cb = pStdInBuf->off = 0; 1792 1793 /* Try and see if we can simply append the data. */ 1794 if (cb + pStdInBuf->cb <= pStdInBuf->cbAllocated) 1795 { 1796 memcpy(&pStdInBuf->pch[pStdInBuf->cb], pch, cb); 1797 pStdInBuf->cb += cb; 1798 rc = txsReplyAck(pPktHdr); 650 RTCritSectLeave(&g_CritSectClients); 1799 651 } 1800 652 else 1801 653 { 1802 /* Try write a bit or two before we move+realloc the buffer. */ 1803 if (cbInBuf > 0) 1804 txsDoExecHlpWriteStdIn(pStdInBuf, *phStdInW); 1805 1806 /* Move any buffered data to the front. */ 1807 cbInBuf = pStdInBuf->cb - pStdInBuf->off; 1808 if (cbInBuf == 0) 1809 pStdInBuf->cb = pStdInBuf->off = 0; 1810 else 654 /* Client sends a request, pick the right client and process it. */ 655 PUTSCLIENT pClient = papClients[uId - 1]; 656 if (fEvts & RTPOLL_EVT_READ) 657 rc = utsClientReqProcess(pClient); 658 659 if ( (fEvts & RTPOLL_EVT_ERROR) 660 || RT_FAILURE(rc)) 1811 661 { 1812 memmove(pStdInBuf->pch, &pStdInBuf->pch[pStdInBuf->off], cbInBuf); 1813 pStdInBuf->cb = cbInBuf; 1814 pStdInBuf->off = 0; 662 /* Close connection and remove client from array. */ 663 g_pTransport->pfnNotifyBye(pClient->pTransportClient); 664 papClients[uId - 1] = NULL; 665 cClientsCur--; 666 utsClientDestroy(pClient); 1815 667 } 1816 1817 /* Do we need to grow the buffer? */1818 if (cb + pStdInBuf->cb > pStdInBuf->cbAllocated)1819 {1820 size_t cbAlloc = pStdInBuf->cb + cb;1821 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);1822 void *pvNew = RTMemRealloc(pStdInBuf->pch, cbAlloc);1823 if (pvNew)1824 {1825 pStdInBuf->pch = (char *)pvNew;1826 pStdInBuf->cbAllocated = cbAlloc;1827 }1828 }1829 1830 /* Finally, copy the data. */1831 if (cb + pStdInBuf->cb <= pStdInBuf->cbAllocated)1832 {1833 memcpy(&pStdInBuf->pch[pStdInBuf->cb], pch, cb);1834 pStdInBuf->cb += cb;1835 rc = txsReplyAck(pPktHdr);1836 }1837 else1838 rc = txsReplySimple(pPktHdr, "STDINMEM");1839 }1840 1841 /*1842 * Flush the buffered data and add/remove the standard input1843 * handle from the set.1844 */1845 txsDoExecHlpWriteStdIn(pStdInBuf, *phStdInW);1846 if (fAddToSet && pStdInBuf->off < pStdInBuf->cb)1847 {1848 int rc2 = RTPollSetAddPipe(hPollSet, *phStdInW, RTPOLL_EVT_WRITE, TXSEXECHNDID_STDIN_WRITABLE);1849 AssertRC(rc2);1850 }1851 else if (!fAddToSet && pStdInBuf->off >= pStdInBuf->cb)1852 {1853 int rc2 = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN_WRITABLE);1854 AssertRC(rc2);1855 668 } 1856 669 } 1857 else1858 rc = txsReplySimple(pPktHdr, "STDINCRC");1859 670 } 1860 else if (pPktHdr->cb < sizeof(TXSPKTHDR) + sizeof(uint32_t)) 1861 rc = txsReplySimple(pPktHdr, "STDINBAD"); 671 } 672 673 RTPollSetDestroy(hPollSet); 674 675 return rc; 676 } 677 678 /** 679 * The main loop. 680 * 681 * @returns exit code. 682 */ 683 static RTEXITCODE utsMainLoop(void) 684 { 685 RTEXITCODE enmExitCode = RTEXITCODE_SUCCESS; 686 while (!g_fTerminate) 687 { 688 /* 689 * Wait for new connection and spin off a new thread 690 * for every new client. 691 */ 692 PUTSTRANSPORTCLIENT pTransportClient; 693 int rc = g_pTransport->pfnWaitForConnect(&pTransportClient); 694 if (RT_FAILURE(rc)) 695 continue; 696 697 /* 698 * New connection, create new client structure and spin of 699 * the request handling thread. 700 */ 701 PUTSCLIENT pClient = (PUTSCLIENT)RTMemAllocZ(sizeof(PUTSCLIENT)); 702 if (RT_LIKELY(pClient)) 703 { 704 pClient->pTransportClient = pTransportClient; 705 } 1862 706 else 1863 rc = txsReplySimple(pPktHdr, "STDINIGN");1864 }1865 /*1866 * The only other two requests are connection oriented and we return a error1867 * code so that we unwind the whole EXEC shebang and start afresh.1868 */1869 else if (txsIsSameOpcode(pPktHdr, "BYE"))1870 {1871 rc = txsDoBye(pPktHdr);1872 if (RT_SUCCESS(rc))1873 rc = VERR_NET_NOT_CONNECTED;1874 }1875 else if (txsIsSameOpcode(pPktHdr, "HOWDY"))1876 {1877 rc = txsDoHowdy(pPktHdr);1878 if (RT_SUCCESS(rc))1879 rc = VERR_NET_NOT_CONNECTED;1880 }1881 else if (txsIsSameOpcode(pPktHdr, "ABORT"))1882 {1883 rc = txsReplyAck(pPktHdr);1884 if (RT_SUCCESS(rc))1885 rc = VINF_EOF; /* this is but ugly! */1886 }1887 else1888 rc = txsReplyFailure(pPktHdr, "UNKNOWN ", "Opcode '%.8s' is not known or not recognized during EXEC", pPktHdr->achOpcode);1889 1890 RTMemFree(pPktHdr);1891 return rc;1892 }1893 1894 /**1895 * Handles the output and input of the process, waits for it finish up.1896 *1897 * @returns IPRT status code from reply send.1898 * @param pTxsExec The TXSEXEC instance.1899 */1900 static int txsDoExecHlp2(PTXSEXEC pTxsExec)1901 {1902 int rc; /* client send. */1903 int rc2;1904 TXSEXECSTDINBUF StdInBuf = { 0, 0, NULL, 0, pTxsExec->hStdInW == NIL_RTPIPE, RTCrc32Start() };1905 uint32_t uStdOutCrc32 = RTCrc32Start();1906 uint32_t uStdErrCrc32 = uStdOutCrc32;1907 uint32_t uTestPipeCrc32 = uStdOutCrc32;1908 uint64_t const MsStart = RTTimeMilliTS();1909 bool fProcessTimedOut = false;1910 uint64_t MsProcessKilled = UINT64_MAX;1911 RTMSINTERVAL const cMsPollBase = g_pTransport->pfnPollSetAdd || pTxsExec->hStdInW == NIL_RTPIPE1912 ? 5000 : 100;1913 RTMSINTERVAL cMsPollCur = 0;1914 1915 /*1916 * Before entering the loop, tell the client that we've started the guest1917 * and that it's now OK to send input to the process. (This is not the1918 * final ACK, so the packet header is NULL ... kind of bogus.)1919 */1920 rc = txsReplyAck(NULL);1921 1922 /*1923 * Process input, output, the test pipe and client requests.1924 */1925 while ( RT_SUCCESS(rc)1926 && RT_UNLIKELY(!g_fTerminate))1927 {1928 /*1929 * Wait/Process all pending events.1930 */1931 uint32_t idPollHnd;1932 uint32_t fPollEvt;1933 Log3(("Calling RTPollNoResume(,%u,)...\n", cMsPollCur));1934 rc2 = RTPollNoResume(pTxsExec->hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);1935 Log3(("RTPollNoResume -> fPollEvt=%#x idPollHnd=%u\n", fPollEvt, idPollHnd));1936 if (g_fTerminate)1937 continue;1938 cMsPollCur = 0; /* no rest until we've checked everything. */1939 1940 if (RT_SUCCESS(rc2))1941 707 { 1942 switch (idPollHnd) 1943 { 1944 case TXSEXECHNDID_STDOUT: 1945 rc = txsDoExecHlpHandleOutputEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdOutR, &uStdOutCrc32, 1946 TXSEXECHNDID_STDOUT, "STDOUT "); 1947 break; 1948 1949 case TXSEXECHNDID_STDERR: 1950 rc = txsDoExecHlpHandleOutputEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdErrR, &uStdErrCrc32, 1951 TXSEXECHNDID_STDERR, "STDERR "); 1952 break; 1953 1954 case TXSEXECHNDID_TESTPIPE: 1955 rc = txsDoExecHlpHandleOutputEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hTestPipeR, &uTestPipeCrc32, 1956 TXSEXECHNDID_TESTPIPE, "TESTPIPE"); 1957 break; 1958 1959 case TXSEXECHNDID_STDIN: 1960 txsDoExecHlpHandleStdInErrorEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdInW, &StdInBuf); 1961 break; 1962 1963 case TXSEXECHNDID_STDIN_WRITABLE: 1964 txsDoExecHlpHandleStdInWritableEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdInW, &StdInBuf); 1965 break; 1966 1967 case TXSEXECHNDID_THREAD: 1968 rc2 = RTPollSetRemove(pTxsExec->hPollSet, TXSEXECHNDID_THREAD); AssertRC(rc2); 1969 break; 1970 1971 default: 1972 rc = txsDoExecHlpHandleTransportEvent(pTxsExec->hPollSet, fPollEvt, idPollHnd, &pTxsExec->hStdInW, 1973 &StdInBuf); 1974 break; 1975 } 1976 if (RT_FAILURE(rc) || rc == VINF_EOF) 1977 break; /* abort command, or client dead or something */ 1978 continue; 708 RTMsgError("Creating new client structure failed with out of memory error\n"); 709 g_pTransport->pfnNotifyBye(pTransportClient); 1979 710 } 1980 711 1981 /* 1982 * Check for incoming data. 1983 */ 1984 if (g_pTransport->pfnPollIn()) 1985 { 1986 rc = txsDoExecHlpHandleTransportEvent(pTxsExec->hPollSet, 0, UINT32_MAX, &pTxsExec->hStdInW, &StdInBuf); 1987 if (RT_FAILURE(rc) || rc == VINF_EOF) 1988 break; /* abort command, or client dead or something */ 1989 continue; 1990 } 1991 1992 /* 1993 * If the process has terminated, we're should head out. 1994 */ 1995 if (!ASMAtomicReadBool(&pTxsExec->fProcessAlive)) 1996 break; 1997 1998 /* 1999 * Check for timed out, killing the process. 2000 */ 2001 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT; 2002 if (pTxsExec->cMsTimeout != RT_INDEFINITE_WAIT) 2003 { 2004 uint64_t u64Now = RTTimeMilliTS(); 2005 uint64_t cMsElapsed = u64Now - MsStart; 2006 if (cMsElapsed >= pTxsExec->cMsTimeout) 2007 { 2008 fProcessTimedOut = true; 2009 if ( MsProcessKilled == UINT64_MAX 2010 || u64Now - MsProcessKilled > 1000) 2011 { 2012 if (u64Now - MsProcessKilled > 20*60*1000) 2013 break; /* give up after 20 mins */ 2014 RTCritSectEnter(&pTxsExec->CritSect); 2015 if (pTxsExec->fProcessAlive) 2016 RTProcTerminate(pTxsExec->hProcess); 2017 RTCritSectLeave(&pTxsExec->CritSect); 2018 MsProcessKilled = u64Now; 2019 continue; 2020 } 2021 cMilliesLeft = 10000; 2022 } 2023 else 2024 cMilliesLeft = pTxsExec->cMsTimeout - (uint32_t)cMsElapsed; 2025 } 2026 2027 /* Reset the polling interval since we've done all pending work. */ 2028 cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft; 2029 } 2030 2031 /* 2032 * At this point we should hopefully only have to wait 0 ms on the thread 2033 * to release the handle... But if for instance the process refuses to die, 2034 * we'll have to try kill it again. Bothersome. 2035 */ 2036 for (size_t i = 0; i < 22; i++) 2037 { 2038 rc2 = RTThreadWait(pTxsExec->hThreadWaiter, 500, NULL); 712 713 } 714 715 return enmExitCode; 716 } 717 718 /** 719 * Initializes the global UTS state. 720 * 721 * @returns IPRT status code. 722 */ 723 static int utsInit(void) 724 { 725 int rc = VINF_SUCCESS; 726 727 RTListInit(&g_LstClientsNew); 728 729 rc = RTCritSectInit(&g_CritSectClients); 730 if (RT_SUCCESS(rc)) 731 { 732 rc = RTPipeCreate(&g_hPipeR, &g_hPipeW, 0); 2039 733 if (RT_SUCCESS(rc)) 2040 734 { 2041 pTxsExec->hThreadWaiter = NIL_RTTHREAD; 2042 Assert(!pTxsExec->fProcessAlive); 2043 break; 2044 } 2045 if (i == 0 || i == 10 || i == 15 || i == 18 || i > 20) 2046 { 2047 RTCritSectEnter(&pTxsExec->CritSect); 2048 if (pTxsExec->fProcessAlive) 2049 RTProcTerminate(pTxsExec->hProcess); 2050 RTCritSectLeave(&pTxsExec->CritSect); 2051 } 2052 } 2053 2054 /* 2055 * If we don't have a client problem (RT_FAILURE(rc) we'll reply to the 2056 * clients exec packet now. 2057 */ 2058 if (RT_SUCCESS(rc)) 2059 rc = txsExecSendExitStatus(pTxsExec, pTxsExec->fProcessAlive, fProcessTimedOut, MsProcessKilled); 2060 2061 RTMemFree(StdInBuf.pch); 2062 return rc; 2063 } 2064 2065 /** 2066 * Creates a poll set for the pipes and let the transport layer add stuff to it 2067 * as well. 2068 * 2069 * @returns IPRT status code, reply to client made on error. 2070 * @param pTxsExec The TXSEXEC instance. 2071 */ 2072 static int txsExecSetupPollSet(PTXSEXEC pTxsExec) 2073 { 2074 int rc = RTPollSetCreate(&pTxsExec->hPollSet); 2075 if (RT_FAILURE(rc)) 2076 return txsExecReplyRC(pTxsExec, rc, "RTPollSetCreate"); 2077 2078 rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hStdInW, RTPOLL_EVT_ERROR, TXSEXECHNDID_STDIN); 2079 if (RT_FAILURE(rc)) 2080 return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/stdin"); 2081 2082 rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 2083 TXSEXECHNDID_STDOUT); 2084 if (RT_FAILURE(rc)) 2085 return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/stdout"); 2086 2087 rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 2088 TXSEXECHNDID_STDERR); 2089 if (RT_FAILURE(rc)) 2090 return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/stderr"); 2091 2092 rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hTestPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 2093 TXSEXECHNDID_TESTPIPE); 2094 if (RT_FAILURE(rc)) 2095 return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/test"); 2096 2097 rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hWakeUpPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 2098 TXSEXECHNDID_THREAD); 2099 if (RT_FAILURE(rc)) 2100 return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/wakeup"); 2101 2102 if (g_pTransport->pfnPollSetAdd) 2103 { 2104 rc = g_pTransport->pfnPollSetAdd(pTxsExec->hPollSet, TXSEXECHNDID_TRANSPORT); 2105 if (RT_FAILURE(rc)) 2106 return txsExecReplyRC(pTxsExec, rc, "%s->pfnPollSetAdd/stdin", g_pTransport->szName); 2107 } 2108 2109 return VINF_SUCCESS; 2110 } 2111 2112 /** 2113 * Thread that calls RTProcWait and signals the main thread when it returns. 2114 * 2115 * The thread is created before the process is started and is waiting for a user 2116 * signal from the main thread before it calls RTProcWait. 2117 * 2118 * @returns VINF_SUCCESS (ignored). 2119 * @param hThreadSelf The thread handle. 2120 * @param pvUser The TXEEXEC structure. 2121 */ 2122 static DECLCALLBACK(int) txsExecWaitThreadProc(RTTHREAD hThreadSelf, void *pvUser) 2123 { 2124 PTXSEXEC pTxsExec = (PTXSEXEC)pvUser; 2125 2126 /* Wait for the go ahead... */ 2127 int rc = RTThreadUserWait(hThreadSelf, RT_INDEFINITE_WAIT); AssertRC(rc); 2128 2129 RTCritSectEnter(&pTxsExec->CritSect); 2130 for (;;) 2131 { 2132 RTCritSectLeave(&pTxsExec->CritSect); 2133 rc = RTProcWaitNoResume(pTxsExec->hProcess, RTPROCWAIT_FLAGS_BLOCK, &pTxsExec->ProcessStatus); 2134 RTCritSectEnter(&pTxsExec->CritSect); 2135 2136 /* If the pipe is NIL, the destructor wants us to get lost ASAP. */ 2137 if (pTxsExec->hWakeUpPipeW == NIL_RTPIPE) 2138 break; 2139 2140 if (RT_FAILURE(rc)) 2141 { 2142 rc = RTProcWait(pTxsExec->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &pTxsExec->ProcessStatus); 2143 if (rc == VERR_PROCESS_RUNNING) 2144 continue; 2145 2146 if (RT_FAILURE(rc)) 2147 { 2148 AssertRC(rc); 2149 pTxsExec->ProcessStatus.iStatus = rc; 2150 pTxsExec->ProcessStatus.enmReason = RTPROCEXITREASON_ABEND; 2151 } 2152 } 2153 2154 /* The process finished, signal the main thread over the pipe. */ 2155 ASMAtomicWriteBool(&pTxsExec->fProcessAlive, false); 2156 size_t cbIgnored; 2157 RTPipeWrite(pTxsExec->hWakeUpPipeW, "done", 4, &cbIgnored); 2158 RTPipeClose(pTxsExec->hWakeUpPipeW); 2159 pTxsExec->hWakeUpPipeW = NIL_RTPIPE; 2160 break; 2161 } 2162 RTCritSectLeave(&pTxsExec->CritSect); 2163 2164 return VINF_SUCCESS; 2165 } 2166 2167 /** 2168 * Sets up the thread that waits for the process to complete. 2169 * 2170 * @returns IPRT status code, reply to client made on error. 2171 * @param pTxsExec The TXSEXEC instance. 2172 */ 2173 static int txsExecSetupThread(PTXSEXEC pTxsExec) 2174 { 2175 int rc = RTPipeCreate(&pTxsExec->hWakeUpPipeR, &pTxsExec->hWakeUpPipeW, 0 /*fFlags*/); 2176 if (RT_FAILURE(rc)) 2177 { 2178 pTxsExec->hWakeUpPipeR = pTxsExec->hWakeUpPipeW = NIL_RTPIPE; 2179 return txsExecReplyRC(pTxsExec, rc, "RTPipeCreate/wait"); 2180 } 2181 2182 rc = RTThreadCreate(&pTxsExec->hThreadWaiter, txsExecWaitThreadProc, 2183 pTxsExec, 0 /*cbStack */, RTTHREADTYPE_DEFAULT, 2184 RTTHREADFLAGS_WAITABLE, "TxsProcW"); 2185 if (RT_FAILURE(rc)) 2186 { 2187 pTxsExec->hThreadWaiter = NIL_RTTHREAD; 2188 return txsExecReplyRC(pTxsExec, rc, "RTThreadCreate"); 2189 } 2190 2191 return VINF_SUCCESS; 2192 } 2193 2194 /** 2195 * Sets up the test pipe. 2196 * 2197 * @returns IPRT status code, reply to client made on error. 2198 * @param pTxsExec The TXSEXEC instance. 2199 * @param pszTestPipe How to set up the test pipe. 2200 */ 2201 static int txsExecSetupTestPipe(PTXSEXEC pTxsExec, const char *pszTestPipe) 2202 { 2203 if (strcmp(pszTestPipe, "|")) 2204 return VINF_SUCCESS; 2205 2206 int rc = RTPipeCreate(&pTxsExec->hTestPipeR, &pTxsExec->hTestPipeW, RTPIPE_C_INHERIT_WRITE); 2207 if (RT_FAILURE(rc)) 2208 { 2209 pTxsExec->hTestPipeR = pTxsExec->hTestPipeW = NIL_RTPIPE; 2210 return txsExecReplyRC(pTxsExec, rc, "RTPipeCreate/test/%s", pszTestPipe); 2211 } 2212 2213 char szVal[64]; 2214 RTStrPrintf(szVal, sizeof(szVal), "%#llx", (uint64_t)RTPipeToNative(pTxsExec->hTestPipeW)); 2215 rc = RTEnvSetEx(pTxsExec->hEnv, "IPRT_TEST_PIPE", szVal); 2216 if (RT_FAILURE(rc)) 2217 return txsExecReplyRC(pTxsExec, rc, "RTEnvSetEx/test/%s", pszTestPipe); 2218 2219 return VINF_SUCCESS; 2220 } 2221 2222 /** 2223 * Sets up the redirection / pipe / nothing for one of the standard handles. 2224 * 2225 * @returns IPRT status code, reply to client made on error. 2226 * @param pTxsExec The TXSEXEC instance. 2227 * @param pszHowTo How to set up this standard handle. 2228 * @param fd Which standard handle it is (0 == stdin, 1 == 2229 * stdout, 2 == stderr). 2230 * @param ph The generic handle that @a pph may be set 2231 * pointing to. Always set. 2232 * @param pph Pointer to the RTProcCreateExec argument. 2233 * Always set. 2234 * @param phPipe Where to return the end of the pipe that we 2235 * should service. Always set. 2236 */ 2237 static int txsExecSetupRedir(PTXSEXEC pTxsExec, const char *pszHowTo, const char *pszStdWhat, int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe) 2238 { 2239 ph->enmType = RTHANDLETYPE_PIPE; 2240 ph->u.hPipe = NIL_RTPIPE; 2241 *pph = NULL; 2242 *phPipe = NIL_RTPIPE; 2243 2244 int rc; 2245 if (!strcmp(pszHowTo, "|")) 2246 { 2247 /* 2248 * Setup a pipe for forwarding to/from the client. 2249 */ 2250 if (fd == 0) 2251 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ); 2252 else 2253 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE); 2254 if (RT_FAILURE(rc)) 2255 return txsExecReplyRC(pTxsExec, rc, "RTPipeCreate/%s/%s", pszStdWhat, pszHowTo); 2256 ph->enmType = RTHANDLETYPE_PIPE; 2257 *pph = ph; 2258 } 2259 else if (!strcmp(pszHowTo, "/dev/null")) 2260 { 2261 /* 2262 * Redirect to/from /dev/null. 2263 */ 2264 RTFILE hFile; 2265 rc = RTFileOpenBitBucket(&hFile, fd == 0 ? RTFILE_O_READ : RTFILE_O_WRITE); 2266 if (RT_FAILURE(rc)) 2267 return txsExecReplyRC(pTxsExec, rc, "RTFileOpenBitBucket/%s/%s", pszStdWhat, pszHowTo); 2268 2269 ph->enmType = RTHANDLETYPE_FILE; 2270 ph->u.hFile = hFile; 2271 *pph = ph; 2272 } 2273 else if (*pszHowTo) 2274 { 2275 /* 2276 * Redirect to/from file. 2277 */ 2278 uint32_t fFlags; 2279 if (fd == 0) 2280 fFlags = RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN; 2281 else 2282 { 2283 if (pszHowTo[0] != '>' || pszHowTo[1] != '>') 2284 fFlags = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE; 735 /* Spin off the thread serving connections. */ 736 rc = RTThreadCreate(&g_hThreadServing, utsClientWorker, NULL, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, 737 "USBTSTSRV"); 738 if (RT_SUCCESS(rc)) 739 return VINF_SUCCESS; 2285 740 else 2286 { 2287 /* append */ 2288 pszHowTo += 2; 2289 fFlags = RTFILE_O_WRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND; 2290 } 2291 } 2292 2293 RTFILE hFile; 2294 rc = RTFileOpen(&hFile, pszHowTo, fFlags); 2295 if (RT_FAILURE(rc)) 2296 return txsExecReplyRC(pTxsExec, rc, "RTFileOpen/%s/%s", pszStdWhat, pszHowTo); 2297 2298 ph->enmType = RTHANDLETYPE_FILE; 2299 ph->u.hFile = hFile; 2300 *pph = ph; 2301 } 2302 else 2303 /* same as parent (us) */ 2304 rc = VINF_SUCCESS; 2305 return rc; 2306 } 2307 2308 /** 2309 * Create the environment. 2310 * 2311 * @returns IPRT status code, reply to client made on error. 2312 * @param pTxsExec The TXSEXEC instance. 2313 * @param cEnvVars The number of environment variables. 2314 * @param papszEnv The environment variables (var=value). 2315 */ 2316 static int txsExecSetupEnv(PTXSEXEC pTxsExec, uint32_t cEnvVars, const char * const *papszEnv) 2317 { 2318 /* 2319 * Create the environment. 2320 */ 2321 int rc = RTEnvClone(&pTxsExec->hEnv, RTENV_DEFAULT); 2322 if (RT_FAILURE(rc)) 2323 return txsExecReplyRC(pTxsExec, rc, "RTEnvClone"); 2324 2325 for (size_t i = 0; i < cEnvVars; i++) 2326 { 2327 rc = RTEnvPutEx(pTxsExec->hEnv, papszEnv[i]); 2328 if (RT_FAILURE(rc)) 2329 return txsExecReplyRC(pTxsExec, rc, "RTEnvPutEx(,'%s')", papszEnv[i]); 2330 } 2331 return VINF_SUCCESS; 2332 } 2333 2334 /** 2335 * Deletes the TXSEXEC structure and frees the memory backing it. 2336 * 2337 * @param pTxsExec The structure to destroy. 2338 */ 2339 static void txsExecDestroy(PTXSEXEC pTxsExec) 2340 { 2341 int rc2; 2342 2343 rc2 = RTEnvDestroy(pTxsExec->hEnv); AssertRC(rc2); 2344 pTxsExec->hEnv = NIL_RTENV; 2345 rc2 = RTPipeClose(pTxsExec->hTestPipeW); AssertRC(rc2); 2346 pTxsExec->hTestPipeW = NIL_RTPIPE; 2347 rc2 = RTHandleClose(pTxsExec->StdErr.phChild); AssertRC(rc2); 2348 pTxsExec->StdErr.phChild = NULL; 2349 rc2 = RTHandleClose(pTxsExec->StdOut.phChild); AssertRC(rc2); 2350 pTxsExec->StdOut.phChild = NULL; 2351 rc2 = RTHandleClose(pTxsExec->StdIn.phChild); AssertRC(rc2); 2352 pTxsExec->StdIn.phChild = NULL; 2353 2354 rc2 = RTPipeClose(pTxsExec->hTestPipeR); AssertRC(rc2); 2355 pTxsExec->hTestPipeR = NIL_RTPIPE; 2356 rc2 = RTPipeClose(pTxsExec->hStdErrR); AssertRC(rc2); 2357 pTxsExec->hStdErrR = NIL_RTPIPE; 2358 rc2 = RTPipeClose(pTxsExec->hStdOutR); AssertRC(rc2); 2359 pTxsExec->hStdOutR = NIL_RTPIPE; 2360 rc2 = RTPipeClose(pTxsExec->hStdInW); AssertRC(rc2); 2361 pTxsExec->hStdInW = NIL_RTPIPE; 2362 2363 RTPollSetDestroy(pTxsExec->hPollSet); 2364 pTxsExec->hPollSet = NIL_RTPOLLSET; 2365 2366 /* 2367 * If the process is still running we're in a bit of a fix... Try kill it, 2368 * although that's potentially racing process termination and reusage of 2369 * the pid. 2370 */ 2371 RTCritSectEnter(&pTxsExec->CritSect); 2372 2373 RTPipeClose(pTxsExec->hWakeUpPipeW); 2374 pTxsExec->hWakeUpPipeW = NIL_RTPIPE; 2375 RTPipeClose(pTxsExec->hWakeUpPipeR); 2376 pTxsExec->hWakeUpPipeR = NIL_RTPIPE; 2377 2378 if ( pTxsExec->hProcess != NIL_RTPROCESS 2379 && pTxsExec->fProcessAlive) 2380 RTProcTerminate(pTxsExec->hProcess); 2381 2382 RTCritSectLeave(&pTxsExec->CritSect); 2383 2384 int rcThread = VINF_SUCCESS; 2385 if (pTxsExec->hThreadWaiter != NIL_RTTHREAD) 2386 rcThread = RTThreadWait(pTxsExec->hThreadWaiter, 5000, NULL); 2387 if (RT_SUCCESS(rcThread)) 2388 { 2389 pTxsExec->hThreadWaiter = NIL_RTTHREAD; 2390 RTCritSectDelete(&pTxsExec->CritSect); 2391 RTMemFree(pTxsExec); 2392 } 2393 /* else: leak it or RTThreadWait may cause heap corruption later. */ 2394 } 2395 2396 /** 2397 * Initializes the TXSEXEC structure. 2398 * 2399 * @returns VINF_SUCCESS and non-NULL *ppTxsExec on success, reply send status 2400 * and *ppTxsExec set to NULL on failure. 2401 * @param pPktHdr The exec packet. 2402 * @param cMsTimeout The time parameter. 2403 * @param ppTxsExec Where to return the structure. 2404 */ 2405 static int txsExecCreate(PCTXSPKTHDR pPktHdr, RTMSINTERVAL cMsTimeout, PTXSEXEC *ppTxsExec) 2406 { 2407 *ppTxsExec = NULL; 2408 2409 /* 2410 * Allocate the basic resources. 2411 */ 2412 PTXSEXEC pTxsExec = (PTXSEXEC)RTMemAlloc(sizeof(*pTxsExec)); 2413 if (!pTxsExec) 2414 return txsReplyRC(pPktHdr, VERR_NO_MEMORY, "RTMemAlloc(%zu)", sizeof(*pTxsExec)); 2415 int rc = RTCritSectInit(&pTxsExec->CritSect); 2416 if (RT_FAILURE(rc)) 2417 { 2418 RTMemFree(pTxsExec); 2419 return txsReplyRC(pPktHdr, rc, "RTCritSectInit"); 2420 } 2421 2422 /* 2423 * Initialize the member to NIL values. 2424 */ 2425 pTxsExec->pPktHdr = pPktHdr; 2426 pTxsExec->cMsTimeout = cMsTimeout; 2427 pTxsExec->rcReplySend = VINF_SUCCESS; 2428 2429 pTxsExec->hPollSet = NIL_RTPOLLSET; 2430 pTxsExec->hStdInW = NIL_RTPIPE; 2431 pTxsExec->hStdOutR = NIL_RTPIPE; 2432 pTxsExec->hStdErrR = NIL_RTPIPE; 2433 pTxsExec->hTestPipeR = NIL_RTPIPE; 2434 pTxsExec->hWakeUpPipeR = NIL_RTPIPE; 2435 pTxsExec->hThreadWaiter = NIL_RTTHREAD; 2436 2437 pTxsExec->StdIn.phChild = NULL; 2438 pTxsExec->StdOut.phChild = NULL; 2439 pTxsExec->StdErr.phChild = NULL; 2440 pTxsExec->hTestPipeW = NIL_RTPIPE; 2441 pTxsExec->hEnv = NIL_RTENV; 2442 2443 pTxsExec->hProcess = NIL_RTPROCESS; 2444 pTxsExec->ProcessStatus.iStatus = 254; 2445 pTxsExec->ProcessStatus.enmReason = RTPROCEXITREASON_ABEND; 2446 pTxsExec->fProcessAlive = false; 2447 pTxsExec->hWakeUpPipeW = NIL_RTPIPE; 2448 2449 *ppTxsExec = pTxsExec; 2450 return VINF_SUCCESS; 2451 } 2452 2453 /** 2454 * txsDoExec helper that takes over when txsDoExec has expanded the packet. 2455 * 2456 * @returns IPRT status code from send. 2457 * @param pPktHdr The exec packet. 2458 * @param fFlags Flags, reserved for future use. 2459 * @param pszExecName The executable name. 2460 * @param cArgs The argument count. 2461 * @param papszArgs The arguments. 2462 * @param cEnvVars The environment variable count. 2463 * @param papszEnv The environment variables. 2464 * @param pszStdIn How to deal with standard in. 2465 * @param pszStdOut How to deal with standard out. 2466 * @param pszStdErr How to deal with standard err. 2467 * @param pszTestPipe How to deal with the test pipe. 2468 * @param pszUsername The user to run the program as. 2469 * @param cMillies The process time limit in milliseconds. 2470 */ 2471 static int txsDoExecHlp(PCTXSPKTHDR pPktHdr, uint32_t fFlags, const char *pszExecName, 2472 uint32_t cArgs, const char * const *papszArgs, 2473 uint32_t cEnvVars, const char * const *papszEnv, 2474 const char *pszStdIn, const char *pszStdOut, const char *pszStdErr, const char *pszTestPipe, 2475 const char *pszUsername, RTMSINTERVAL cMillies) 2476 { 2477 int rc2; 2478 2479 /* 2480 * Input validation, filter out things we don't yet support.. 2481 */ 2482 Assert(!fFlags); 2483 if (!*pszExecName) 2484 return txsReplyFailure(pPktHdr, "STR ZERO", "Executable name is empty"); 2485 if (!*pszStdIn) 2486 return txsReplyFailure(pPktHdr, "STR ZERO", "The stdin howto is empty"); 2487 if (!*pszStdOut) 2488 return txsReplyFailure(pPktHdr, "STR ZERO", "The stdout howto is empty"); 2489 if (!*pszStdErr) 2490 return txsReplyFailure(pPktHdr, "STR ZERO", "The stderr howto is empty"); 2491 if (!*pszTestPipe) 2492 return txsReplyFailure(pPktHdr, "STR ZERO", "The testpipe howto is empty"); 2493 if (strcmp(pszTestPipe, "|") && strcmp(pszTestPipe, "/dev/null")) 2494 return txsReplyFailure(pPktHdr, "BAD TSTP", "Only \"|\" and \"/dev/null\" are allowed as testpipe howtos ('%s')", 2495 pszTestPipe); 2496 if (*pszUsername) 2497 return txsReplyFailure(pPktHdr, "NOT IMPL", "Executing as a specific user is not implemented ('%s')", pszUsername); 2498 2499 /* 2500 * Prepare for process launch. 2501 */ 2502 PTXSEXEC pTxsExec; 2503 int rc = txsExecCreate(pPktHdr, cMillies, &pTxsExec); 2504 if (pTxsExec == NULL) 2505 return rc; 2506 rc = txsExecSetupEnv(pTxsExec, cEnvVars, papszEnv); 2507 if (RT_SUCCESS(rc)) 2508 rc = txsExecSetupRedir(pTxsExec, pszStdIn, "StdIn", 0, &pTxsExec->StdIn.hChild, &pTxsExec->StdIn.phChild, &pTxsExec->hStdInW); 2509 if (RT_SUCCESS(rc)) 2510 rc = txsExecSetupRedir(pTxsExec, pszStdOut, "StdOut", 1, &pTxsExec->StdOut.hChild, &pTxsExec->StdOut.phChild, &pTxsExec->hStdOutR); 2511 if (RT_SUCCESS(rc)) 2512 rc = txsExecSetupRedir(pTxsExec, pszStdErr, "StdErr", 2, &pTxsExec->StdErr.hChild, &pTxsExec->StdErr.phChild, &pTxsExec->hStdErrR); 2513 if (RT_SUCCESS(rc)) 2514 rc = txsExecSetupTestPipe(pTxsExec, pszTestPipe); 2515 if (RT_SUCCESS(rc)) 2516 rc = txsExecSetupThread(pTxsExec); 2517 if (RT_SUCCESS(rc)) 2518 rc = txsExecSetupPollSet(pTxsExec); 2519 if (RT_SUCCESS(rc)) 2520 { 2521 /* 2522 * Create the process. 2523 */ 2524 if (g_fDisplayOutput) 2525 { 2526 RTPrintf("txs: Executing \"%s\": ", pszExecName); 2527 for (uint32_t i = 0; i < cArgs; i++) 2528 RTPrintf(" \"%s\"", papszArgs[i]); 2529 RTPrintf("\n"); 2530 } 2531 rc = RTProcCreateEx(pszExecName, papszArgs, pTxsExec->hEnv, 0 /*fFlags*/, 2532 pTxsExec->StdIn.phChild, pTxsExec->StdOut.phChild, pTxsExec->StdErr.phChild, 2533 *pszUsername ? pszUsername : NULL, NULL, 2534 &pTxsExec->hProcess); 2535 if (RT_SUCCESS(rc)) 2536 { 2537 ASMAtomicWriteBool(&pTxsExec->fProcessAlive, true); 2538 rc2 = RTThreadUserSignal(pTxsExec->hThreadWaiter); AssertRC(rc2); 2539 2540 /* 2541 * Close the child ends of any pipes and redirected files. 2542 */ 2543 rc2 = RTHandleClose(pTxsExec->StdIn.phChild); AssertRC(rc2); 2544 pTxsExec->StdIn.phChild = NULL; 2545 rc2 = RTHandleClose(pTxsExec->StdOut.phChild); AssertRC(rc2); 2546 pTxsExec->StdOut.phChild = NULL; 2547 rc2 = RTHandleClose(pTxsExec->StdErr.phChild); AssertRC(rc2); 2548 pTxsExec->StdErr.phChild = NULL; 2549 rc2 = RTPipeClose(pTxsExec->hTestPipeW); AssertRC(rc2); 2550 pTxsExec->hTestPipeW = NIL_RTPIPE; 2551 2552 /* 2553 * Let another worker function funnel output and input to the 2554 * client as well as the process exit code. 2555 */ 2556 rc = txsDoExecHlp2(pTxsExec); 741 RTMsgError("Creating the client worker thread failed with %Rrc\n", rc); 742 743 RTPipeClose(g_hPipeR); 744 RTPipeClose(g_hPipeW); 2557 745 } 2558 746 else 2559 rc = txsReplyFailure(pPktHdr, "FAILED ", "Executing process \"%s\" failed with %Rrc", 2560 pszExecName, rc); 747 RTMsgError("Creating communications pipe failed with %Rrc\n", rc); 748 749 RTCritSectDelete(&g_CritSectClients); 2561 750 } 2562 751 else 2563 rc = pTxsExec->rcReplySend;2564 txsExecDestroy(pTxsExec); 752 RTMsgError("Creating global critical section failed with %Rrc\n", rc); 753 2565 754 return rc; 2566 755 } 2567 756 2568 757 /** 2569 * Execute a program.2570 *2571 * @returns IPRT status code from send.2572 * @param pPktHdr The exec packet.2573 */2574 static int txsDoExec(PCTXSPKTHDR pPktHdr)2575 {2576 /*2577 * This packet has a lot of parameters, most of which are zero terminated2578 * strings. The strings used in items 7 thru 10 are either file names,2579 * "/dev/null" or a pipe char (|).2580 *2581 * Packet content:2582 * 1. Flags reserved for future use (32-bit unsigned).2583 * 2. The executable name (string).2584 * 3. The argument count given as a 32-bit unsigned integer.2585 * 4. The arguments strings.2586 * 5. The number of environment strings (32-bit unsigned).2587 * 6. The environment strings (var=val) to apply the environment.2588 * 7. What to do about standard in (string).2589 * 8. What to do about standard out (string).2590 * 9. What to do about standard err (string).2591 * 10. What to do about the test pipe (string).2592 * 11. The name of the user to run the program as (string). Empty string2593 * means running it as the current user.2594 * 12. Process time limit in milliseconds (32-bit unsigned). Max == no limit.2595 */2596 size_t const cbMin = sizeof(TXSPKTHDR)2597 + sizeof(uint32_t) /* flags */ + 22598 + sizeof(uint32_t) /* argc */ + 2 /* argv */2599 + sizeof(uint32_t) + 0 /* environ */2600 + 4 * 12601 + sizeof(uint32_t) /* timeout */;2602 if (pPktHdr->cb < cbMin)2603 return txsReplyBadMinSize(pPktHdr, cbMin);2604 2605 /* unpack the packet */2606 char const *pchEnd = (char const *)pPktHdr + pPktHdr->cb;2607 char const *pch = (char const *)(pPktHdr + 1); /* cursor */2608 2609 /* 1. flags */2610 uint32_t const fFlags = *(uint32_t const *)pch;2611 pch += sizeof(uint32_t);2612 if (fFlags != 0)2613 return txsReplyFailure(pPktHdr, "BAD FLAG", "Invalid EXEC flags %#x, expected 0", fFlags);2614 2615 /* 2. exec name */2616 int rc;2617 char *pszExecName = NULL;2618 if (!txsIsStringValid(pPktHdr, "execname", pch, &pszExecName, &pch, &rc))2619 return rc;2620 2621 /* 3. argc */2622 uint32_t const cArgs = (size_t)(pchEnd - pch) > sizeof(uint32_t) ? *(uint32_t const *)pch : 0xff;2623 pch += sizeof(uint32_t);2624 if (cArgs * 1 >= (size_t)(pchEnd - pch))2625 rc = txsReplyFailure(pPktHdr, "BAD ARGC", "Bad or missing argument count (%#x)", cArgs);2626 else if (cArgs > 128)2627 rc = txsReplyFailure(pPktHdr, "BAD ARGC", "Too many arguments (%#x)", cArgs);2628 else2629 {2630 char **papszArgs = (char **)RTMemTmpAllocZ(sizeof(char *) * (cArgs + 1));2631 if (papszArgs)2632 {2633 /* 4. argv */2634 bool fOk = true;2635 for (size_t i = 0; i < cArgs && fOk; i++)2636 {2637 fOk = txsIsStringValid(pPktHdr, "argvN", pch, &papszArgs[i], &pch, &rc);2638 if (!fOk)2639 break;2640 }2641 if (fOk)2642 {2643 /* 5. cEnvVars */2644 uint32_t const cEnvVars = (size_t)(pchEnd - pch) > sizeof(uint32_t) ? *(uint32_t const *)pch : 0xfff;2645 pch += sizeof(uint32_t);2646 if (cEnvVars * 1 >= (size_t)(pchEnd - pch))2647 rc = txsReplyFailure(pPktHdr, "BAD ENVC", "Bad or missing environment variable count (%#x)", cEnvVars);2648 else if (cEnvVars > 256)2649 rc = txsReplyFailure(pPktHdr, "BAD ENVC", "Too many environment variables (%#x)", cEnvVars);2650 else2651 {2652 char **papszEnv = (char **)RTMemTmpAllocZ(sizeof(char *) * (cEnvVars + 1));2653 if (papszEnv)2654 {2655 /* 6. environ */2656 for (size_t i = 0; i < cEnvVars && fOk; i++)2657 {2658 fOk = txsIsStringValid(pPktHdr, "envN", pch, &papszEnv[i], &pch, &rc);2659 if (!fOk) /* Bail out on error. */2660 break;2661 }2662 if (fOk)2663 {2664 /* 7. stdout */2665 char *pszStdIn;2666 if (txsIsStringValid(pPktHdr, "stdin", pch, &pszStdIn, &pch, &rc))2667 {2668 /* 8. stdout */2669 char *pszStdOut;2670 if (txsIsStringValid(pPktHdr, "stdout", pch, &pszStdOut, &pch, &rc))2671 {2672 /* 9. stderr */2673 char *pszStdErr;2674 if (txsIsStringValid(pPktHdr, "stderr", pch, &pszStdErr, &pch, &rc))2675 {2676 /* 10. testpipe */2677 char *pszTestPipe;2678 if (txsIsStringValid(pPktHdr, "testpipe", pch, &pszTestPipe, &pch, &rc))2679 {2680 /* 11. username */2681 char *pszUsername;2682 if (txsIsStringValid(pPktHdr, "username", pch, &pszUsername, &pch, &rc))2683 {2684 /** @todo No password value? */2685 2686 /* 12. time limit */2687 uint32_t const cMillies = (size_t)(pchEnd - pch) >= sizeof(uint32_t)2688 ? *(uint32_t const *)pch2689 : 0;2690 if ((size_t)(pchEnd - pch) > sizeof(uint32_t))2691 rc = txsReplyFailure(pPktHdr, "BAD END ", "Timeout argument not at end of packet (%#x)", (size_t)(pchEnd - pch));2692 else if ((size_t)(pchEnd - pch) < sizeof(uint32_t))2693 rc = txsReplyFailure(pPktHdr, "BAD NOTO", "No timeout argument");2694 else if (cMillies < 1000)2695 rc = txsReplyFailure(pPktHdr, "BAD TO ", "Timeout is less than a second (%#x)", cMillies);2696 else2697 {2698 pch += sizeof(uint32_t);2699 2700 /*2701 * Time to employ a helper here before we go way beyond2702 * the right margin...2703 */2704 rc = txsDoExecHlp(pPktHdr, fFlags, pszExecName,2705 cArgs, papszArgs,2706 cEnvVars, papszEnv,2707 pszStdIn, pszStdOut, pszStdErr, pszTestPipe,2708 pszUsername,2709 cMillies == UINT32_MAX ? RT_INDEFINITE_WAIT : cMillies);2710 }2711 RTStrFree(pszUsername);2712 }2713 RTStrFree(pszTestPipe);2714 }2715 RTStrFree(pszStdErr);2716 }2717 RTStrFree(pszStdOut);2718 }2719 RTStrFree(pszStdIn);2720 }2721 }2722 for (size_t i = 0; i < cEnvVars; i++)2723 RTStrFree(papszEnv[i]);2724 RTMemTmpFree(papszEnv);2725 }2726 else2727 rc = txsReplyFailure(pPktHdr, "NO MEM ", "Failed to allocate %zu bytes environ", sizeof(char *) * (cEnvVars + 1));2728 }2729 }2730 for (size_t i = 0; i < cArgs; i++)2731 RTStrFree(papszArgs[i]);2732 RTMemTmpFree(papszArgs);2733 }2734 else2735 rc = txsReplyFailure(pPktHdr, "NO MEM ", "Failed to allocate %zu bytes for argv", sizeof(char *) * (cArgs + 1));2736 }2737 RTStrFree(pszExecName);2738 2739 return rc;2740 }2741 2742 /**2743 * The main loop.2744 *2745 * @returns exit code.2746 */2747 static RTEXITCODE txsMainLoop(void)2748 {2749 RTEXITCODE enmExitCode = RTEXITCODE_SUCCESS;2750 while (!g_fTerminate)2751 {2752 /*2753 * Read client command packet and process it.2754 */2755 PTXSPKTHDR pPktHdr;2756 int rc = txsRecvPkt(&pPktHdr, true /*fAutoRetryOnFailure*/);2757 if (RT_FAILURE(rc))2758 continue;2759 2760 /*2761 * Do a string switch on the opcode bit.2762 */2763 /* Connection: */2764 if ( txsIsSameOpcode(pPktHdr, "HOWDY "))2765 rc = txsDoHowdy(pPktHdr);2766 else if (txsIsSameOpcode(pPktHdr, "BYE "))2767 rc = txsDoBye(pPktHdr);2768 else if (txsIsSameOpcode(pPktHdr, "UUID "))2769 rc = txsDoUuid(pPktHdr);2770 /* Process: */2771 else if (txsIsSameOpcode(pPktHdr, "EXEC "))2772 rc = txsDoExec(pPktHdr);2773 /* Admin: */2774 else if (txsIsSameOpcode(pPktHdr, "REBOOT "))2775 rc = txsDoReboot(pPktHdr);2776 else if (txsIsSameOpcode(pPktHdr, "SHUTDOWN"))2777 rc = txsDoShutdown(pPktHdr);2778 /* CD/DVD control: */2779 else if (txsIsSameOpcode(pPktHdr, "CD EJECT"))2780 rc = txsDoCdEject(pPktHdr);2781 /* File system: */2782 else if (txsIsSameOpcode(pPktHdr, "CLEANUP "))2783 rc = txsDoCleanup(pPktHdr);2784 else if (txsIsSameOpcode(pPktHdr, "MKDIR "))2785 rc = txsDoMkDir(pPktHdr);2786 else if (txsIsSameOpcode(pPktHdr, "MKDRPATH"))2787 rc = txsDoMkDrPath(pPktHdr);2788 else if (txsIsSameOpcode(pPktHdr, "MKSYMLNK"))2789 rc = txsDoMkSymlnk(pPktHdr);2790 else if (txsIsSameOpcode(pPktHdr, "RMDIR "))2791 rc = txsDoRmDir(pPktHdr);2792 else if (txsIsSameOpcode(pPktHdr, "RMFILE "))2793 rc = txsDoRmFile(pPktHdr);2794 else if (txsIsSameOpcode(pPktHdr, "RMSYMLNK"))2795 rc = txsDoRmSymlnk(pPktHdr);2796 else if (txsIsSameOpcode(pPktHdr, "RMTREE "))2797 rc = txsDoRmTree(pPktHdr);2798 else if (txsIsSameOpcode(pPktHdr, "CHMOD "))2799 rc = txsDoChMod(pPktHdr);2800 else if (txsIsSameOpcode(pPktHdr, "CHOWN "))2801 rc = txsDoChOwn(pPktHdr);2802 else if (txsIsSameOpcode(pPktHdr, "CHGRP "))2803 rc = txsDoChGrp(pPktHdr);2804 else if (txsIsSameOpcode(pPktHdr, "ISDIR "))2805 rc = txsDoIsDir(pPktHdr);2806 else if (txsIsSameOpcode(pPktHdr, "ISFILE "))2807 rc = txsDoIsFile(pPktHdr);2808 else if (txsIsSameOpcode(pPktHdr, "ISSYMLNK"))2809 rc = txsDoIsSymlnk(pPktHdr);2810 else if (txsIsSameOpcode(pPktHdr, "STAT "))2811 rc = txsDoStat(pPktHdr);2812 else if (txsIsSameOpcode(pPktHdr, "LSTAT "))2813 rc = txsDoLStat(pPktHdr);2814 else if (txsIsSameOpcode(pPktHdr, "LIST "))2815 rc = txsDoList(pPktHdr);2816 else if (txsIsSameOpcode(pPktHdr, "PUT FILE"))2817 rc = txsDoPutFile(pPktHdr);2818 else if (txsIsSameOpcode(pPktHdr, "GET FILE"))2819 rc = txsDoGetFile(pPktHdr);2820 #ifndef RT_OS_WINDOWS2821 else if (txsIsSameOpcode(pPktHdr, "UNPKFILE"))2822 rc = txsDoUnpackFile(pPktHdr);2823 #endif2824 /* Misc: */2825 else2826 rc = txsReplyUnknown(pPktHdr);2827 2828 RTMemFree(pPktHdr);2829 }2830 2831 return enmExitCode;2832 }2833 2834 2835 /**2836 * Finalizes the scratch directory, making sure it exists.2837 *2838 * @returns exit code.2839 */2840 static RTEXITCODE txsFinalizeScratch(void)2841 {2842 RTPathStripTrailingSlash(g_szScratchPath);2843 char *pszFilename = RTPathFilename(g_szScratchPath);2844 if (!pszFilename)2845 return RTMsgErrorExit(RTEXITCODE_FAILURE, "cannot use root for scratch (%s)\n", g_szScratchPath);2846 2847 int rc;2848 if (strchr(pszFilename, 'X'))2849 {2850 char ch = *pszFilename;2851 rc = RTDirCreateFullPath(g_szScratchPath, 0700);2852 *pszFilename = ch;2853 if (RT_SUCCESS(rc))2854 rc = RTDirCreateTemp(g_szScratchPath, 0700);2855 }2856 else2857 {2858 if (RTDirExists(g_szScratchPath))2859 rc = VINF_SUCCESS;2860 else2861 rc = RTDirCreateFullPath(g_szScratchPath, 0700);2862 }2863 if (RT_FAILURE(rc))2864 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create scratch directory: %Rrc (%s)\n", rc, g_szScratchPath);2865 return RTEXITCODE_SUCCESS;2866 }2867 2868 /**2869 * Attempts to complete an upgrade by updating the original and relaunching2870 * ourselves from there again.2871 *2872 * On failure, we'll continue running as the temporary copy.2873 *2874 * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.2875 * @param argc The number of arguments.2876 * @param argv The argument vector.2877 * @param pfExit For indicating exit when the exit code is zero.2878 */2879 static RTEXITCODE txsAutoUpdateStage2(int argc, char **argv, bool *pfExit, const char *pszUpgrading)2880 {2881 /*2882 * Copy the current executable onto the original.2883 * Note that we're racing the original program on some platforms, thus the2884 * 60 sec sleep mess.2885 */2886 char szUpgradePath[RTPATH_MAX];2887 if (!RTProcGetExecutablePath(szUpgradePath, sizeof(szUpgradePath)))2888 {2889 RTMsgError("RTProcGetExecutablePath failed (step 2)\n");2890 return RTEXITCODE_SUCCESS;2891 }2892 void *pvUpgrade;2893 size_t cbUpgrade;2894 int rc = RTFileReadAll(szUpgradePath, &pvUpgrade, &cbUpgrade);2895 if (RT_FAILURE(rc))2896 {2897 RTMsgError("RTFileReadAllEx(\"%s\"): %Rrc (step 2)\n", szUpgradePath, rc);2898 return RTEXITCODE_SUCCESS;2899 }2900 2901 uint64_t StartMilliTS = RTTimeMilliTS();2902 RTFILE hFile;2903 rc = RTFileOpen(&hFile, pszUpgrading,2904 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE2905 | (0755 << RTFILE_O_CREATE_MODE_SHIFT));2906 while ( RT_FAILURE(rc)2907 && RTTimeMilliTS() - StartMilliTS < 60000)2908 {2909 RTThreadSleep(1000);2910 rc = RTFileOpen(&hFile, pszUpgrading,2911 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE2912 | (0755 << RTFILE_O_CREATE_MODE_SHIFT));2913 }2914 if (RT_SUCCESS(rc))2915 {2916 rc = RTFileWrite(hFile, pvUpgrade, cbUpgrade, NULL);2917 RTFileClose(hFile);2918 if (RT_SUCCESS(rc))2919 {2920 /*2921 * Relaunch the service with the original name, foricbly barring2922 * further upgrade cycles in case of bugs (and simplifying the code).2923 */2924 const char **papszArgs = (const char **)RTMemAlloc((argc + 1 + 1) * sizeof(char **));2925 if (papszArgs)2926 {2927 papszArgs[0] = pszUpgrading;2928 for (int i = 1; i < argc; i++)2929 papszArgs[i] = argv[i];2930 papszArgs[argc] = "--no-auto-upgrade";2931 papszArgs[argc + 1] = NULL;2932 2933 RTMsgInfo("Launching upgraded image: \"%s\"\n", pszUpgrading);2934 RTPROCESS hProc;2935 rc = RTProcCreate(pszUpgrading, papszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &hProc);2936 if (RT_SUCCESS(rc))2937 *pfExit = true;2938 else2939 RTMsgError("RTProcCreate(\"%s\"): %Rrc (upgrade stage 2)\n", pszUpgrading, rc);2940 RTMemFree(papszArgs);2941 }2942 else2943 RTMsgError("RTMemAlloc failed during upgrade attempt (stage 2)\n");2944 }2945 else2946 RTMsgError("RTFileWrite(%s,,%zu): %Rrc (step 2) - BAD\n", pszUpgrading, cbUpgrade, rc);2947 }2948 else2949 RTMsgError("RTFileOpen(,%s,): %Rrc\n", pszUpgrading, rc);2950 RTFileReadAllFree(pvUpgrade, cbUpgrade);2951 return RTEXITCODE_SUCCESS;2952 }2953 2954 /**2955 * Checks for an upgrade and respawns if there is.2956 *2957 * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.2958 * @param argc The number of arguments.2959 * @param argv The argument vector.2960 * @param pfExit For indicating exit when the exit code is zero.2961 */2962 static RTEXITCODE txsAutoUpdateStage1(int argc, char **argv, bool *pfExit)2963 {2964 /*2965 * Figure names of the current service image and the potential upgrade.2966 */2967 char szOrgPath[RTPATH_MAX];2968 if (!RTProcGetExecutablePath(szOrgPath, sizeof(szOrgPath)))2969 {2970 RTMsgError("RTProcGetExecutablePath failed\n");2971 return RTEXITCODE_SUCCESS;2972 }2973 2974 char szUpgradePath[RTPATH_MAX];2975 int rc = RTPathJoin(szUpgradePath, sizeof(szUpgradePath), g_szCdRomPath, g_szOsSlashArchShortName);2976 if (RT_SUCCESS(rc))2977 rc = RTPathAppend(szUpgradePath, sizeof(szUpgradePath), RTPathFilename(szOrgPath));2978 if (RT_FAILURE(rc))2979 {2980 RTMsgError("Failed to construct path to potential service upgrade: %Rrc\n", rc);2981 return RTEXITCODE_SUCCESS;2982 }2983 2984 /*2985 * Query information about the two images and read the entire potential source file.2986 */2987 RTFSOBJINFO UpgradeInfo;2988 rc = RTPathQueryInfo(szUpgradePath, &UpgradeInfo, RTFSOBJATTRADD_NOTHING);2989 if ( rc == VERR_FILE_NOT_FOUND2990 || rc == VERR_PATH_NOT_FOUND2991 || rc == VERR_MEDIA_NOT_PRESENT2992 || rc == VERR_MEDIA_NOT_RECOGNIZED)2993 return RTEXITCODE_SUCCESS;2994 if (RT_FAILURE(rc))2995 {2996 RTMsgError("RTPathQueryInfo(\"%s\"): %Rrc (upgrade)\n", szUpgradePath, rc);2997 return RTEXITCODE_SUCCESS;2998 }2999 3000 RTFSOBJINFO OrgInfo;3001 rc = RTPathQueryInfo(szOrgPath, &OrgInfo, RTFSOBJATTRADD_NOTHING);3002 if (RT_FAILURE(rc))3003 {3004 RTMsgError("RTPathQueryInfo(\"%s\"): %Rrc (old)\n", szOrgPath, rc);3005 return RTEXITCODE_SUCCESS;3006 }3007 3008 void *pvUpgrade;3009 size_t cbUpgrade;3010 rc = RTFileReadAllEx(szUpgradePath, 0, UpgradeInfo.cbObject, RTFILE_RDALL_O_DENY_NONE, &pvUpgrade, &cbUpgrade);3011 if (RT_FAILURE(rc))3012 {3013 RTMsgError("RTPathQueryInfo(\"%s\"): %Rrc (old)\n", szOrgPath, rc);3014 return RTEXITCODE_SUCCESS;3015 }3016 3017 /*3018 * Compare and see if we've got a different service image or not.3019 */3020 if (OrgInfo.cbObject == UpgradeInfo.cbObject)3021 {3022 /* must compare bytes. */3023 void *pvOrg;3024 size_t cbOrg;3025 rc = RTFileReadAllEx(szOrgPath, 0, OrgInfo.cbObject, RTFILE_RDALL_O_DENY_NONE, &pvOrg, &cbOrg);3026 if (RT_FAILURE(rc))3027 {3028 RTMsgError("RTFileReadAllEx(\"%s\"): %Rrc\n", szOrgPath, rc);3029 RTFileReadAllFree(pvUpgrade, cbUpgrade);3030 return RTEXITCODE_SUCCESS;3031 }3032 bool fSame = !memcmp(pvUpgrade, pvOrg, OrgInfo.cbObject);3033 RTFileReadAllFree(pvOrg, cbOrg);3034 if (fSame)3035 {3036 RTFileReadAllFree(pvUpgrade, cbUpgrade);3037 return RTEXITCODE_SUCCESS;3038 }3039 }3040 3041 /*3042 * Should upgrade. Start by creating an executable copy of the update3043 * image in the scratch area.3044 */3045 RTEXITCODE rcExit = txsFinalizeScratch();3046 if (rcExit == RTEXITCODE_SUCCESS)3047 {3048 char szTmpPath[RTPATH_MAX];3049 rc = RTPathJoin(szTmpPath, sizeof(szTmpPath), g_szScratchPath, RTPathFilename(szOrgPath));3050 if (RT_SUCCESS(rc))3051 {3052 RTFileDelete(szTmpPath); /* shouldn't hurt. */3053 3054 RTFILE hFile;3055 rc = RTFileOpen(&hFile, szTmpPath,3056 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE3057 | (0755 << RTFILE_O_CREATE_MODE_SHIFT));3058 if (RT_SUCCESS(rc))3059 {3060 rc = RTFileWrite(hFile, pvUpgrade, UpgradeInfo.cbObject, NULL);3061 RTFileClose(hFile);3062 if (RT_SUCCESS(rc))3063 {3064 /*3065 * Try execute the new image and quit if it works.3066 */3067 const char **papszArgs = (const char **)RTMemAlloc((argc + 2 + 1) * sizeof(char **));3068 if (papszArgs)3069 {3070 papszArgs[0] = szTmpPath;3071 for (int i = 1; i < argc; i++)3072 papszArgs[i] = argv[i];3073 papszArgs[argc] = "--upgrading";3074 papszArgs[argc + 1] = szOrgPath;3075 papszArgs[argc + 2] = NULL;3076 3077 RTMsgInfo("Launching intermediate automatic upgrade stage: \"%s\"\n", szTmpPath);3078 RTPROCESS hProc;3079 rc = RTProcCreate(szTmpPath, papszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &hProc);3080 if (RT_SUCCESS(rc))3081 *pfExit = true;3082 else3083 RTMsgError("RTProcCreate(\"%s\"): %Rrc (upgrade stage 1)\n", szTmpPath, rc);3084 RTMemFree(papszArgs);3085 }3086 else3087 RTMsgError("RTMemAlloc failed during upgrade attempt (stage)\n");3088 }3089 else3090 RTMsgError("RTFileWrite(%s,,%zu): %Rrc\n", szTmpPath, UpgradeInfo.cbObject, rc);3091 }3092 else3093 RTMsgError("RTFileOpen(,%s,): %Rrc\n", szTmpPath, rc);3094 }3095 else3096 RTMsgError("Failed to construct path to temporary upgrade image: %Rrc\n", rc);3097 }3098 3099 RTFileReadAllFree(pvUpgrade, cbUpgrade);3100 return rcExit;3101 }3102 3103 /**3104 758 * Determines the default configuration. 3105 759 */ 3106 static void txsSetDefaults(void)760 static void utsSetDefaults(void) 3107 761 { 3108 762 /* … … 3155 809 if (RT_SUCCESS(rc)) 3156 810 #if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(RT_OS_DOS) 3157 rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), " txs-XXXX.tmp");811 rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "uts-XXXX.tmp"); 3158 812 #else 3159 rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), " txs-XXXXXXXXX.tmp");813 rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "uts-XXXXXXXXX.tmp"); 3160 814 #endif 3161 815 if (RT_FAILURE(rc)) 3162 816 { 3163 817 RTMsgError("RTPathTemp/Append failed when constructing scratch path: %Rrc\n", rc); 3164 strcpy(g_szDefScratchPath, "/tmp/ txs-XXXX.tmp");818 strcpy(g_szDefScratchPath, "/tmp/uts-XXXX.tmp"); 3165 819 } 3166 820 strcpy(g_szScratchPath, g_szDefScratchPath); … … 3178 832 * @param pszArgv0 The program name (argv[0]). 3179 833 */ 3180 static void txsUsage(PRTSTREAM pStrm, const char *argv0)834 static void utsUsage(PRTSTREAM pStrm, const char *argv0) 3181 835 { 3182 836 RTStrmPrintf(pStrm, … … 3201 855 RTStrmPrintf(pStrm, " Default: %s\n", g_pTransport->szName); 3202 856 RTStrmPrintf(pStrm, 3203 " --auto-upgrade, --no-auto-upgrade\n"3204 " To enable or disable the automatic upgrade mechanism where any different\n"3205 " version found on the CD-ROM on startup will replace the initial copy.\n"3206 " Default: --auto-upgrade\n"3207 " --upgrading <org-path>\n"3208 " Internal use only.\n");3209 RTStrmPrintf(pStrm,3210 857 " --display-output, --no-display-output\n" 3211 858 " Display the output and the result of all child processes.\n"); … … 3237 884 * @param pfExit For indicating exit when the exit code is zero. 3238 885 */ 3239 static RTEXITCODE txsParseArgv(int argc, char **argv, bool *pfExit)886 static RTEXITCODE utsParseArgv(int argc, char **argv, bool *pfExit) 3240 887 { 3241 888 *pfExit = false; … … 3244 891 * Storage for locally handled options. 3245 892 */ 3246 bool fAutoUpgrade = true;3247 893 bool fDaemonize = true; 3248 894 bool fDaemonized = false; 3249 bool fTransportFixed = false;3250 const char *pszUpgrading = NULL;3251 895 3252 896 /* … … 3258 902 { "--cdrom", 'c', RTGETOPT_REQ_STRING }, 3259 903 { "--scratch", 's', RTGETOPT_REQ_STRING }, 3260 { "--auto-upgrade", 'a', RTGETOPT_REQ_NOTHING },3261 { "--no-auto-upgrade", 'A', RTGETOPT_REQ_NOTHING },3262 { "--upgrading", 'U', RTGETOPT_REQ_STRING },3263 904 { "--display-output", 'd', RTGETOPT_REQ_NOTHING }, 3264 905 { "--no-display-output",'D', RTGETOPT_REQ_NOTHING }, … … 3296 937 switch (ch) 3297 938 { 3298 case 'a':3299 fAutoUpgrade = true;3300 break;3301 3302 case 'A':3303 fAutoUpgrade = false;3304 break;3305 3306 939 case 'c': 3307 940 rc = RTStrCopy(g_szCdRomPath, sizeof(g_szCdRomPath), Val.psz); … … 3323 956 3324 957 case 'h': 3325 txsUsage(g_pStdOut, argv[0]);958 utsUsage(g_pStdOut, argv[0]); 3326 959 *pfExit = true; 3327 960 return RTEXITCODE_SUCCESS; … … 3335 968 case 't': 3336 969 { 3337 PC TXSTRANSPORT pTransport = NULL;970 PCUTSTRANSPORT pTransport = NULL; 3338 971 for (size_t i = 0; RT_ELEMENTS(g_apTransports); i++) 3339 972 if (!strcmp(g_apTransports[i]->szName, Val.psz)) … … 3347 980 break; 3348 981 } 3349 3350 case 'U':3351 pszUpgrading = Val.psz;3352 break;3353 982 3354 983 case 'V': … … 3388 1017 3389 1018 /* 3390 * Handle automatic upgrading of the service.3391 */3392 if (fAutoUpgrade && !*pfExit)3393 {3394 RTEXITCODE rcExit;3395 if (pszUpgrading)3396 rcExit = txsAutoUpdateStage2(argc, argv, pfExit, pszUpgrading);3397 else3398 rcExit = txsAutoUpdateStage1(argc, argv, pfExit);3399 if ( *pfExit3400 || rcExit != RTEXITCODE_SUCCESS)3401 return rcExit;3402 }3403 3404 /*3405 1019 * Daemonize ourselves if asked to. 3406 1020 */ … … 3429 1043 * Determine defaults and parse the arguments. 3430 1044 */ 3431 txsSetDefaults();1045 utsSetDefaults(); 3432 1046 bool fExit; 3433 RTEXITCODE rcExit = txsParseArgv(argc, argv, &fExit);1047 RTEXITCODE rcExit = utsParseArgv(argc, argv, &fExit); 3434 1048 if (rcExit != RTEXITCODE_SUCCESS || fExit) 3435 1049 return rcExit; 3436 1050 3437 1051 /* 3438 * Generate a UUID for this TXS instance.3439 */ 3440 rc = RTUuidCreate(&g_InstanceUuid);1052 * Initialize global state. 1053 */ 1054 rc = utsInit(); 3441 1055 if (RT_FAILURE(rc)) 3442 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTUuidCreate failed: %Rrc", rc); 3443 3444 /* 3445 * Finalize the scratch directory and initialize the transport layer. 3446 */ 3447 rcExit = txsFinalizeScratch(); 3448 if (rcExit != RTEXITCODE_SUCCESS) 3449 return rcExit; 3450 1056 return RTEXITCODE_FAILURE; 1057 1058 /* 1059 * Initialize the transport layer. 1060 */ 3451 1061 rc = g_pTransport->pfnInit(); 3452 1062 if (RT_FAILURE(rc)) … … 3456 1066 * Ok, start working 3457 1067 */ 3458 rcExit = txsMainLoop();1068 rcExit = utsMainLoop(); 3459 1069 3460 1070 /* -
trunk/src/VBox/ValidationKit/utils/usb/UsbTestServiceTcp.cpp
r60266 r60279 1 1 /* $Id$ */ 2 2 /** @file 3 * TestExecServ - Basic Remote Execution Service, TCP/IP Transport Layer.3 * UsbTestService - Remote USB test configuration and execution server, TCP/IP Transport Layer. 4 4 */ 5 5 6 6 /* 7 * Copyright (C) 2010-201 5Oracle Corporation7 * Copyright (C) 2010-2016 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 43 43 #include <iprt/time.h> 44 44 45 #include " TestExecServiceInternal.h"45 #include "UsbTestServiceInternal.h" 46 46 47 47 … … 50 50 *********************************************************************************************************************************/ 51 51 /** The default server port. */ 52 #define TXS_TCP_DEF_BIND_PORT 5042 53 /** The default client port. */ 54 #define TXS_TCP_DEF_CONNECT_PORT 5048 55 52 #define UTS_TCP_DEF_BIND_PORT 6042 56 53 /** The default server bind address. */ 57 #define TXS_TCP_DEF_BIND_ADDRESS "" 58 /** The default client connect address (i.e. of the host server). */ 59 #define TXS_TCP_DEF_CONNECT_ADDRESS "10.0.2.2" 60 54 #define UTS_TCP_DEF_BIND_ADDRESS "" 55 56 57 /********************************************************************************************************************************* 58 * Structures and Typedefs * 59 *********************************************************************************************************************************/ 60 61 /** 62 * TCP specific client data. 63 */ 64 typedef struct UTSTRANSPORTCLIENT 65 { 66 /** Socket of the current client. */ 67 RTSOCKET hTcpClient; 68 /** The size of the stashed data. */ 69 size_t cbTcpStashed; 70 /** The size of the stashed data allocation. */ 71 size_t cbTcpStashedAlloced; 72 /** The stashed data. */ 73 uint8_t *pbTcpStashed; 74 } UTSTRANSPORTCLIENT; 61 75 62 76 /********************************************************************************************************************************* … … 65 79 /** @name TCP Parameters 66 80 * @{ */ 67 static enum { TXSTCPMODE_BOTH, TXSTCPMODE_CLIENT, TXSTCPMODE_SERVER }68 g_enmTcpMode = TXSTCPMODE_BOTH;69 70 81 /** The addresses to bind to. Empty string means any. */ 71 static char g_szTcpBindAddr[256] = TXS_TCP_DEF_BIND_ADDRESS;82 static char g_szTcpBindAddr[256] = UTS_TCP_DEF_BIND_ADDRESS; 72 83 /** The TCP port to listen to. */ 73 static uint32_t g_uTcpBindPort = TXS_TCP_DEF_BIND_PORT; 74 /** The addresses to connect to if fRevesedSetupMode is @c true. */ 75 static char g_szTcpConnectAddr[256] = TXS_TCP_DEF_CONNECT_ADDRESS; 76 /** The TCP port to listen to. */ 77 static uint32_t g_uTcpConnectPort = TXS_TCP_DEF_CONNECT_PORT; 84 static uint32_t g_uTcpBindPort = UTS_TCP_DEF_BIND_PORT; 78 85 /** @} */ 79 86 80 /** Critical section for serializing access to the next few variables. */81 static RTCRITSECT g_TcpCritSect;82 87 /** Pointer to the TCP server instance. */ 83 88 static PRTTCPSERVER g_pTcpServer = NULL; 84 /** Thread calling RTTcpServerListen2. */85 static RTTHREAD g_hThreadTcpServer = NIL_RTTHREAD;86 /** Thread calling RTTcpClientConnect. */87 static RTTHREAD g_hThreadTcpConnect = NIL_RTTHREAD;88 /** The main thread handle (for signalling). */89 static RTTHREAD g_hThreadMain = NIL_RTTHREAD;90 89 /** Stop connecting attempts when set. */ 91 90 static bool g_fTcpStopConnecting = false; 92 /** Connect cancel cookie. */ 93 static PRTTCPCLIENTCONNECTCANCEL volatile g_pTcpConnectCancelCookie = NULL; 94 95 /** Socket of the current client. */ 96 static RTSOCKET g_hTcpClient = NIL_RTSOCKET; 97 /** Indicates whether g_hTcpClient comes from the server or from a client 98 * connect (relevant when closing it). */ 99 static bool g_fTcpClientFromServer = false; 100 /** The size of the stashed data. */ 101 static size_t g_cbTcpStashed = 0; 102 /** The size of the stashed data allocation. */ 103 static size_t g_cbTcpStashedAlloced = 0; 104 /** The stashed data. */ 105 static uint8_t *g_pbTcpStashed = NULL; 106 107 108 109 /** 110 * Disconnects the current client. 111 */ 112 static void txsTcpDisconnectClient(void) 91 92 93 94 /** 95 * Disconnects the current client and frees its data structure. 96 */ 97 static void utsTcpDisconnectClient(PUTSTRANSPORTCLIENT pClient) 98 { 99 int rc = RTTcpServerDisconnectClient2(pClient->hTcpClient); 100 AssertRCSuccess(rc); 101 if (pClient->pbTcpStashed) 102 RTMemFree(pClient->pbTcpStashed); 103 RTMemFree(pClient); 104 } 105 106 /** 107 * @interface_method_impl{UTSTRANSPORT,pfnWaitForConnect} 108 */ 109 static int utsTcpWaitForConnect(PPUTSTRANSPORTCLIENT ppClientNew) 113 110 { 114 111 int rc; 115 if (g_fTcpClientFromServer) 116 rc = RTTcpServerDisconnectClient2(g_hTcpClient); 117 else 118 rc = RTTcpClientClose(g_hTcpClient); 119 AssertRCSuccess(rc); 120 g_hTcpClient = NIL_RTSOCKET; 121 } 122 123 /** 124 * Sets the current client socket in a safe manner. 125 * 126 * @returns NIL_RTSOCKET if consumed, other wise hTcpClient. 127 * @param hTcpClient The client socket. 128 * @param fFromServer Set if server type connection. 129 */ 130 static RTSOCKET txsTcpSetClient(RTSOCKET hTcpClient, bool fFromServer) 131 { 132 RTCritSectEnter(&g_TcpCritSect); 133 if ( g_hTcpClient == NIL_RTSOCKET 134 && !g_fTcpStopConnecting 135 && g_hThreadMain != NIL_RTTHREAD 136 ) 137 { 138 g_fTcpClientFromServer = true; 139 g_hTcpClient = hTcpClient; 140 int rc = RTThreadUserSignal(g_hThreadMain); AssertRC(rc); 141 hTcpClient = NIL_RTSOCKET; 142 } 143 RTCritSectLeave(&g_TcpCritSect); 144 return hTcpClient; 145 } 146 147 /** 148 * Server mode connection thread. 149 * 150 * @returns iprt status code. 151 * @param hSelf Thread handle. Ignored. 152 * @param pvUser Ignored. 153 */ 154 static DECLCALLBACK(int) txsTcpServerConnectThread(RTTHREAD hSelf, void *pvUser) 155 { 156 RTSOCKET hTcpClient; 157 int rc = RTTcpServerListen2(g_pTcpServer, &hTcpClient); 158 Log(("txsTcpConnectServerThread: RTTcpServerListen2 -> %Rrc\n", rc)); 112 RTSOCKET hClientNew; 113 114 rc = RTTcpServerListen2(g_pTcpServer, &hClientNew); 115 Log(("utsTcpWaitForConnect: RTTcpServerListen2 -> %Rrc\n", rc)); 116 159 117 if (RT_SUCCESS(rc)) 160 118 { 161 hTcpClient = txsTcpSetClient(hTcpClient, true /*fFromServer*/); 162 RTTcpServerDisconnectClient2(hTcpClient); 119 PUTSTRANSPORTCLIENT pClient = (PUTSTRANSPORTCLIENT)RTMemAllocZ(sizeof(UTSTRANSPORTCLIENT)); 120 if (RT_LIKELY(pClient)) 121 { 122 pClient->hTcpClient = hClientNew; 123 pClient->cbTcpStashed = 0; 124 pClient->cbTcpStashedAlloced = 0; 125 pClient->pbTcpStashed = NULL; 126 *ppClientNew = pClient; 127 } 128 else 129 { 130 RTTcpServerDisconnectClient2(hClientNew); 131 rc = VERR_NO_MEMORY; 132 } 163 133 } 164 134 … … 167 137 168 138 /** 169 * Checks if it's a fatal RTTcpClientConnect return code. 170 * 171 * @returns true / false. 172 * @param rc The IPRT status code. 173 */ 174 static bool txsTcpIsFatalClientConnectStatus(int rc) 175 { 176 return rc != VERR_NET_UNREACHABLE 177 && rc != VERR_NET_HOST_DOWN 178 && rc != VERR_NET_HOST_UNREACHABLE 179 && rc != VERR_NET_CONNECTION_REFUSED 180 && rc != VERR_TIMEOUT 181 && rc != VERR_NET_CONNECTION_TIMED_OUT; 182 } 183 184 /** 185 * Client mode connection thread. 186 * 187 * @returns iprt status code. 188 * @param hSelf Thread handle. Use to sleep on. The main thread will 189 * signal it to speed up thread shutdown. 190 * @param pvUser Ignored. 191 */ 192 static DECLCALLBACK(int) txsTcpClientConnectThread(RTTHREAD hSelf, void *pvUser) 193 { 194 for (;;) 195 { 196 /* Stop? */ 197 RTCritSectEnter(&g_TcpCritSect); 198 bool fStop = g_fTcpStopConnecting; 199 RTCritSectLeave(&g_TcpCritSect); 200 if (fStop) 201 return VINF_SUCCESS; 202 203 /* Try connect. */ /** @todo make cancelable! */ 204 RTSOCKET hTcpClient; 205 Log2(("Calling RTTcpClientConnect(%s, %u,)...\n", g_szTcpConnectAddr, g_uTcpConnectPort)); 206 int rc = RTTcpClientConnectEx(g_szTcpConnectAddr, g_uTcpConnectPort, &hTcpClient, 207 RT_SOCKETCONNECT_DEFAULT_WAIT, &g_pTcpConnectCancelCookie); 208 Log(("txsTcpRecvPkt: RTTcpClientConnect -> %Rrc\n", rc)); 209 if (RT_SUCCESS(rc)) 210 { 211 hTcpClient = txsTcpSetClient(hTcpClient, true /*fFromServer*/); 212 RTTcpClientCloseEx(hTcpClient, true /* fGracefulShutdown*/); 213 break; 214 } 215 216 if (txsTcpIsFatalClientConnectStatus(rc)) 217 return rc; 218 219 /* Delay a wee bit before retrying. */ 220 RTThreadUserWait(hSelf, 1536); 221 } 222 return VINF_SUCCESS; 223 } 224 225 /** 226 * Wait on the threads to complete. 227 * 228 * @returns Thread status (if collected), otherwise VINF_SUCCESS. 229 * @param cMillies The period to wait on each thread. 230 */ 231 static int txsTcpConnectWaitOnThreads(RTMSINTERVAL cMillies) 232 { 233 int rcRet = VINF_SUCCESS; 234 235 if (g_hThreadTcpConnect != NIL_RTTHREAD) 236 { 237 int rcThread; 238 int rc2 = RTThreadWait(g_hThreadTcpConnect, cMillies, &rcThread); 239 if (RT_SUCCESS(rc2)) 240 { 241 g_hThreadTcpConnect = NIL_RTTHREAD; 242 rcRet = rcThread; 243 } 244 } 245 246 if (g_hThreadTcpServer != NIL_RTTHREAD) 247 { 248 int rcThread; 249 int rc2 = RTThreadWait(g_hThreadTcpServer, cMillies, &rcThread); 250 if (RT_SUCCESS(rc2)) 251 { 252 g_hThreadTcpServer = NIL_RTTHREAD; 253 if (RT_SUCCESS(rc2)) 254 rcRet = rcThread; 255 } 256 } 257 return rcRet; 258 } 259 260 /** 261 * Connects to the peer. 262 * 263 * @returns VBox status code. Updates g_hTcpClient and g_fTcpClientFromServer on 264 * success 265 */ 266 static int txsTcpConnect(void) 267 { 268 int rc; 269 if (g_enmTcpMode == TXSTCPMODE_SERVER) 270 { 271 g_fTcpClientFromServer = true; 272 rc = RTTcpServerListen2(g_pTcpServer, &g_hTcpClient); 273 Log(("txsTcpRecvPkt: RTTcpServerListen2 -> %Rrc\n", rc)); 274 } 275 else if (g_enmTcpMode == TXSTCPMODE_CLIENT) 276 { 277 g_fTcpClientFromServer = false; 278 for (;;) 279 { 280 Log2(("Calling RTTcpClientConnect(%s, %u,)...\n", g_szTcpConnectAddr, g_uTcpConnectPort)); 281 rc = RTTcpClientConnect(g_szTcpConnectAddr, g_uTcpConnectPort, &g_hTcpClient); 282 Log(("txsTcpRecvPkt: RTTcpClientConnect -> %Rrc\n", rc)); 283 if (RT_SUCCESS(rc) || txsTcpIsFatalClientConnectStatus(rc)) 284 break; 285 286 /* Delay a wee bit before retrying. */ 287 RTThreadSleep(1536); 288 } 289 } 290 else 291 { 292 Assert(g_enmTcpMode == TXSTCPMODE_BOTH); 293 RTTHREAD hSelf = RTThreadSelf(); 294 295 /* 296 * Create client threads. 297 */ 298 RTCritSectEnter(&g_TcpCritSect); 299 RTThreadUserReset(hSelf); 300 g_hThreadMain = hSelf; 301 g_fTcpStopConnecting = false; 302 RTCritSectLeave(&g_TcpCritSect); 303 304 txsTcpConnectWaitOnThreads(32); 305 306 rc = VINF_SUCCESS; 307 if (g_hThreadTcpConnect == NIL_RTTHREAD) 308 { 309 g_pTcpConnectCancelCookie = NULL; 310 rc = RTThreadCreate(&g_hThreadTcpConnect, txsTcpClientConnectThread, NULL, 0, RTTHREADTYPE_DEFAULT, 311 RTTHREADFLAGS_WAITABLE, "tcpconn"); 312 } 313 if (g_hThreadTcpServer == NIL_RTTHREAD && RT_SUCCESS(rc)) 314 rc = RTThreadCreate(&g_hThreadTcpServer, txsTcpServerConnectThread, NULL, 0, RTTHREADTYPE_DEFAULT, 315 RTTHREADFLAGS_WAITABLE, "tcpserv"); 316 317 RTCritSectEnter(&g_TcpCritSect); 318 319 /* 320 * Wait for connection to be established. 321 */ 322 while ( RT_SUCCESS(rc) 323 && g_hTcpClient == NIL_RTSOCKET) 324 { 325 RTCritSectLeave(&g_TcpCritSect); 326 RTThreadUserWait(hSelf, 1536); 327 rc = txsTcpConnectWaitOnThreads(0); 328 RTCritSectEnter(&g_TcpCritSect); 329 } 330 331 /* 332 * Cancel the threads. 333 */ 334 g_hThreadMain = NIL_RTTHREAD; 335 g_fTcpStopConnecting = true; 336 337 RTCritSectLeave(&g_TcpCritSect); 338 RTTcpClientCancelConnect(&g_pTcpConnectCancelCookie); 339 } 340 341 AssertMsg(RT_SUCCESS(rc) ? g_hTcpClient != NIL_RTSOCKET : g_hTcpClient == NIL_RTSOCKET, ("%Rrc %p\n", rc, g_hTcpClient)); 342 g_cbTcpStashed = 0; 343 return rc; 344 } 345 346 /** 347 * @interface_method_impl{TXSTRANSPORT,txsTcpNotifyReboot} 348 */ 349 static DECLCALLBACK(void) txsTcpNotifyReboot(void) 350 { 351 Log(("txsTcpNotifyReboot: RTTcpServerDestroy(%p)\n", g_pTcpServer)); 139 * @interface_method_impl{UTSTRANSPORT,pfnNotifyReboot} 140 */ 141 static DECLCALLBACK(void) utsTcpNotifyReboot(void) 142 { 143 Log(("utsTcpNotifyReboot: RTTcpServerDestroy(%p)\n", g_pTcpServer)); 352 144 if (g_pTcpServer) 353 145 { 354 146 int rc = RTTcpServerDestroy(g_pTcpServer); 355 147 if (RT_FAILURE(rc)) 356 RTMsgInfo("RTTcpServerDestroy failed in txsTcpNotifyReboot: %Rrc", rc);148 RTMsgInfo("RTTcpServerDestroy failed in utsTcpNotifyReboot: %Rrc", rc); 357 149 g_pTcpServer = NULL; 358 150 } … … 360 152 361 153 /** 362 * @interface_method_impl{ TXSTRANSPORT,pfnNotifyBye}363 */ 364 static DECLCALLBACK(void) txsTcpNotifyBye(void)365 { 366 Log((" txsTcpNotifyBye: txsTcpDisconnectClient %RTsock\n", g_hTcpClient));367 txsTcpDisconnectClient();368 } 369 370 /** 371 * @interface_method_impl{ TXSTRANSPORT,pfnNotifyHowdy}372 */ 373 static DECLCALLBACK(void) txsTcpNotifyHowdy(void)154 * @interface_method_impl{UTSTRANSPORT,pfnNotifyBye} 155 */ 156 static DECLCALLBACK(void) utsTcpNotifyBye(PUTSTRANSPORTCLIENT pClient) 157 { 158 Log(("utsTcpNotifyBye: utsTcpDisconnectClient %RTsock\n", pClient->hTcpClient)); 159 utsTcpDisconnectClient(pClient); 160 } 161 162 /** 163 * @interface_method_impl{UTSTRANSPORT,pfnNotifyHowdy} 164 */ 165 static DECLCALLBACK(void) utsTcpNotifyHowdy(PUTSTRANSPORTCLIENT pClient) 374 166 { 375 167 /* nothing to do here */ … … 377 169 378 170 /** 379 * @interface_method_impl{TXSTRANSPORT,pfnBabble} 380 */ 381 static DECLCALLBACK(void) txsTcpBabble(PCTXSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout) 382 { 383 /* 384 * Quietly ignore already disconnected client. 385 */ 386 RTSOCKET hTcpClient = g_hTcpClient; 387 if (hTcpClient == NIL_RTSOCKET) 388 return; 389 171 * @interface_method_impl{UTSTRANSPORT,pfnBabble} 172 */ 173 static DECLCALLBACK(void) utsTcpBabble(PUTSTRANSPORTCLIENT pClient, PCUTSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout) 174 { 390 175 /* 391 176 * Try send the babble reply. … … 393 178 NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */ 394 179 int rc; 395 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);396 do rc = RTTcpWrite( hTcpClient, pPktHdr, cbToSend);180 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, UTSPKT_ALIGNMENT); 181 do rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend); 397 182 while (rc == VERR_INTERRUPTED); 398 183 … … 400 185 * Disconnect the client. 401 186 */ 402 Log(("txsTcpBabble: txsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", g_hTcpClient, rc)); 403 txsTcpDisconnectClient(); 404 } 405 406 /** 407 * @interface_method_impl{TXSTRANSPORT,pfnSendPkt} 408 */ 409 static DECLCALLBACK(int) txsTcpSendPkt(PCTXSPKTHDR pPktHdr) 410 { 411 Assert(pPktHdr->cb >= sizeof(TXSPKTHDR)); 412 413 /* 414 * Fail if no client connection. 415 */ 416 RTSOCKET hTcpClient = g_hTcpClient; 417 if (hTcpClient == NIL_RTSOCKET) 418 return VERR_NET_NOT_CONNECTED; 187 Log(("utsTcpBabble: utsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", pClient->hTcpClient, rc)); 188 utsTcpDisconnectClient(pClient); 189 } 190 191 /** 192 * @interface_method_impl{UTSTRANSPORT,pfnSendPkt} 193 */ 194 static DECLCALLBACK(int) utsTcpSendPkt(PUTSTRANSPORTCLIENT pClient, PCUTSPKTHDR pPktHdr) 195 { 196 Assert(pPktHdr->cb >= sizeof(UTSPKTHDR)); 419 197 420 198 /* 421 199 * Write it. 422 200 */ 423 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);424 int rc = RTTcpWrite( hTcpClient, pPktHdr, cbToSend);201 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, UTSPKT_ALIGNMENT); 202 int rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend); 425 203 if ( RT_FAILURE(rc) 426 204 && rc != VERR_INTERRUPTED) 427 205 { 428 206 /* assume fatal connection error. */ 429 Log(("RTTcpWrite -> %Rrc -> txsTcpDisconnectClient(%RTsock)\n", rc, g_hTcpClient));430 txsTcpDisconnectClient();207 Log(("RTTcpWrite -> %Rrc -> utsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient)); 208 utsTcpDisconnectClient(pClient); 431 209 } 432 210 … … 435 213 436 214 /** 437 * @interface_method_impl{ TXSTRANSPORT,pfnRecvPkt}438 */ 439 static DECLCALLBACK(int) txsTcpRecvPkt(PPTXSPKTHDR ppPktHdr)215 * @interface_method_impl{UTSTRANSPORT,pfnRecvPkt} 216 */ 217 static DECLCALLBACK(int) utsTcpRecvPkt(PUTSTRANSPORTCLIENT pClient, PPUTSPKTHDR ppPktHdr) 440 218 { 441 219 int rc = VINF_SUCCESS; 442 220 *ppPktHdr = NULL; 443 444 /*445 * Do we have to wait for a client to connect?446 */447 RTSOCKET hTcpClient = g_hTcpClient;448 if (hTcpClient == NIL_RTSOCKET)449 {450 rc = txsTcpConnect();451 if (RT_FAILURE(rc))452 return rc;453 hTcpClient = g_hTcpClient; Assert(hTcpClient != NIL_RTSOCKET);454 }455 221 456 222 /* … … 465 231 * Any stashed data? 466 232 */ 467 if ( g_cbTcpStashedAlloced)468 { 469 offData = g_cbTcpStashed;470 cbDataAlloced = g_cbTcpStashedAlloced;471 pbData = g_pbTcpStashed;472 473 g_cbTcpStashed = 0;474 g_cbTcpStashedAlloced = 0;475 g_pbTcpStashed = NULL;233 if (pClient->cbTcpStashedAlloced) 234 { 235 offData = pClient->cbTcpStashed; 236 cbDataAlloced = pClient->cbTcpStashedAlloced; 237 pbData = pClient->pbTcpStashed; 238 239 pClient->cbTcpStashed = 0; 240 pClient->cbTcpStashedAlloced = 0; 241 pClient->pbTcpStashed = NULL; 476 242 } 477 243 else 478 244 { 479 cbDataAlloced = RT_ALIGN_Z(64, TXSPKT_ALIGNMENT);245 cbDataAlloced = RT_ALIGN_Z(64, UTSPKT_ALIGNMENT); 480 246 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced); 481 247 if (!pbData) … … 489 255 { 490 256 size_t cbRead; 491 rc = RTTcpRead( hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);257 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead); 492 258 if (RT_FAILURE(rc)) 493 259 break; 494 260 if (cbRead == 0) 495 261 { 496 Log((" txsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));262 Log(("utsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc)); 497 263 rc = VERR_NET_NOT_CONNECTED; 498 264 break; … … 504 270 ASMCompilerBarrier(); /* paranoia^3 */ 505 271 cbData = *(uint32_t volatile *)pbData; 506 if (cbData >= sizeof( TXSPKTHDR) && cbData <= TXSPKT_MAX_SIZE)272 if (cbData >= sizeof(UTSPKTHDR) && cbData <= UTSPKT_MAX_SIZE) 507 273 { 508 274 /* 509 275 * Align the length and reallocate the return packet it necessary. 510 276 */ 511 cbData = RT_ALIGN_Z(cbData, TXSPKT_ALIGNMENT);277 cbData = RT_ALIGN_Z(cbData, UTSPKT_ALIGNMENT); 512 278 if (cbData > cbDataAlloced) 513 279 { … … 529 295 { 530 296 size_t cbRead; 531 rc = RTTcpRead( hTcpClient, pbData + offData, cbData - offData, &cbRead);297 rc = RTTcpRead(pClient->hTcpClient, pbData + offData, cbData - offData, &cbRead); 532 298 if (RT_FAILURE(rc)) 533 299 break; 534 300 if (cbRead == 0) 535 301 { 536 Log((" txsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));302 Log(("utsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc)); 537 303 rc = VERR_NET_NOT_CONNECTED; 538 304 break; … … 546 312 } 547 313 if (RT_SUCCESS(rc)) 548 *ppPktHdr = (P TXSPKTHDR)pbData;314 *ppPktHdr = (PUTSPKTHDR)pbData; 549 315 else 550 316 { … … 555 321 { 556 322 /* stash it away for the next call. */ 557 g_cbTcpStashed = cbData;558 g_cbTcpStashedAlloced = cbDataAlloced;559 g_pbTcpStashed = pbData;323 pClient->cbTcpStashed = cbData; 324 pClient->cbTcpStashedAlloced = cbDataAlloced; 325 pClient->pbTcpStashed = pbData; 560 326 } 561 327 else … … 564 330 565 331 /* assume fatal connection error. */ 566 Log((" txsTcpRecvPkt: RTTcpRead -> %Rrc -> txsTcpDisconnectClient(%RTsock)\n", rc, g_hTcpClient));567 txsTcpDisconnectClient();332 Log(("utsTcpRecvPkt: RTTcpRead -> %Rrc -> utsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient)); 333 utsTcpDisconnectClient(pClient); 568 334 } 569 335 } … … 573 339 574 340 /** 575 * @interface_method_impl{TXSTRANSPORT,pfnPollSetAdd} 576 */ 577 static DECLCALLBACK(int) txsTcpPollSetAdd(RTPOLLSET hPollSet, uint32_t idStart) 578 { 579 return RTPollSetAddSocket(hPollSet, g_hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart); 580 } 581 582 /** 583 * @interface_method_impl{TXSTRANSPORT,pfnPollIn} 584 */ 585 static DECLCALLBACK(bool) txsTcpPollIn(void) 586 { 587 RTSOCKET hTcpClient = g_hTcpClient; 588 if (hTcpClient == NIL_RTSOCKET) 589 return false; 590 int rc = RTTcpSelectOne(hTcpClient, 0/*cMillies*/); 341 * @interface_method_impl{UTSTRANSPORT,pfnPollSetAdd} 342 */ 343 static DECLCALLBACK(int) utsTcpPollSetAdd(RTPOLLSET hPollSet, PUTSTRANSPORTCLIENT pClient, uint32_t idStart) 344 { 345 return RTPollSetAddSocket(hPollSet, pClient->hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart); 346 } 347 348 /** 349 * @interface_method_impl{UTSTRANSPORT,pfnPollIn} 350 */ 351 static DECLCALLBACK(bool) utsTcpPollIn(PUTSTRANSPORTCLIENT pClient) 352 { 353 int rc = RTTcpSelectOne(pClient->hTcpClient, 0/*cMillies*/); 591 354 return RT_SUCCESS(rc); 592 355 } 593 356 594 357 /** 595 * @interface_method_impl{TXSTRANSPORT,pfnTerm} 596 */ 597 static DECLCALLBACK(void) txsTcpTerm(void) 598 { 599 /* Signal thread */ 600 if (RTCritSectIsInitialized(&g_TcpCritSect)) 601 { 602 RTCritSectEnter(&g_TcpCritSect); 603 g_fTcpStopConnecting = true; 604 RTCritSectLeave(&g_TcpCritSect); 605 } 606 607 if (g_hThreadTcpConnect != NIL_RTTHREAD) 608 { 609 RTThreadUserSignal(g_hThreadTcpConnect); 610 RTTcpClientCancelConnect(&g_pTcpConnectCancelCookie); 611 } 612 358 * @interface_method_impl{UTSTRANSPORT,pfnTerm} 359 */ 360 static DECLCALLBACK(void) utsTcpTerm(void) 361 { 613 362 /* Shut down the server (will wake up thread). */ 614 363 if (g_pTcpServer) 615 364 { 616 Log((" txsTcpTerm: Destroying server...\n"));365 Log(("utsTcpTerm: Destroying server...\n")); 617 366 int rc = RTTcpServerDestroy(g_pTcpServer); 618 367 if (RT_FAILURE(rc)) 619 RTMsgInfo("RTTcpServerDestroy failed in txsTcpTerm: %Rrc", rc);368 RTMsgInfo("RTTcpServerDestroy failed in utsTcpTerm: %Rrc", rc); 620 369 g_pTcpServer = NULL; 621 370 } 622 371 623 /* Shut down client */ 624 if (g_hTcpClient != NIL_RTSOCKET) 625 { 626 if (g_fTcpClientFromServer) 627 { 628 Log(("txsTcpTerm: Disconnecting client...\n")); 629 int rc = RTTcpServerDisconnectClient2(g_hTcpClient); 630 if (RT_FAILURE(rc)) 631 RTMsgInfo("RTTcpServerDisconnectClient2(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc); 632 } 633 else 634 { 635 int rc = RTTcpClientClose(g_hTcpClient); 636 if (RT_FAILURE(rc)) 637 RTMsgInfo("RTTcpClientClose(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc); 638 } 639 g_hTcpClient = NIL_RTSOCKET; 640 } 641 642 /* Clean up stashing. */ 643 RTMemFree(g_pbTcpStashed); 644 g_pbTcpStashed = NULL; 645 g_cbTcpStashed = 0; 646 g_cbTcpStashedAlloced = 0; 647 648 /* Wait for the thread (they should've had some time to quit by now). */ 649 txsTcpConnectWaitOnThreads(15000); 650 651 /* Finally, clean up the critical section. */ 652 if (RTCritSectIsInitialized(&g_TcpCritSect)) 653 RTCritSectDelete(&g_TcpCritSect); 654 655 Log(("txsTcpTerm: done\n")); 656 } 657 658 /** 659 * @interface_method_impl{TXSTRANSPORT,pfnInit} 660 */ 661 static DECLCALLBACK(int) txsTcpInit(void) 662 { 663 int rc = RTCritSectInit(&g_TcpCritSect); 664 if (RT_SUCCESS(rc) && g_enmTcpMode != TXSTCPMODE_CLIENT) 665 { 666 rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer); 372 Log(("utsTcpTerm: done\n")); 373 } 374 375 /** 376 * @interface_method_impl{UTSTRANSPORT,pfnInit} 377 */ 378 static DECLCALLBACK(int) utsTcpInit(void) 379 { 380 int rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer); 381 if (RT_FAILURE(rc)) 382 { 383 if (rc == VERR_NET_DOWN) 384 { 385 RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n", 386 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc); 387 uint64_t StartMs = RTTimeMilliTS(); 388 do 389 { 390 RTThreadSleep(1000); 391 rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer); 392 } while ( rc == VERR_NET_DOWN 393 && RTTimeMilliTS() - StartMs < 20000); 394 if (RT_SUCCESS(rc)) 395 RTMsgInfo("RTTcpServerCreateEx succceeded.\n"); 396 } 667 397 if (RT_FAILURE(rc)) 668 398 { 669 if (rc == VERR_NET_DOWN) 670 { 671 RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n", 672 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc); 673 uint64_t StartMs = RTTimeMilliTS(); 674 do 675 { 676 RTThreadSleep(1000); 677 rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer); 678 } while ( rc == VERR_NET_DOWN 679 && RTTimeMilliTS() - StartMs < 20000); 680 if (RT_SUCCESS(rc)) 681 RTMsgInfo("RTTcpServerCreateEx succceeded.\n"); 682 } 683 if (RT_FAILURE(rc)) 684 { 685 g_pTcpServer = NULL; 686 RTCritSectDelete(&g_TcpCritSect); 687 RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n", 688 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc); 689 } 399 g_pTcpServer = NULL; 400 RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n", 401 g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc); 690 402 } 691 403 } … … 695 407 696 408 /** Options */ 697 enum TXSTCPOPT 698 { 699 TXSTCPOPT_MODE = 1000, 700 TXSTCPOPT_BIND_ADDRESS, 701 TXSTCPOPT_BIND_PORT, 702 TXSTCPOPT_CONNECT_ADDRESS, 703 TXSTCPOPT_CONNECT_PORT, 704 705 /* legacy: */ 706 TXSTCPOPT_LEGACY_PORT, 707 TXSTCPOPT_LEGACY_CONNECT 409 enum UTSTCPOPT 410 { 411 UTSTCPOPT_BIND_ADDRESS = 1000, 412 UTSTCPOPT_BIND_PORT 708 413 }; 709 414 710 415 /** 711 * @interface_method_impl{ TXSTRANSPORT,pfnOption}712 */ 713 static DECLCALLBACK(int) txsTcpOption(int ch, PCRTGETOPTUNION pVal)416 * @interface_method_impl{UTSTRANSPORT,pfnOption} 417 */ 418 static DECLCALLBACK(int) utsTcpOption(int ch, PCRTGETOPTUNION pVal) 714 419 { 715 420 int rc; … … 717 422 switch (ch) 718 423 { 719 case TXSTCPOPT_MODE: 720 if (!strcmp(pVal->psz, "both")) 721 g_enmTcpMode = TXSTCPMODE_BOTH; 722 else if (!strcmp(pVal->psz, "client")) 723 g_enmTcpMode = TXSTCPMODE_CLIENT; 724 else if (!strcmp(pVal->psz, "server")) 725 g_enmTcpMode = TXSTCPMODE_SERVER; 726 else 727 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Invalid TCP mode: '%s'\n", pVal->psz); 728 return VINF_SUCCESS; 729 730 case TXSTCPOPT_BIND_ADDRESS: 424 case UTSTCPOPT_BIND_ADDRESS: 731 425 rc = RTStrCopy(g_szTcpBindAddr, sizeof(g_szTcpBindAddr), pVal->psz); 732 426 if (RT_FAILURE(rc)) … … 734 428 return VINF_SUCCESS; 735 429 736 case TXSTCPOPT_BIND_PORT:737 g_uTcpBindPort = pVal->u16 == 0 ? TXS_TCP_DEF_BIND_PORT : pVal->u16;430 case UTSTCPOPT_BIND_PORT: 431 g_uTcpBindPort = pVal->u16 == 0 ? UTS_TCP_DEF_BIND_PORT : pVal->u16; 738 432 return VINF_SUCCESS; 739 740 case TXSTCPOPT_LEGACY_CONNECT:741 g_enmTcpMode = TXSTCPMODE_CLIENT;742 /* fall thru */743 case TXSTCPOPT_CONNECT_ADDRESS:744 rc = RTStrCopy(g_szTcpConnectAddr, sizeof(g_szTcpConnectAddr), pVal->psz);745 if (RT_FAILURE(rc))746 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP connect address is too long (%Rrc)", rc);747 if (!g_szTcpConnectAddr[0])748 strcpy(g_szTcpConnectAddr, TXS_TCP_DEF_CONNECT_ADDRESS);749 return VINF_SUCCESS;750 751 case TXSTCPOPT_CONNECT_PORT:752 g_uTcpConnectPort = pVal->u16 == 0 ? TXS_TCP_DEF_CONNECT_PORT : pVal->u16;753 return VINF_SUCCESS;754 755 case TXSTCPOPT_LEGACY_PORT:756 if (pVal->u16 == 0)757 {758 g_uTcpBindPort = TXS_TCP_DEF_BIND_PORT;759 g_uTcpConnectPort = TXS_TCP_DEF_CONNECT_PORT;760 }761 else762 {763 g_uTcpBindPort = pVal->u16;764 g_uTcpConnectPort = pVal->u16;765 }766 return VINF_SUCCESS;767 433 } 768 434 return VERR_TRY_AGAIN; … … 770 436 771 437 /** 772 * @interface_method_impl{ TXSTRANSPORT,pfnUsage}773 */ 774 DECLCALLBACK(void) txsTcpUsage(PRTSTREAM pStream)438 * @interface_method_impl{UTSTRANSPORT,pfnUsage} 439 */ 440 DECLCALLBACK(void) utsTcpUsage(PRTSTREAM pStream) 775 441 { 776 442 RTStrmPrintf(pStream, 777 " --tcp-mode <both|client|server>\n"778 " Selects the mode of operation.\n"779 " Default: both\n"780 443 " --tcp-bind-address <address>\n" 781 444 " The address(es) to listen to TCP connection on. Empty string\n" … … 784 447 " The port to listen to TCP connections on.\n" 785 448 " Default: %u\n" 786 " --tcp-connect-address <address>\n" 787 " The address of the server to try connect to in client mode.\n" 788 " Default: " TXS_TCP_DEF_CONNECT_ADDRESS "\n" 789 " --tcp-connect-port <port>\n" 790 " The port on the server to connect to in client mode.\n" 791 " Default: %u\n" 792 , TXS_TCP_DEF_BIND_PORT, TXS_TCP_DEF_CONNECT_PORT); 449 , UTS_TCP_DEF_BIND_PORT); 793 450 } 794 451 … … 796 453 static const RTGETOPTDEF g_TcpOpts[] = 797 454 { 798 { "--tcp-mode", TXSTCPOPT_MODE, RTGETOPT_REQ_STRING }, 799 { "--tcp-bind-address", TXSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING }, 800 { "--tcp-bind-port", TXSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 }, 801 { "--tcp-connect-address", TXSTCPOPT_CONNECT_ADDRESS, RTGETOPT_REQ_STRING }, 802 { "--tcp-connect-port", TXSTCPOPT_CONNECT_PORT, RTGETOPT_REQ_UINT16 }, 803 804 /* legacy */ 805 { "--tcp-port", TXSTCPOPT_LEGACY_PORT, RTGETOPT_REQ_UINT16 }, 806 { "--tcp-connect", TXSTCPOPT_LEGACY_CONNECT, RTGETOPT_REQ_STRING }, 455 { "--tcp-bind-address", UTSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING }, 456 { "--tcp-bind-port", UTSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 } 807 457 }; 808 458 809 459 /** TCP/IP transport layer. */ 810 const TXSTRANSPORT g_TcpTransport = 811 { 812 /* .szName = */ "tcp", 813 /* .pszDesc = */ "TCP/IP", 814 /* .cOpts = */ &g_TcpOpts[0], 815 /* .paOpts = */ RT_ELEMENTS(g_TcpOpts), 816 /* .pfnUsage = */ txsTcpUsage, 817 /* .pfnOption = */ txsTcpOption, 818 /* .pfnInit = */ txsTcpInit, 819 /* .pfnTerm = */ txsTcpTerm, 820 /* .pfnPollIn = */ txsTcpPollIn, 821 /* .pfnPollSetAdd = */ txsTcpPollSetAdd, 822 /* .pfnRecvPkt = */ txsTcpRecvPkt, 823 /* .pfnSendPkt = */ txsTcpSendPkt, 824 /* .pfnBabble = */ txsTcpBabble, 825 /* .pfnNotifyHowdy = */ txsTcpNotifyHowdy, 826 /* .pfnNotifyBye = */ txsTcpNotifyBye, 827 /* .pfnNotifyReboot = */ txsTcpNotifyReboot, 828 /* .u32EndMarker = */ UINT32_C(0x12345678) 460 const UTSTRANSPORT g_TcpTransport = 461 { 462 /* .szName = */ "tcp", 463 /* .pszDesc = */ "TCP/IP", 464 /* .cOpts = */ &g_TcpOpts[0], 465 /* .paOpts = */ RT_ELEMENTS(g_TcpOpts), 466 /* .pfnUsage = */ utsTcpUsage, 467 /* .pfnOption = */ utsTcpOption, 468 /* .pfnInit = */ utsTcpInit, 469 /* .pfnTerm = */ utsTcpTerm, 470 /* .pfnWaitForConnect = */ utsTcpWaitForConnect, 471 /* .pfnPollIn = */ utsTcpPollIn, 472 /* .pfnPollSetAdd = */ utsTcpPollSetAdd, 473 /* .pfnRecvPkt = */ utsTcpRecvPkt, 474 /* .pfnSendPkt = */ utsTcpSendPkt, 475 /* .pfnBabble = */ utsTcpBabble, 476 /* .pfnNotifyHowdy = */ utsTcpNotifyHowdy, 477 /* .pfnNotifyBye = */ utsTcpNotifyBye, 478 /* .pfnNotifyReboot = */ utsTcpNotifyReboot, 479 /* .u32EndMarker = */ UINT32_C(0x12345678) 829 480 }; 830
Note:
See TracChangeset
for help on using the changeset viewer.