VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/TestExecServ/TestExecService.cpp@ 70877

最後變更 在這個檔案從70877是 70488,由 vboxsync 提交於 7 年 前

ValidationKit/TestExecService: Added verbose output option. Made the txsDoUnpackFile command available on windows too.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 122.5 KB
 
1/* $Id: TestExecService.cpp 70488 2018-01-08 20:40:45Z vboxsync $ */
2/** @file
3 * TestExecServ - Basic Remote Execution Service.
4 */
5
6/*
7 * Copyright (C) 2010-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_DEFAULT
32#include <iprt/alloca.h>
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#include <iprt/cdrom.h>
36#include <iprt/critsect.h>
37#include <iprt/crc.h>
38#include <iprt/ctype.h>
39#include <iprt/dir.h>
40#include <iprt/env.h>
41#include <iprt/err.h>
42#include <iprt/file.h>
43#include <iprt/getopt.h>
44#include <iprt/handle.h>
45#include <iprt/initterm.h>
46#include <iprt/log.h>
47#include <iprt/mem.h>
48#include <iprt/message.h>
49#include <iprt/param.h>
50#include <iprt/path.h>
51#include <iprt/pipe.h>
52#include <iprt/poll.h>
53#include <iprt/process.h>
54#include <iprt/stream.h>
55#include <iprt/string.h>
56#include <iprt/system.h>
57#include <iprt/thread.h>
58#include <iprt/time.h>
59#include <iprt/uuid.h>
60#include <iprt/zip.h>
61
62#include "TestExecServiceInternal.h"
63
64
65
66/*********************************************************************************************************************************
67* Structures and Typedefs *
68*********************************************************************************************************************************/
69/**
70 * Handle IDs used by txsDoExec for the poll set.
71 */
72typedef enum TXSEXECHNDID
73{
74 TXSEXECHNDID_STDIN = 0,
75 TXSEXECHNDID_STDOUT,
76 TXSEXECHNDID_STDERR,
77 TXSEXECHNDID_TESTPIPE,
78 TXSEXECHNDID_STDIN_WRITABLE,
79 TXSEXECHNDID_TRANSPORT,
80 TXSEXECHNDID_THREAD
81} TXSEXECHNDID;
82
83
84/**
85 * For buffering process input supplied by the client.
86 */
87typedef struct TXSEXECSTDINBUF
88{
89 /** The mount of buffered data. */
90 size_t cb;
91 /** The current data offset. */
92 size_t off;
93 /** The data buffer. */
94 char *pch;
95 /** The amount of allocated buffer space. */
96 size_t cbAllocated;
97 /** Send further input into the bit bucket (stdin is dead). */
98 bool fBitBucket;
99 /** The CRC-32 for standard input (received part). */
100 uint32_t uCrc32;
101} TXSEXECSTDINBUF;
102/** Pointer to a standard input buffer. */
103typedef TXSEXECSTDINBUF *PTXSEXECSTDINBUF;
104
105/**
106 * TXS child process info.
107 */
108typedef struct TXSEXEC
109{
110 PCTXSPKTHDR pPktHdr;
111 RTMSINTERVAL cMsTimeout;
112 int rcReplySend;
113
114 RTPOLLSET hPollSet;
115 RTPIPE hStdInW;
116 RTPIPE hStdOutR;
117 RTPIPE hStdErrR;
118 RTPIPE hTestPipeR;
119 RTPIPE hWakeUpPipeR;
120 RTTHREAD hThreadWaiter;
121
122 /** @name For the setup phase
123 * @{ */
124 struct StdPipe
125 {
126 RTHANDLE hChild;
127 PRTHANDLE phChild;
128 } StdIn,
129 StdOut,
130 StdErr;
131 RTPIPE hTestPipeW;
132 RTENV hEnv;
133 /** @} */
134
135 /** For serializating some access. */
136 RTCRITSECT CritSect;
137 /** @name Members protected by the critical section.
138 * @{ */
139 RTPROCESS hProcess;
140 /** The process status. Only valid when fProcessAlive is cleared. */
141 RTPROCSTATUS ProcessStatus;
142 /** Set when the process is alive, clear when dead. */
143 bool volatile fProcessAlive;
144 /** The end of the pipe that hThreadWaiter writes to. */
145 RTPIPE hWakeUpPipeW;
146 /** @} */
147} TXSEXEC;
148/** Pointer to a the TXS child process info. */
149typedef TXSEXEC *PTXSEXEC;
150
151
152/*********************************************************************************************************************************
153* Global Variables *
154*********************************************************************************************************************************/
155/**
156 * Transport layers.
157 */
158static const PCTXSTRANSPORT g_apTransports[] =
159{
160 &g_TcpTransport,
161 //&g_SerialTransport,
162 //&g_FileSysTransport,
163 //&g_GuestPropTransport,
164 //&g_TestDevTransport,
165};
166
167/** The select transport layer. */
168static PCTXSTRANSPORT g_pTransport;
169/** The scratch path. */
170static char g_szScratchPath[RTPATH_MAX];
171/** The default scratch path. */
172static char g_szDefScratchPath[RTPATH_MAX];
173/** The CD/DVD-ROM path. */
174static char g_szCdRomPath[RTPATH_MAX];
175/** The default CD/DVD-ROM path. */
176static char g_szDefCdRomPath[RTPATH_MAX];
177/** The operating system short name. */
178static char g_szOsShortName[16];
179/** The CPU architecture short name. */
180static char g_szArchShortName[16];
181/** The combined "OS.arch" name. */
182static char g_szOsDotArchShortName[32];
183/** The combined "OS/arch" name. */
184static char g_szOsSlashArchShortName[32];
185/** The executable suffix. */
186static char g_szExeSuff[8];
187/** The shell script suffix. */
188static char g_szScriptSuff[8];
189/** UUID identifying this TXS instance. This can be used to see if TXS
190 * has been restarted or not. */
191static RTUUID g_InstanceUuid;
192/** Whether to display the output of the child process or not. */
193static bool g_fDisplayOutput = true;
194/** Whether to terminate or not.
195 * @todo implement signals and stuff. */
196static bool volatile g_fTerminate = false;
197/** Verbosity level. */
198uint32_t g_cVerbose = 1;
199
200
201/**
202 * Calculates the checksum value, zero any padding space and send the packet.
203 *
204 * @returns IPRT status code.
205 * @param pPkt The packet to send. Must point to a correctly
206 * aligned buffer.
207 */
208static int txsSendPkt(PTXSPKTHDR pPkt)
209{
210 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));
216 Log2(("%.*Rhxd\n", RT_MIN(pPkt->cb, 256), pPkt));
217 int rc = g_pTransport->pfnSendPkt(pPkt);
218 while (RT_UNLIKELY(rc == VERR_INTERRUPTED) && !g_fTerminate)
219 rc = g_pTransport->pfnSendPkt(pPkt);
220 if (RT_FAILURE(rc))
221 Log(("txsSendPkt: rc=%Rrc\n", rc));
222
223 return rc;
224}
225
226/**
227 * Sends a babble reply and disconnects the client (if applicable).
228 *
229 * @param pszOpcode The BABBLE opcode.
230 */
231static void txsReplyBabble(const char *pszOpcode)
232{
233 TXSPKTHDR Reply;
234 Reply.cb = sizeof(Reply);
235 Reply.uCrc32 = 0;
236 memcpy(Reply.achOpcode, pszOpcode, sizeof(Reply.achOpcode));
237
238 g_pTransport->pfnBabble(&Reply, 20*1000);
239}
240
241/**
242 * Receive and validate a packet.
243 *
244 * Will send bable responses to malformed packets that results in a error status
245 * code.
246 *
247 * @returns IPRT status code.
248 * @param ppPktHdr Where to return the packet on success. Free
249 * with RTMemFree.
250 * @param fAutoRetryOnFailure Whether to retry on error.
251 */
252static int txsRecvPkt(PPTXSPKTHDR ppPktHdr, bool fAutoRetryOnFailure)
253{
254 for (;;)
255 {
256 PTXSPKTHDR pPktHdr;
257 int rc = g_pTransport->pfnRecvPkt(&pPktHdr);
258 if (RT_SUCCESS(rc))
259 {
260 /* validate the packet. */
261 if ( pPktHdr->cb >= sizeof(TXSPKTHDR)
262 && pPktHdr->cb < TXSPKT_MAX_SIZE)
263 {
264 Log2(("txsRecvPkt: pPktHdr=%p cb=%#x crc32=%#x opcode=%.8s\n"
265 "%.*Rhxd\n",
266 pPktHdr, pPktHdr->cb, pPktHdr->uCrc32, pPktHdr->achOpcode, RT_MIN(pPktHdr->cb, 256), pPktHdr));
267 uint32_t uCrc32Calc = pPktHdr->uCrc32 != 0
268 ? RTCrc32(&pPktHdr->achOpcode[0], pPktHdr->cb - RT_OFFSETOF(TXSPKTHDR, achOpcode))
269 : 0;
270 if (pPktHdr->uCrc32 == uCrc32Calc)
271 {
272 AssertCompileMemberSize(TXSPKTHDR, achOpcode, 8);
273 if ( RT_C_IS_UPPER(pPktHdr->achOpcode[0])
274 && RT_C_IS_UPPER(pPktHdr->achOpcode[1])
275 && (RT_C_IS_UPPER(pPktHdr->achOpcode[2]) || pPktHdr->achOpcode[2] == ' ')
276 && (RT_C_IS_PRINT(pPktHdr->achOpcode[3]) || pPktHdr->achOpcode[3] == ' ')
277 && (RT_C_IS_PRINT(pPktHdr->achOpcode[4]) || pPktHdr->achOpcode[4] == ' ')
278 && (RT_C_IS_PRINT(pPktHdr->achOpcode[5]) || pPktHdr->achOpcode[5] == ' ')
279 && (RT_C_IS_PRINT(pPktHdr->achOpcode[6]) || pPktHdr->achOpcode[6] == ' ')
280 && (RT_C_IS_PRINT(pPktHdr->achOpcode[7]) || pPktHdr->achOpcode[7] == ' ')
281 )
282 {
283 Log(("txsRecvPkt: cb=%#x opcode=%.8s\n", pPktHdr->cb, pPktHdr->achOpcode));
284 *ppPktHdr = pPktHdr;
285 return rc;
286 }
287
288 rc = VERR_IO_BAD_COMMAND;
289 }
290 else
291 {
292 Log(("txsRecvPkt: cb=%#x opcode=%.8s crc32=%#x actual=%#x\n",
293 pPktHdr->cb, pPktHdr->achOpcode, pPktHdr->uCrc32, uCrc32Calc));
294 rc = VERR_IO_CRC;
295 }
296 }
297 else
298 rc = VERR_IO_BAD_LENGTH;
299
300 /* Send babble reply and disconnect the client if the transport is
301 connection oriented. */
302 if (rc == VERR_IO_BAD_LENGTH)
303 txsReplyBabble("BABBLE L");
304 else if (rc == VERR_IO_CRC)
305 txsReplyBabble("BABBLE C");
306 else if (rc == VERR_IO_BAD_COMMAND)
307 txsReplyBabble("BABBLE O");
308 else
309 txsReplyBabble("BABBLE ");
310 RTMemFree(pPktHdr);
311 }
312
313 /* Try again or return failure? */
314 if ( g_fTerminate
315 || rc != VERR_INTERRUPTED
316 || !fAutoRetryOnFailure
317 )
318 {
319 Log(("txsRecvPkt: rc=%Rrc\n", rc));
320 return rc;
321 }
322 }
323}
324
325/**
326 * Make a simple reply, only status opcode.
327 *
328 * @returns IPRT status code of the send.
329 * @param pReply The reply packet.
330 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
331 * with space.
332 * @param cbExtra Bytes in addition to the header.
333 */
334static int txsReplyInternal(PTXSPKTHDR pReply, const char *pszOpcode, size_t cbExtra)
335{
336 /* copy the opcode, don't be too strict in case of a padding screw up. */
337 size_t cchOpcode = strlen(pszOpcode);
338 if (RT_LIKELY(cchOpcode == sizeof(pReply->achOpcode)))
339 memcpy(pReply->achOpcode, pszOpcode, sizeof(pReply->achOpcode));
340 else
341 {
342 Assert(cchOpcode == sizeof(pReply->achOpcode));
343 while (cchOpcode > 0 && pszOpcode[cchOpcode - 1] == ' ')
344 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);
354}
355
356/**
357 * Make a simple reply, only status opcode.
358 *
359 * @returns IPRT status code of the send.
360 * @param pPktHdr The original packet (for future use).
361 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
362 * with space.
363 */
364static int txsReplySimple(PCTXSPKTHDR pPktHdr, const char *pszOpcode)
365{
366 TXSPKTHDR Pkt;
367 NOREF(pPktHdr);
368 return txsReplyInternal(&Pkt, pszOpcode, 0);
369}
370
371/**
372 * Acknowledges a packet with success.
373 *
374 * @returns IPRT status code of the send.
375 * @param pPktHdr The original packet (for future use).
376 */
377static int txsReplyAck(PCTXSPKTHDR pPktHdr)
378{
379 return txsReplySimple(pPktHdr, "ACK ");
380}
381
382/**
383 * Replies with a failure.
384 *
385 * @returns IPRT status code of the send.
386 * @param pPktHdr The original packet (for future use).
387 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
388 * with space.
389 * @param pszDetailFmt Longer description of the problem (format
390 * string).
391 * @param va Format arguments.
392 */
393static int txsReplyFailureV(PCTXSPKTHDR pPktHdr, const char *pszOpcode, const char *pszDetailFmt, va_list va)
394{
395 NOREF(pPktHdr);
396 union
397 {
398 TXSPKTHDR Hdr;
399 char ach[256];
400 } uPkt;
401
402 size_t cchDetail = RTStrPrintfV(&uPkt.ach[sizeof(TXSPKTHDR)],
403 sizeof(uPkt) - sizeof(TXSPKTHDR),
404 pszDetailFmt, va);
405 return txsReplyInternal(&uPkt.Hdr, pszOpcode, cchDetail + 1);
406}
407
408/**
409 * Replies with a failure.
410 *
411 * @returns IPRT status code of the send.
412 * @param pPktHdr The original packet (for future use).
413 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
414 * with space.
415 * @param pszDetailFmt Longer description of the problem (format
416 * string).
417 * @param ... Format arguments.
418 */
419static int txsReplyFailure(PCTXSPKTHDR pPktHdr, const char *pszOpcode, const char *pszDetailFmt, ...)
420{
421 va_list va;
422 va_start(va, pszDetailFmt);
423 int rc = txsReplyFailureV(pPktHdr, pszOpcode, pszDetailFmt, va);
424 va_end(va);
425 return rc;
426}
427
428/**
429 * Replies according to the return code.
430 *
431 * @returns IPRT status code of the send.
432 * @param pPktHdr The packet to reply to.
433 * @param rcOperation The status code to report.
434 * @param pszOperationFmt The operation that failed. Typically giving the
435 * function call with important arguments.
436 * @param ... Arguments to the format string.
437 */
438static int txsReplyRC(PCTXSPKTHDR pPktHdr, int rcOperation, const char *pszOperationFmt, ...)
439{
440 if (RT_SUCCESS(rcOperation))
441 return txsReplyAck(pPktHdr);
442
443 char szOperation[128];
444 va_list va;
445 va_start(va, pszOperationFmt);
446 RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va);
447 va_end(va);
448
449 return txsReplyFailure(pPktHdr, "FAILED ", "%s failed with rc=%Rrc (opcode '%.8s')",
450 szOperation, rcOperation, pPktHdr->achOpcode);
451}
452
453/**
454 * Signal a bad packet minum size.
455 *
456 * @returns IPRT status code of the send.
457 * @param pPktHdr The packet to reply to.
458 * @param cbMin The minimum size.
459 */
460static int txsReplyBadMinSize(PCTXSPKTHDR pPktHdr, size_t cbMin)
461{
462 return txsReplyFailure(pPktHdr, "BAD SIZE", "Expected at least %zu bytes, got %u (opcode '%.8s')",
463 cbMin, pPktHdr->cb, pPktHdr->achOpcode);
464}
465
466/**
467 * Signal a bad packet exact size.
468 *
469 * @returns IPRT status code of the send.
470 * @param pPktHdr The packet to reply to.
471 * @param cb The wanted size.
472 */
473static int txsReplyBadSize(PCTXSPKTHDR pPktHdr, size_t cb)
474{
475 return txsReplyFailure(pPktHdr, "BAD SIZE", "Expected at %zu bytes, got %u (opcode '%.8s')",
476 cb, pPktHdr->cb, pPktHdr->achOpcode);
477}
478
479/**
480 * Deals with a command that isn't implemented yet.
481 * @returns IPRT status code of the send.
482 * @param pPktHdr The packet which opcode isn't implemented.
483 */
484static int txsReplyNotImplemented(PCTXSPKTHDR pPktHdr)
485{
486 return txsReplyFailure(pPktHdr, "NOT IMPL", "Opcode '%.8s' is not implemented", pPktHdr->achOpcode);
487}
488
489/**
490 * Deals with a unknown command.
491 * @returns IPRT status code of the send.
492 * @param pPktHdr The packet to reply to.
493 */
494static 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 */
510static 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 */
541static 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 */
617static 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 */
667static 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);
677}
678
679/**
680 * Checks if the two opcodes match.
681 *
682 * @returns true on match, false on mismatch.
683 * @param pPktHdr The packet header.
684 * @param pszOpcode2 The opcode we're comparing with. Does not have
685 * to be the whole 8 chars long.
686 */
687DECLINLINE(bool) txsIsSameOpcode(PCTXSPKTHDR pPktHdr, const char *pszOpcode2)
688{
689 if (pPktHdr->achOpcode[0] != pszOpcode2[0])
690 return false;
691 if (pPktHdr->achOpcode[1] != pszOpcode2[1])
692 return false;
693
694 unsigned i = 2;
695 while ( i < RT_SIZEOFMEMB(TXSPKTHDR, achOpcode)
696 && pszOpcode2[i] != '\0')
697 {
698 if (pPktHdr->achOpcode[i] != pszOpcode2[i])
699 break;
700 i++;
701 }
702
703 if ( i < RT_SIZEOFMEMB(TXSPKTHDR, achOpcode)
704 && pszOpcode2[i] == '\0')
705 {
706 while ( i < RT_SIZEOFMEMB(TXSPKTHDR, achOpcode)
707 && pPktHdr->achOpcode[i] == ' ')
708 i++;
709 }
710
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 */
722static int txsWaitForAck(PCTXSPKTHDR pPktHdr)
723{
724 NOREF(pPktHdr);
725 /** @todo timeout? */
726 PTXSPKTHDR pReply;
727 int rc = txsRecvPkt(&pReply, false /*fAutoRetryOnFailure*/);
728 if (RT_SUCCESS(rc))
729 {
730 if (txsIsSameOpcode(pReply, "ACK"))
731 rc = VINF_SUCCESS;
732 else if (txsIsSameOpcode(pReply, "NACK"))
733 rc = VERR_GENERAL_FAILURE;
734 else
735 {
736 txsReplyBabble("BABBLE ");
737 rc = VERR_NET_NOT_CONNECTED;
738 }
739 RTMemFree(pReply);
740 }
741 return rc;
742}
743
744/**
745 * Unpacks a tar file.
746 *
747 * @returns IPRT status code from send.
748 * @param pPktHdr The unpack file packet.
749 */
750static int txsDoUnpackFile(PCTXSPKTHDR pPktHdr)
751{
752 int rc;
753 char *pszFile = NULL;
754 char *pszDirectory = NULL;
755
756 /* Packet cursor. */
757 const char *pch = (const char *)(pPktHdr + 1);
758
759 if (txsIsStringValid(pPktHdr, "file", pch, &pszFile, &pch, &rc))
760 {
761 if (txsIsStringValid(pPktHdr, "directory", pch, &pszDirectory, &pch, &rc))
762 {
763 char *pszSuff = RTPathSuffix(pszFile);
764
765 const char *apszArgs[7];
766 unsigned cArgs = 0;
767
768 apszArgs[cArgs++] = "RTTar";
769 apszArgs[cArgs++] = "--extract";
770
771 apszArgs[cArgs++] = "--file";
772 apszArgs[cArgs++] = pszFile;
773
774 apszArgs[cArgs++] = "--directory";
775 apszArgs[cArgs++] = pszDirectory;
776
777 if ( pszSuff
778 && ( !RTStrICmp(pszSuff, ".gz")
779 || !RTStrICmp(pszSuff, ".tgz")))
780 apszArgs[cArgs++] = "--gunzip";
781
782 RTEXITCODE rcExit = RTZipTarCmd(cArgs, (char **)apszArgs);
783 if (rcExit != RTEXITCODE_SUCCESS)
784 rc = VERR_GENERAL_FAILURE; /** @todo proper return code. */
785 else
786 rc = VINF_SUCCESS;
787
788 rc = txsReplyRC(pPktHdr, rc, "RTZipTarCmd(\"%s\",\"%s\")",
789 pszFile, pszDirectory);
790
791 RTStrFree(pszDirectory);
792 }
793 RTStrFree(pszFile);
794 }
795
796 return rc;
797}
798
799/**
800 * Downloads a file to the client.
801 *
802 * The transfer sends a stream of DATA packets (0 or more) and ends it all with
803 * a ACK packet. If an error occurs, a FAILURE packet is sent and the transfer
804 * aborted.
805 *
806 * @returns IPRT status code from send.
807 * @param pPktHdr The get file packet.
808 */
809static int txsDoGetFile(PCTXSPKTHDR pPktHdr)
810{
811 int rc;
812 char *pszPath;
813 if (!txsIsStringPktValid(pPktHdr, "file", &pszPath, &rc))
814 return rc;
815
816 RTFILE hFile;
817 rc = RTFileOpen(&hFile, pszPath, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN);
818 if (RT_SUCCESS(rc))
819 {
820 uint32_t uMyCrc32 = RTCrc32Start();
821 for (;;)
822 {
823 struct
824 {
825 TXSPKTHDR Hdr;
826 uint32_t uCrc32;
827 char ab[_64K];
828 char abPadding[TXSPKT_ALIGNMENT];
829 } Pkt;
830 size_t cbRead;
831 rc = RTFileRead(hFile, &Pkt.ab[0], _64K, &cbRead);
832 if (RT_FAILURE(rc) || cbRead == 0)
833 {
834 if (rc == VERR_EOF || (RT_SUCCESS(rc) && cbRead == 0))
835 {
836 Pkt.uCrc32 = RTCrc32Finish(uMyCrc32);
837 rc = txsReplyInternal(&Pkt.Hdr, "DATA EOF", sizeof(uint32_t));
838 if (RT_SUCCESS(rc))
839 rc = txsWaitForAck(&Pkt.Hdr);
840 }
841 else
842 rc = txsReplyRC(pPktHdr, rc, "RTFileRead");
843 break;
844 }
845
846 uMyCrc32 = RTCrc32Process(uMyCrc32, &Pkt.ab[0], cbRead);
847 Pkt.uCrc32 = RTCrc32Finish(uMyCrc32);
848 rc = txsReplyInternal(&Pkt.Hdr, "DATA ", cbRead + sizeof(uint32_t));
849 if (RT_FAILURE(rc))
850 break;
851 rc = txsWaitForAck(&Pkt.Hdr);
852 if (RT_FAILURE(rc))
853 break;
854 }
855
856 RTFileClose(hFile);
857 }
858 else
859 rc = txsReplyRC(pPktHdr, rc, "RTFileOpen(,\"%s\",)", pszPath);
860
861 RTStrFree(pszPath);
862 return rc;
863}
864
865/**
866 * Uploads a file from the client.
867 *
868 * The transfer sends a stream of DATA packets (0 or more) and ends it all with
869 * a DATA EOF packet. We ACK each of these, so that if a write error occurs we
870 * can abort the transfer straight away.
871 *
872 * @returns IPRT status code from send.
873 * @param pPktHdr The put file packet.
874 */
875static int txsDoPutFile(PCTXSPKTHDR pPktHdr)
876{
877 int rc;
878 char *pszPath;
879 if (!txsIsStringPktValid(pPktHdr, "file", &pszPath, &rc))
880 return rc;
881
882 RTFILE hFile;
883 rc = RTFileOpen(&hFile, pszPath, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE);
884 if (RT_SUCCESS(rc))
885 {
886 bool fSuccess = false;
887 rc = txsReplyAck(pPktHdr);
888 if (RT_SUCCESS(rc))
889 {
890 /*
891 * Read client command packets and process them.
892 */
893 uint32_t uMyCrc32 = RTCrc32Start();
894 for (;;)
895 {
896 PTXSPKTHDR pDataPktHdr;
897 rc = txsRecvPkt(&pDataPktHdr, false /*fAutoRetryOnFailure*/);
898 if (RT_FAILURE(rc))
899 break;
900
901 if (txsIsSameOpcode(pDataPktHdr, "DATA"))
902 {
903 size_t const cbMin = sizeof(TXSPKTHDR) + sizeof(uint32_t);
904 if (pDataPktHdr->cb >= cbMin)
905 {
906 size_t cbData = pDataPktHdr->cb - cbMin;
907 const void *pvData = (const char *)pDataPktHdr + cbMin;
908 uint32_t uCrc32 = *(uint32_t const *)(pDataPktHdr + 1);
909
910 uMyCrc32 = RTCrc32Process(uMyCrc32, pvData, cbData);
911 if (RTCrc32Finish(uMyCrc32) == uCrc32)
912 {
913 rc = RTFileWrite(hFile, pvData, cbData, NULL);
914 if (RT_SUCCESS(rc))
915 {
916 rc = txsReplyAck(pDataPktHdr);
917 RTMemFree(pDataPktHdr);
918 continue;
919 }
920
921 rc = txsReplyRC(pDataPktHdr, rc, "RTFileWrite");
922 }
923 else
924 rc = txsReplyFailure(pDataPktHdr, "BAD DCRC", "mycrc=%#x your=%#x", uMyCrc32, uCrc32);
925 }
926 else
927 rc = txsReplyBadMinSize(pPktHdr, cbMin);
928 }
929 else if (txsIsSameOpcode(pDataPktHdr, "DATA EOF"))
930 {
931 if (pDataPktHdr->cb == sizeof(TXSPKTHDR) + sizeof(uint32_t))
932 {
933 uint32_t uCrc32 = *(uint32_t const *)(pDataPktHdr + 1);
934 if (RTCrc32Finish(uMyCrc32) == uCrc32)
935 {
936 rc = txsReplyAck(pDataPktHdr);
937 fSuccess = RT_SUCCESS(rc);
938 }
939 else
940 rc = txsReplyFailure(pDataPktHdr, "BAD DCRC", "mycrc=%#x your=%#x", uMyCrc32, uCrc32);
941 }
942 else
943 rc = txsReplyAck(pDataPktHdr);
944 }
945 else if (txsIsSameOpcode(pDataPktHdr, "ABORT"))
946 rc = txsReplyAck(pDataPktHdr);
947 else
948 rc = txsReplyFailure(pDataPktHdr, "UNKNOWN ", "Opcode '%.8s' is not known or not recognized during PUT FILE", pDataPktHdr->achOpcode);
949 RTMemFree(pDataPktHdr);
950 break;
951 }
952 }
953
954 RTFileClose(hFile);
955
956 /*
957 * Delete the file on failure.
958 */
959 if (!fSuccess)
960 RTFileDelete(pszPath);
961 }
962 else
963 rc = txsReplyRC(pPktHdr, rc, "RTFileOpen(,\"%s\",)", pszPath);
964
965 RTStrFree(pszPath);
966 return rc;
967}
968
969/**
970 * List the entries in the specified directory.
971 *
972 * @returns IPRT status code from send.
973 * @param pPktHdr The list packet.
974 */
975static int txsDoList(PCTXSPKTHDR pPktHdr)
976{
977 int rc;
978 char *pszPath;
979 if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc))
980 return rc;
981
982 rc = txsReplyNotImplemented(pPktHdr);
983
984 RTStrFree(pszPath);
985 return rc;
986}
987
988
989/**
990 * Get info about a file system object, following all but the symbolic links
991 * except in the final path component.
992 *
993 * @returns IPRT status code from send.
994 * @param pPktHdr The lstat packet.
995 */
996static int txsDoLStat(PCTXSPKTHDR pPktHdr)
997{
998 int rc;
999 char *pszPath;
1000 if (!txsIsStringPktValid(pPktHdr, "path", &pszPath, &rc))
1001 return rc;
1002
1003 RTFSOBJINFO Info;
1004 rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
1005 if (RT_SUCCESS(rc))
1006 /** @todo figure out how to format the return buffer here. */
1007 rc = txsReplyNotImplemented(pPktHdr);
1008 else
1009 rc = txsReplyRC(pPktHdr, rc, "RTPathQueryInfoEx(\"%s\",,UNIX,ON_LINK)", pszPath);
1010
1011 RTStrFree(pszPath);
1012 return rc;
1013}
1014
1015/**
1016 * Get info about a file system object, following all symbolic links.
1017 *
1018 * @returns IPRT status code from send.
1019 * @param pPktHdr The stat packet.
1020 */
1021static int txsDoStat(PCTXSPKTHDR pPktHdr)
1022{
1023 int rc;
1024 char *pszPath;
1025 if (!txsIsStringPktValid(pPktHdr, "path", &pszPath, &rc))
1026 return rc;
1027
1028 RTFSOBJINFO Info;
1029 rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK);
1030 if (RT_SUCCESS(rc))
1031 /** @todo figure out how to format the return buffer here. */
1032 rc = txsReplyNotImplemented(pPktHdr);
1033 else
1034 rc = txsReplyRC(pPktHdr, rc, "RTPathQueryInfoEx(\"%s\",,UNIX,FOLLOW_LINK)", pszPath);
1035
1036 RTStrFree(pszPath);
1037 return rc;
1038}
1039
1040/**
1041 * Checks if the specified path is a symbolic link.
1042 *
1043 * @returns IPRT status code from send.
1044 * @param pPktHdr The issymlnk packet.
1045 */
1046static int txsDoIsSymlnk(PCTXSPKTHDR pPktHdr)
1047{
1048 int rc;
1049 char *pszPath;
1050 if (!txsIsStringPktValid(pPktHdr, "symlink", &pszPath, &rc))
1051 return rc;
1052
1053 RTFSOBJINFO Info;
1054 rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1055 if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(Info.Attr.fMode))
1056 rc = txsReplySimple(pPktHdr, "TRUE ");
1057 else
1058 rc = txsReplySimple(pPktHdr, "FALSE ");
1059
1060 RTStrFree(pszPath);
1061 return rc;
1062}
1063
1064/**
1065 * Checks if the specified path is a file or not.
1066 *
1067 * If the final path element is a symbolic link to a file, we'll return
1068 * FALSE.
1069 *
1070 * @returns IPRT status code from send.
1071 * @param pPktHdr The isfile packet.
1072 */
1073static int txsDoIsFile(PCTXSPKTHDR pPktHdr)
1074{
1075 int rc;
1076 char *pszPath;
1077 if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc))
1078 return rc;
1079
1080 RTFSOBJINFO Info;
1081 rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1082 if (RT_SUCCESS(rc) && RTFS_IS_FILE(Info.Attr.fMode))
1083 rc = txsReplySimple(pPktHdr, "TRUE ");
1084 else
1085 rc = txsReplySimple(pPktHdr, "FALSE ");
1086
1087 RTStrFree(pszPath);
1088 return rc;
1089}
1090
1091/**
1092 * Checks if the specified path is a directory or not.
1093 *
1094 * If the final path element is a symbolic link to a directory, we'll return
1095 * FALSE.
1096 *
1097 * @returns IPRT status code from send.
1098 * @param pPktHdr The isdir packet.
1099 */
1100static int txsDoIsDir(PCTXSPKTHDR pPktHdr)
1101{
1102 int rc;
1103 char *pszPath;
1104 if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc))
1105 return rc;
1106
1107 RTFSOBJINFO Info;
1108 rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1109 if (RT_SUCCESS(rc) && RTFS_IS_DIRECTORY(Info.Attr.fMode))
1110 rc = txsReplySimple(pPktHdr, "TRUE ");
1111 else
1112 rc = txsReplySimple(pPktHdr, "FALSE ");
1113
1114 RTStrFree(pszPath);
1115 return rc;
1116}
1117
1118/**
1119 * Changes the group of a file, directory of symbolic link.
1120 *
1121 * @returns IPRT status code from send.
1122 * @param pPktHdr The chmod packet.
1123 */
1124static int txsDoChGrp(PCTXSPKTHDR pPktHdr)
1125{
1126 return txsReplyNotImplemented(pPktHdr);
1127}
1128
1129/**
1130 * Changes the owner of a file, directory of symbolic link.
1131 *
1132 * @returns IPRT status code from send.
1133 * @param pPktHdr The chmod packet.
1134 */
1135static int txsDoChOwn(PCTXSPKTHDR pPktHdr)
1136{
1137 return txsReplyNotImplemented(pPktHdr);
1138}
1139
1140/**
1141 * Changes the mode of a file or directory.
1142 *
1143 * @returns IPRT status code from send.
1144 * @param pPktHdr The chmod packet.
1145 */
1146static int txsDoChMod(PCTXSPKTHDR pPktHdr)
1147{
1148 return txsReplyNotImplemented(pPktHdr);
1149}
1150
1151/**
1152 * Removes a directory tree.
1153 *
1154 * @returns IPRT status code from send.
1155 * @param pPktHdr The rmtree packet.
1156 */
1157static int txsDoRmTree(PCTXSPKTHDR pPktHdr)
1158{
1159 int rc;
1160 char *pszPath;
1161 if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc))
1162 return rc;
1163
1164 rc = RTDirRemoveRecursive(pszPath, 0 /*fFlags*/);
1165
1166 rc = txsReplyRC(pPktHdr, rc, "RTDirRemoveRecusive(\"%s\",0)", pszPath);
1167 RTStrFree(pszPath);
1168 return rc;
1169}
1170
1171/**
1172 * Removes a symbolic link.
1173 *
1174 * @returns IPRT status code from send.
1175 * @param pPktHdr The rmsymlink packet.
1176 */
1177static int txsDoRmSymlnk(PCTXSPKTHDR pPktHdr)
1178{
1179 int rc;
1180 char *pszPath;
1181 if (!txsIsStringPktValid(pPktHdr, "symlink", &pszPath, &rc))
1182 return rc;
1183
1184 rc = VERR_NOT_IMPLEMENTED; /// @todo RTSymlinkDelete(pszPath);
1185
1186 rc = txsReplyRC(pPktHdr, rc, "RTSymlinkDelete(\"%s\")", pszPath);
1187 RTStrFree(pszPath);
1188 return rc;
1189}
1190
1191/**
1192 * Removes a file.
1193 *
1194 * @returns IPRT status code from send.
1195 * @param pPktHdr The rmfile packet.
1196 */
1197static int txsDoRmFile(PCTXSPKTHDR pPktHdr)
1198{
1199 int rc;
1200 char *pszPath;
1201 if (!txsIsStringPktValid(pPktHdr, "file", &pszPath, &rc))
1202 return rc;
1203
1204 rc = RTFileDelete(pszPath);
1205
1206 rc = txsReplyRC(pPktHdr, rc, "RTFileDelete(\"%s\")", pszPath);
1207 RTStrFree(pszPath);
1208 return rc;
1209}
1210
1211/**
1212 * Removes a directory.
1213 *
1214 * @returns IPRT status code from send.
1215 * @param pPktHdr The rmdir packet.
1216 */
1217static int txsDoRmDir(PCTXSPKTHDR pPktHdr)
1218{
1219 int rc;
1220 char *pszPath;
1221 if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc))
1222 return rc;
1223
1224 rc = RTDirRemove(pszPath);
1225
1226 rc = txsReplyRC(pPktHdr, rc, "RTDirRemove(\"%s\")", pszPath);
1227 RTStrFree(pszPath);
1228 return rc;
1229}
1230
1231/**
1232 * Creates a symbolic link.
1233 *
1234 * @returns IPRT status code from send.
1235 * @param pPktHdr The mksymlnk packet.
1236 */
1237static int txsDoMkSymlnk(PCTXSPKTHDR pPktHdr)
1238{
1239 return txsReplyNotImplemented(pPktHdr);
1240}
1241
1242/**
1243 * Creates a directory and all its parents.
1244 *
1245 * @returns IPRT status code from send.
1246 * @param pPktHdr The mkdir -p packet.
1247 */
1248static int txsDoMkDrPath(PCTXSPKTHDR pPktHdr)
1249{
1250 /* The same format as the MKDIR command. */
1251 if (pPktHdr->cb < sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2)
1252 return txsReplyBadMinSize(pPktHdr, sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2);
1253
1254 int rc;
1255 char *pszPath;
1256 if (!txsIsStringValid(pPktHdr, "dir", (const char *)(pPktHdr + 1) + sizeof(RTFMODE), &pszPath, NULL, &rc))
1257 return rc;
1258
1259 RTFMODE fMode = *(RTFMODE const *)(pPktHdr + 1);
1260 rc = RTDirCreateFullPath(pszPath, fMode);
1261
1262 rc = txsReplyRC(pPktHdr, rc, "RTDirCreateFullPath(\"%s\", %#x)", pszPath, fMode);
1263 RTStrFree(pszPath);
1264 return rc;
1265}
1266
1267/**
1268 * Creates a directory.
1269 *
1270 * @returns IPRT status code from send.
1271 * @param pPktHdr The mkdir packet.
1272 */
1273static int txsDoMkDir(PCTXSPKTHDR pPktHdr)
1274{
1275 /* After the packet header follows a mode mask and the remainder of
1276 the packet is the zero terminated directory name. */
1277 size_t const cbMin = sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2;
1278 if (pPktHdr->cb < cbMin)
1279 return txsReplyBadMinSize(pPktHdr, cbMin);
1280
1281 int rc;
1282 char *pszPath;
1283 if (!txsIsStringValid(pPktHdr, "dir", (const char *)(pPktHdr + 1) + sizeof(RTFMODE), &pszPath, NULL, &rc))
1284 return rc;
1285
1286 RTFMODE fMode = *(RTFMODE const *)(pPktHdr + 1);
1287 rc = RTDirCreate(pszPath, fMode, 0);
1288
1289 rc = txsReplyRC(pPktHdr, rc, "RTDirCreate(\"%s\", %#x)", pszPath, fMode);
1290 RTStrFree(pszPath);
1291 return rc;
1292}
1293
1294/**
1295 * Cleans up the scratch area.
1296 *
1297 * @returns IPRT status code from send.
1298 * @param pPktHdr The shutdown packet.
1299 */
1300static int txsDoCleanup(PCTXSPKTHDR pPktHdr)
1301{
1302 int rc = RTDirRemoveRecursive(g_szScratchPath, RTDIRRMREC_F_CONTENT_ONLY);
1303 return txsReplyRC(pPktHdr, rc, "RTDirRemoveRecursive(\"%s\", CONTENT_ONLY)", g_szScratchPath);
1304}
1305
1306/**
1307 * Ejects the specified DVD/CD drive.
1308 *
1309 * @returns IPRT status code from send.
1310 * @param pPktHdr The eject packet.
1311 */
1312static int txsDoCdEject(PCTXSPKTHDR pPktHdr)
1313{
1314 /* After the packet header follows a uint32_t ordinal. */
1315 size_t const cbExpected = sizeof(TXSPKTHDR) + sizeof(uint32_t);
1316 if (pPktHdr->cb != cbExpected)
1317 return txsReplyBadSize(pPktHdr, cbExpected);
1318 uint32_t iOrdinal = *(uint32_t const *)(pPktHdr + 1);
1319
1320 RTCDROM hCdrom;
1321 int rc = RTCdromOpenByOrdinal(iOrdinal, RTCDROM_O_CONTROL, &hCdrom);
1322 if (RT_FAILURE(rc))
1323 return txsReplyRC(pPktHdr, rc, "RTCdromOpenByOrdinal(%u, RTCDROM_O_CONTROL, )", iOrdinal);
1324 rc = RTCdromEject(hCdrom, true /*fForce*/);
1325 RTCdromRelease(hCdrom);
1326
1327 return txsReplyRC(pPktHdr, rc, "RTCdromEject(ord=%u, fForce=true)", iOrdinal);
1328}
1329
1330/**
1331 * Common worker for txsDoShutdown and txsDoReboot.
1332 *
1333 * @returns IPRT status code from send.
1334 * @param pPktHdr The reboot packet.
1335 * @param fAction Which action to take.
1336 */
1337static int txsCommonShutdownReboot(PCTXSPKTHDR pPktHdr, uint32_t fAction)
1338{
1339 /*
1340 * We ACK the reboot & shutdown before actually performing them, then we
1341 * terminate the transport layer.
1342 *
1343 * This is to make sure the client isn't stuck with a dead connection. The
1344 * transport layer termination also make sure we won't accept new
1345 * connections in case the client is too eager to reconnect to a rebooted
1346 * test victim. On the down side, we cannot easily report RTSystemShutdown
1347 * failures failures this way. But the client can kind of figure it out by
1348 * reconnecting and seeing that our UUID was unchanged.
1349 */
1350 int rc;
1351 if (pPktHdr->cb != sizeof(TXSPKTHDR))
1352 return txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR));
1353 g_pTransport->pfnNotifyReboot();
1354 rc = txsReplyAck(pPktHdr);
1355 RTThreadSleep(2560); /* fudge factor */
1356 g_pTransport->pfnTerm();
1357
1358 /*
1359 * Do the job, if it fails we'll restart the transport layer.
1360 */
1361#if 0
1362 rc = VINF_SUCCESS;
1363#else
1364 rc = RTSystemShutdown(0 /*cMsDelay*/,
1365 fAction | RTSYSTEM_SHUTDOWN_PLANNED | RTSYSTEM_SHUTDOWN_FORCE,
1366 "Test Execution Service");
1367#endif
1368 if (RT_SUCCESS(rc))
1369 {
1370 RTMsgInfo(fAction == RTSYSTEM_SHUTDOWN_REBOOT ? "Rebooting...\n" : "Shutting down...\n");
1371 g_fTerminate = true;
1372 }
1373 else
1374 {
1375 RTMsgError("RTSystemShutdown w/ fAction=%#x failed: %Rrc", fAction, rc);
1376
1377 int rc2 = g_pTransport->pfnInit();
1378 if (RT_FAILURE(rc2))
1379 {
1380 g_fTerminate = true;
1381 rc = rc2;
1382 }
1383 }
1384 return rc;
1385}
1386
1387/**
1388 * Shuts down the machine, powering it off if possible.
1389 *
1390 * @returns IPRT status code from send.
1391 * @param pPktHdr The shutdown packet.
1392 */
1393static int txsDoShutdown(PCTXSPKTHDR pPktHdr)
1394{
1395 return txsCommonShutdownReboot(pPktHdr, RTSYSTEM_SHUTDOWN_POWER_OFF_HALT);
1396}
1397
1398/**
1399 * Reboots the machine.
1400 *
1401 * @returns IPRT status code from send.
1402 * @param pPktHdr The reboot packet.
1403 */
1404static int txsDoReboot(PCTXSPKTHDR pPktHdr)
1405{
1406 return txsCommonShutdownReboot(pPktHdr, RTSYSTEM_SHUTDOWN_REBOOT);
1407}
1408
1409/**
1410 * Verifies and acknowledges a "UUID" request.
1411 *
1412 * @returns IPRT status code.
1413 * @param pPktHdr The howdy packet.
1414 */
1415static int txsDoUuid(PCTXSPKTHDR pPktHdr)
1416{
1417 if (pPktHdr->cb != sizeof(TXSPKTHDR))
1418 return txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR));
1419
1420 struct
1421 {
1422 TXSPKTHDR Hdr;
1423 char szUuid[RTUUID_STR_LENGTH];
1424 char abPadding[TXSPKT_ALIGNMENT];
1425 } Pkt;
1426
1427 int rc = RTUuidToStr(&g_InstanceUuid, Pkt.szUuid, sizeof(Pkt.szUuid));
1428 if (RT_FAILURE(rc))
1429 return txsReplyRC(pPktHdr, rc, "RTUuidToStr");
1430 return txsReplyInternal(&Pkt.Hdr, "ACK UUID", strlen(Pkt.szUuid) + 1);
1431}
1432
1433/**
1434 * Verifies and acknowledges a "BYE" request.
1435 *
1436 * @returns IPRT status code.
1437 * @param pPktHdr The howdy packet.
1438 */
1439static int txsDoBye(PCTXSPKTHDR pPktHdr)
1440{
1441 int rc;
1442 if (pPktHdr->cb == sizeof(TXSPKTHDR))
1443 rc = txsReplyAck(pPktHdr);
1444 else
1445 rc = txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR));
1446 g_pTransport->pfnNotifyBye();
1447 return rc;
1448}
1449
1450/**
1451 * Verifies and acknowledges a "HOWDY" request.
1452 *
1453 * @returns IPRT status code.
1454 * @param pPktHdr The howdy packet.
1455 */
1456static int txsDoHowdy(PCTXSPKTHDR pPktHdr)
1457{
1458 if (pPktHdr->cb != sizeof(TXSPKTHDR))
1459 return txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR));
1460 int rc = txsReplyAck(pPktHdr);
1461 if (RT_SUCCESS(rc))
1462 {
1463 g_pTransport->pfnNotifyHowdy();
1464 RTDirRemoveRecursive(g_szScratchPath, RTDIRRMREC_F_CONTENT_ONLY);
1465 }
1466 return rc;
1467}
1468
1469/**
1470 * Replies according to the return code.
1471 *
1472 * @returns rcOperation and pTxsExec->rcReplySend.
1473 * @param pTxsExec The TXSEXEC instance.
1474 * @param rcOperation The status code to report.
1475 * @param pszOperationFmt The operation that failed. Typically giving the
1476 * function call with important arguments.
1477 * @param ... Arguments to the format string.
1478 */
1479static int txsExecReplyRC(PTXSEXEC pTxsExec, int rcOperation, const char *pszOperationFmt, ...)
1480{
1481 AssertStmt(RT_FAILURE_NP(rcOperation), rcOperation = VERR_IPE_UNEXPECTED_INFO_STATUS);
1482
1483 char szOperation[128];
1484 va_list va;
1485 va_start(va, pszOperationFmt);
1486 RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va);
1487 va_end(va);
1488
1489 pTxsExec->rcReplySend = txsReplyFailure(pTxsExec->pPktHdr, "FAILED ",
1490 "%s failed with rc=%Rrc (opcode '%.8s')",
1491 szOperation, rcOperation, pTxsExec->pPktHdr->achOpcode);
1492 return rcOperation;
1493}
1494
1495
1496/**
1497 * Sends the process exit status reply to the TXS client.
1498 *
1499 * @returns IPRT status code of the send.
1500 * @param pTxsExec The TXSEXEC instance.
1501 * @param fProcessAlive Whether the process is still alive (against our
1502 * will).
1503 * @param fProcessTimedOut Whether the process timed out.
1504 * @param MsProcessKilled When the process was killed, UINT64_MAX if not.
1505 */
1506static int txsExecSendExitStatus(PTXSEXEC pTxsExec, bool fProcessAlive, bool fProcessTimedOut, uint64_t MsProcessKilled)
1507{
1508 int rc;
1509 if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
1510 {
1511 rc = txsReplySimple(pTxsExec->pPktHdr, "PROC TOK");
1512 if (g_fDisplayOutput)
1513 RTPrintf("txs: Process timed out and was killed\n");
1514 }
1515 else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
1516 {
1517 rc = txsReplySimple(pTxsExec->pPktHdr, "PROC TOA");
1518 if (g_fDisplayOutput)
1519 RTPrintf("txs: Process timed out and was not killed successfully\n");
1520 }
1521 else if (g_fTerminate && (fProcessAlive || MsProcessKilled != UINT64_MAX))
1522 rc = txsReplySimple(pTxsExec->pPktHdr, "PROC DWN");
1523 else if (fProcessAlive)
1524 {
1525 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC DOO", "Doofus! process is alive when it should not");
1526 AssertFailed();
1527 }
1528 else if (MsProcessKilled != UINT64_MAX)
1529 {
1530 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC DOO", "Doofus! process has been killed when it should not");
1531 AssertFailed();
1532 }
1533 else if ( pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL
1534 && pTxsExec->ProcessStatus.iStatus == 0)
1535 {
1536 rc = txsReplySimple(pTxsExec->pPktHdr, "PROC OK ");
1537 if (g_fDisplayOutput)
1538 RTPrintf("txs: Process exited with status: 0\n");
1539 }
1540 else if (pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
1541 {
1542 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC NOK", "%d", pTxsExec->ProcessStatus.iStatus);
1543 if (g_fDisplayOutput)
1544 RTPrintf("txs: Process exited with status: %d\n", pTxsExec->ProcessStatus.iStatus);
1545 }
1546 else if (pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
1547 {
1548 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC SIG", "%d", pTxsExec->ProcessStatus.iStatus);
1549 if (g_fDisplayOutput)
1550 RTPrintf("txs: Process exited with status: signal %d\n", pTxsExec->ProcessStatus.iStatus);
1551 }
1552 else if (pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
1553 {
1554 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC ABD", "");
1555 if (g_fDisplayOutput)
1556 RTPrintf("txs: Process exited with status: abend\n");
1557 }
1558 else
1559 {
1560 rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC DOO", "enmReason=%d iStatus=%d",
1561 pTxsExec->ProcessStatus.enmReason, pTxsExec->ProcessStatus.iStatus);
1562 AssertMsgFailed(("enmReason=%d iStatus=%d", pTxsExec->ProcessStatus.enmReason, pTxsExec->ProcessStatus.iStatus));
1563 }
1564 return rc;
1565}
1566
1567/**
1568 * Handle pending output data or error on standard out, standard error or the
1569 * test pipe.
1570 *
1571 * @returns IPRT status code from client send.
1572 * @param hPollSet The polling set.
1573 * @param fPollEvt The event mask returned by RTPollNoResume.
1574 * @param phPipeR The pipe handle.
1575 * @param puCrc32 The current CRC-32 of the stream. (In/Out)
1576 * @param enmHndId The handle ID.
1577 * @param pszOpcode The opcode for the data upload.
1578 *
1579 * @todo Put the last 4 parameters into a struct!
1580 */
1581static int txsDoExecHlpHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phPipeR,
1582 uint32_t *puCrc32, TXSEXECHNDID enmHndId, const char *pszOpcode)
1583{
1584 Log(("txsDoExecHlpHandleOutputEvent: %s fPollEvt=%#x\n", pszOpcode, fPollEvt));
1585
1586 /*
1587 * Try drain the pipe before acting on any errors.
1588 */
1589 int rc = VINF_SUCCESS;
1590 struct
1591 {
1592 TXSPKTHDR Hdr;
1593 uint32_t uCrc32;
1594 char abBuf[_64K];
1595 char abPadding[TXSPKT_ALIGNMENT];
1596 } Pkt;
1597 size_t cbRead;
1598 int rc2 = RTPipeRead(*phPipeR, Pkt.abBuf, sizeof(Pkt.abBuf), &cbRead);
1599 if (RT_SUCCESS(rc2) && cbRead)
1600 {
1601 Log(("Crc32=%#x ", *puCrc32));
1602 *puCrc32 = RTCrc32Process(*puCrc32, Pkt.abBuf, cbRead);
1603 Log(("cbRead=%#x Crc32=%#x \n", cbRead, *puCrc32));
1604 Pkt.uCrc32 = RTCrc32Finish(*puCrc32);
1605 if (g_fDisplayOutput)
1606 {
1607 if (enmHndId == TXSEXECHNDID_STDOUT)
1608 RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf);
1609 else if (enmHndId == TXSEXECHNDID_STDERR)
1610 RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf);
1611 }
1612
1613 rc = txsReplyInternal(&Pkt.Hdr, pszOpcode, cbRead + sizeof(uint32_t));
1614
1615 /* Make sure we go another poll round in case there was too much data
1616 for the buffer to hold. */
1617 fPollEvt &= RTPOLL_EVT_ERROR;
1618 }
1619 else if (RT_FAILURE(rc2))
1620 {
1621 fPollEvt |= RTPOLL_EVT_ERROR;
1622 AssertMsg(rc2 == VERR_BROKEN_PIPE, ("%Rrc\n", rc));
1623 }
1624
1625 /*
1626 * If an error was raised signalled,
1627 */
1628 if (fPollEvt & RTPOLL_EVT_ERROR)
1629 {
1630 rc2 = RTPollSetRemove(hPollSet, enmHndId);
1631 AssertRC(rc2);
1632
1633 rc2 = RTPipeClose(*phPipeR);
1634 AssertRC(rc2);
1635 *phPipeR = NIL_RTPIPE;
1636 }
1637 return rc;
1638}
1639
1640/**
1641 * Try write some more data to the standard input of the child.
1642 *
1643 * @returns IPRT status code.
1644 * @param pStdInBuf The standard input buffer.
1645 * @param hStdInW The standard input pipe.
1646 */
1647static int txsDoExecHlpWriteStdIn(PTXSEXECSTDINBUF pStdInBuf, RTPIPE hStdInW)
1648{
1649 size_t cbToWrite = pStdInBuf->cb - pStdInBuf->off;
1650 size_t cbWritten;
1651 int rc = RTPipeWrite(hStdInW, &pStdInBuf->pch[pStdInBuf->off], cbToWrite, &cbWritten);
1652 if (RT_SUCCESS(rc))
1653 {
1654 Assert(cbWritten == cbToWrite);
1655 pStdInBuf->off += cbWritten;
1656 }
1657 return rc;
1658}
1659
1660/**
1661 * Handle an error event on standard input.
1662 *
1663 * @param hPollSet The polling set.
1664 * @param fPollEvt The event mask returned by RTPollNoResume.
1665 * @param phStdInW The standard input pipe handle.
1666 * @param pStdInBuf The standard input buffer.
1667 */
1668static void txsDoExecHlpHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
1669 PTXSEXECSTDINBUF pStdInBuf)
1670{
1671 NOREF(fPollEvt);
1672 int rc2;
1673 if (pStdInBuf->off < pStdInBuf->cb)
1674 {
1675 rc2 = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN_WRITABLE);
1676 AssertRC(rc2);
1677 }
1678
1679 rc2 = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN);
1680 AssertRC(rc2);
1681
1682 rc2 = RTPipeClose(*phStdInW);
1683 AssertRC(rc2);
1684 *phStdInW = NIL_RTPIPE;
1685
1686 RTMemFree(pStdInBuf->pch);
1687 pStdInBuf->pch = NULL;
1688 pStdInBuf->off = 0;
1689 pStdInBuf->cb = 0;
1690 pStdInBuf->cbAllocated = 0;
1691 pStdInBuf->fBitBucket = true;
1692}
1693
1694/**
1695 * Handle an event indicating we can write to the standard input pipe of the
1696 * child process.
1697 *
1698 * @param hPollSet The polling set.
1699 * @param fPollEvt The event mask returned by RTPollNoResume.
1700 * @param phStdInW The standard input pipe.
1701 * @param pStdInBuf The standard input buffer.
1702 */
1703static void txsDoExecHlpHandleStdInWritableEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
1704 PTXSEXECSTDINBUF pStdInBuf)
1705{
1706 int rc;
1707 if (!(fPollEvt & RTPOLL_EVT_ERROR))
1708 {
1709 rc = txsDoExecHlpWriteStdIn(pStdInBuf, *phStdInW);
1710 if (RT_FAILURE(rc) && rc != VERR_BAD_PIPE)
1711 {
1712 /** @todo do we need to do something about this error condition? */
1713 AssertRC(rc);
1714 }
1715
1716 if (pStdInBuf->off < pStdInBuf->cb)
1717 {
1718 rc = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN_WRITABLE);
1719 AssertRC(rc);
1720 }
1721 }
1722 else
1723 txsDoExecHlpHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
1724}
1725
1726/**
1727 * Handle a transport event or successful pfnPollIn() call.
1728 *
1729 * @returns IPRT status code from client send.
1730 * @retval VINF_EOF indicates ABORT command.
1731 *
1732 * @param hPollSet The polling set.
1733 * @param fPollEvt The event mask returned by RTPollNoResume.
1734 * @param idPollHnd The handle ID.
1735 * @param phStdInW The standard input pipe.
1736 * @param pStdInBuf The standard input buffer.
1737 */
1738static int txsDoExecHlpHandleTransportEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, uint32_t idPollHnd,
1739 PRTPIPE phStdInW, PTXSEXECSTDINBUF pStdInBuf)
1740{
1741 /* ASSUMES the transport layer will detect or clear any error condition. */
1742 NOREF(fPollEvt); NOREF(idPollHnd);
1743 Log(("txsDoExecHlpHandleTransportEvent\n"));
1744 /** @todo Use a callback for this case? */
1745
1746 /*
1747 * Read client command packet and process it.
1748 */
1749 /** @todo Sometimes this hangs on windows because there isn't any data pending.
1750 * We probably get woken up at the wrong time or in the wrong way, i.e. RTPoll()
1751 * is busted for sockets.
1752 *
1753 * Temporary workaround: Poll for input before trying to read it. */
1754 if (!g_pTransport->pfnPollIn())
1755 {
1756 Log(("Bad transport event\n"));
1757 RTThreadYield();
1758 return VINF_SUCCESS;
1759 }
1760 PTXSPKTHDR pPktHdr;
1761 int rc = txsRecvPkt(&pPktHdr, false /*fAutoRetryOnFailure*/);
1762 if (RT_FAILURE(rc))
1763 return rc;
1764 Log(("Bad transport event\n"));
1765
1766 /*
1767 * The most common thing here would be a STDIN request with data
1768 * for the child process.
1769 */
1770 if (txsIsSameOpcode(pPktHdr, "STDIN"))
1771 {
1772 if ( !pStdInBuf->fBitBucket
1773 && pPktHdr->cb >= sizeof(TXSPKTHDR) + sizeof(uint32_t))
1774 {
1775 uint32_t uCrc32 = *(uint32_t *)(pPktHdr + 1);
1776 const char *pch = (const char *)(pPktHdr + 1) + sizeof(uint32_t);
1777 size_t cb = pPktHdr->cb - sizeof(TXSPKTHDR) - sizeof(uint32_t);
1778
1779 /* Check the CRC */
1780 pStdInBuf->uCrc32 = RTCrc32Process(pStdInBuf->uCrc32, pch, cb);
1781 if (RTCrc32Finish(pStdInBuf->uCrc32) == uCrc32)
1782 {
1783
1784 /* Rewind the buffer if it's empty. */
1785 size_t cbInBuf = pStdInBuf->cb - pStdInBuf->off;
1786 bool const fAddToSet = cbInBuf == 0;
1787 if (fAddToSet)
1788 pStdInBuf->cb = pStdInBuf->off = 0;
1789
1790 /* Try and see if we can simply append the data. */
1791 if (cb + pStdInBuf->cb <= pStdInBuf->cbAllocated)
1792 {
1793 memcpy(&pStdInBuf->pch[pStdInBuf->cb], pch, cb);
1794 pStdInBuf->cb += cb;
1795 rc = txsReplyAck(pPktHdr);
1796 }
1797 else
1798 {
1799 /* Try write a bit or two before we move+realloc the buffer. */
1800 if (cbInBuf > 0)
1801 txsDoExecHlpWriteStdIn(pStdInBuf, *phStdInW);
1802
1803 /* Move any buffered data to the front. */
1804 cbInBuf = pStdInBuf->cb - pStdInBuf->off;
1805 if (cbInBuf == 0)
1806 pStdInBuf->cb = pStdInBuf->off = 0;
1807 else
1808 {
1809 memmove(pStdInBuf->pch, &pStdInBuf->pch[pStdInBuf->off], cbInBuf);
1810 pStdInBuf->cb = cbInBuf;
1811 pStdInBuf->off = 0;
1812 }
1813
1814 /* Do we need to grow the buffer? */
1815 if (cb + pStdInBuf->cb > pStdInBuf->cbAllocated)
1816 {
1817 size_t cbAlloc = pStdInBuf->cb + cb;
1818 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
1819 void *pvNew = RTMemRealloc(pStdInBuf->pch, cbAlloc);
1820 if (pvNew)
1821 {
1822 pStdInBuf->pch = (char *)pvNew;
1823 pStdInBuf->cbAllocated = cbAlloc;
1824 }
1825 }
1826
1827 /* Finally, copy the data. */
1828 if (cb + pStdInBuf->cb <= pStdInBuf->cbAllocated)
1829 {
1830 memcpy(&pStdInBuf->pch[pStdInBuf->cb], pch, cb);
1831 pStdInBuf->cb += cb;
1832 rc = txsReplyAck(pPktHdr);
1833 }
1834 else
1835 rc = txsReplySimple(pPktHdr, "STDINMEM");
1836 }
1837
1838 /*
1839 * Flush the buffered data and add/remove the standard input
1840 * handle from the set.
1841 */
1842 txsDoExecHlpWriteStdIn(pStdInBuf, *phStdInW);
1843 if (fAddToSet && pStdInBuf->off < pStdInBuf->cb)
1844 {
1845 int rc2 = RTPollSetAddPipe(hPollSet, *phStdInW, RTPOLL_EVT_WRITE, TXSEXECHNDID_STDIN_WRITABLE);
1846 AssertRC(rc2);
1847 }
1848 else if (!fAddToSet && pStdInBuf->off >= pStdInBuf->cb)
1849 {
1850 int rc2 = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN_WRITABLE);
1851 AssertRC(rc2);
1852 }
1853 }
1854 else
1855 rc = txsReplyFailure(pPktHdr, "STDINCRC", "Invalid CRC checksum expected %#x got %#x",
1856 pStdInBuf->uCrc32, uCrc32);
1857 }
1858 else if (pPktHdr->cb < sizeof(TXSPKTHDR) + sizeof(uint32_t))
1859 rc = txsReplySimple(pPktHdr, "STDINBAD");
1860 else
1861 rc = txsReplySimple(pPktHdr, "STDINIGN");
1862 }
1863 /*
1864 * Marks the end of the stream for stdin.
1865 */
1866 else if (txsIsSameOpcode(pPktHdr, "STDINEOS"))
1867 {
1868 if (RT_LIKELY(pPktHdr->cb == sizeof(TXSPKTHDR)))
1869 {
1870 /* Close the pipe. */
1871 txsDoExecHlpHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
1872 rc = txsReplyAck(pPktHdr);
1873 }
1874 else
1875 rc = txsReplySimple(pPktHdr, "STDINBAD");
1876 }
1877 /*
1878 * The only other two requests are connection oriented and we return a error
1879 * code so that we unwind the whole EXEC shebang and start afresh.
1880 */
1881 else if (txsIsSameOpcode(pPktHdr, "BYE"))
1882 {
1883 rc = txsDoBye(pPktHdr);
1884 if (RT_SUCCESS(rc))
1885 rc = VERR_NET_NOT_CONNECTED;
1886 }
1887 else if (txsIsSameOpcode(pPktHdr, "HOWDY"))
1888 {
1889 rc = txsDoHowdy(pPktHdr);
1890 if (RT_SUCCESS(rc))
1891 rc = VERR_NET_NOT_CONNECTED;
1892 }
1893 else if (txsIsSameOpcode(pPktHdr, "ABORT"))
1894 {
1895 rc = txsReplyAck(pPktHdr);
1896 if (RT_SUCCESS(rc))
1897 rc = VINF_EOF; /* this is but ugly! */
1898 }
1899 else
1900 rc = txsReplyFailure(pPktHdr, "UNKNOWN ", "Opcode '%.8s' is not known or not recognized during EXEC", pPktHdr->achOpcode);
1901
1902 RTMemFree(pPktHdr);
1903 return rc;
1904}
1905
1906/**
1907 * Handles the output and input of the process, waits for it finish up.
1908 *
1909 * @returns IPRT status code from reply send.
1910 * @param pTxsExec The TXSEXEC instance.
1911 */
1912static int txsDoExecHlp2(PTXSEXEC pTxsExec)
1913{
1914 int rc; /* client send. */
1915 int rc2;
1916 TXSEXECSTDINBUF StdInBuf = { 0, 0, NULL, 0, pTxsExec->hStdInW == NIL_RTPIPE, RTCrc32Start() };
1917 uint32_t uStdOutCrc32 = RTCrc32Start();
1918 uint32_t uStdErrCrc32 = uStdOutCrc32;
1919 uint32_t uTestPipeCrc32 = uStdOutCrc32;
1920 uint64_t const MsStart = RTTimeMilliTS();
1921 bool fProcessTimedOut = false;
1922 uint64_t MsProcessKilled = UINT64_MAX;
1923 RTMSINTERVAL const cMsPollBase = g_pTransport->pfnPollSetAdd || pTxsExec->hStdInW == NIL_RTPIPE
1924 ? 5000 : 100;
1925 RTMSINTERVAL cMsPollCur = 0;
1926
1927 /*
1928 * Before entering the loop, tell the client that we've started the guest
1929 * and that it's now OK to send input to the process. (This is not the
1930 * final ACK, so the packet header is NULL ... kind of bogus.)
1931 */
1932 rc = txsReplyAck(NULL);
1933
1934 /*
1935 * Process input, output, the test pipe and client requests.
1936 */
1937 while ( RT_SUCCESS(rc)
1938 && RT_UNLIKELY(!g_fTerminate))
1939 {
1940 /*
1941 * Wait/Process all pending events.
1942 */
1943 uint32_t idPollHnd;
1944 uint32_t fPollEvt;
1945 Log3(("Calling RTPollNoResume(,%u,)...\n", cMsPollCur));
1946 rc2 = RTPollNoResume(pTxsExec->hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
1947 Log3(("RTPollNoResume -> fPollEvt=%#x idPollHnd=%u\n", fPollEvt, idPollHnd));
1948 if (g_fTerminate)
1949 continue;
1950 cMsPollCur = 0; /* no rest until we've checked everything. */
1951
1952 if (RT_SUCCESS(rc2))
1953 {
1954 switch (idPollHnd)
1955 {
1956 case TXSEXECHNDID_STDOUT:
1957 rc = txsDoExecHlpHandleOutputEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdOutR, &uStdOutCrc32,
1958 TXSEXECHNDID_STDOUT, "STDOUT ");
1959 break;
1960
1961 case TXSEXECHNDID_STDERR:
1962 rc = txsDoExecHlpHandleOutputEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdErrR, &uStdErrCrc32,
1963 TXSEXECHNDID_STDERR, "STDERR ");
1964 break;
1965
1966 case TXSEXECHNDID_TESTPIPE:
1967 rc = txsDoExecHlpHandleOutputEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hTestPipeR, &uTestPipeCrc32,
1968 TXSEXECHNDID_TESTPIPE, "TESTPIPE");
1969 break;
1970
1971 case TXSEXECHNDID_STDIN:
1972 txsDoExecHlpHandleStdInErrorEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdInW, &StdInBuf);
1973 break;
1974
1975 case TXSEXECHNDID_STDIN_WRITABLE:
1976 txsDoExecHlpHandleStdInWritableEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdInW, &StdInBuf);
1977 break;
1978
1979 case TXSEXECHNDID_THREAD:
1980 rc2 = RTPollSetRemove(pTxsExec->hPollSet, TXSEXECHNDID_THREAD); AssertRC(rc2);
1981 break;
1982
1983 default:
1984 rc = txsDoExecHlpHandleTransportEvent(pTxsExec->hPollSet, fPollEvt, idPollHnd, &pTxsExec->hStdInW,
1985 &StdInBuf);
1986 break;
1987 }
1988 if (RT_FAILURE(rc) || rc == VINF_EOF)
1989 break; /* abort command, or client dead or something */
1990 continue;
1991 }
1992
1993 /*
1994 * Check for incoming data.
1995 */
1996 if (g_pTransport->pfnPollIn())
1997 {
1998 rc = txsDoExecHlpHandleTransportEvent(pTxsExec->hPollSet, 0, UINT32_MAX, &pTxsExec->hStdInW, &StdInBuf);
1999 if (RT_FAILURE(rc) || rc == VINF_EOF)
2000 break; /* abort command, or client dead or something */
2001 continue;
2002 }
2003
2004 /*
2005 * If the process has terminated, we're should head out.
2006 */
2007 if (!ASMAtomicReadBool(&pTxsExec->fProcessAlive))
2008 break;
2009
2010 /*
2011 * Check for timed out, killing the process.
2012 */
2013 uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
2014 if (pTxsExec->cMsTimeout != RT_INDEFINITE_WAIT)
2015 {
2016 uint64_t u64Now = RTTimeMilliTS();
2017 uint64_t cMsElapsed = u64Now - MsStart;
2018 if (cMsElapsed >= pTxsExec->cMsTimeout)
2019 {
2020 fProcessTimedOut = true;
2021 if ( MsProcessKilled == UINT64_MAX
2022 || u64Now - MsProcessKilled > 1000)
2023 {
2024 if (u64Now - MsProcessKilled > 20*60*1000)
2025 break; /* give up after 20 mins */
2026 RTCritSectEnter(&pTxsExec->CritSect);
2027 if (pTxsExec->fProcessAlive)
2028 RTProcTerminate(pTxsExec->hProcess);
2029 RTCritSectLeave(&pTxsExec->CritSect);
2030 MsProcessKilled = u64Now;
2031 continue;
2032 }
2033 cMilliesLeft = 10000;
2034 }
2035 else
2036 cMilliesLeft = pTxsExec->cMsTimeout - (uint32_t)cMsElapsed;
2037 }
2038
2039 /* Reset the polling interval since we've done all pending work. */
2040 cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
2041 }
2042
2043 /*
2044 * At this point we should hopefully only have to wait 0 ms on the thread
2045 * to release the handle... But if for instance the process refuses to die,
2046 * we'll have to try kill it again. Bothersome.
2047 */
2048 for (size_t i = 0; i < 22; i++)
2049 {
2050 rc2 = RTThreadWait(pTxsExec->hThreadWaiter, 500, NULL);
2051 if (RT_SUCCESS(rc))
2052 {
2053 pTxsExec->hThreadWaiter = NIL_RTTHREAD;
2054 Assert(!pTxsExec->fProcessAlive);
2055 break;
2056 }
2057 if (i == 0 || i == 10 || i == 15 || i == 18 || i > 20)
2058 {
2059 RTCritSectEnter(&pTxsExec->CritSect);
2060 if (pTxsExec->fProcessAlive)
2061 RTProcTerminate(pTxsExec->hProcess);
2062 RTCritSectLeave(&pTxsExec->CritSect);
2063 }
2064 }
2065
2066 /*
2067 * If we don't have a client problem (RT_FAILURE(rc) we'll reply to the
2068 * clients exec packet now.
2069 */
2070 if (RT_SUCCESS(rc))
2071 rc = txsExecSendExitStatus(pTxsExec, pTxsExec->fProcessAlive, fProcessTimedOut, MsProcessKilled);
2072
2073 RTMemFree(StdInBuf.pch);
2074 return rc;
2075}
2076
2077/**
2078 * Creates a poll set for the pipes and let the transport layer add stuff to it
2079 * as well.
2080 *
2081 * @returns IPRT status code, reply to client made on error.
2082 * @param pTxsExec The TXSEXEC instance.
2083 */
2084static int txsExecSetupPollSet(PTXSEXEC pTxsExec)
2085{
2086 int rc = RTPollSetCreate(&pTxsExec->hPollSet);
2087 if (RT_FAILURE(rc))
2088 return txsExecReplyRC(pTxsExec, rc, "RTPollSetCreate");
2089
2090 rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hStdInW, RTPOLL_EVT_ERROR, TXSEXECHNDID_STDIN);
2091 if (RT_FAILURE(rc))
2092 return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/stdin");
2093
2094 rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
2095 TXSEXECHNDID_STDOUT);
2096 if (RT_FAILURE(rc))
2097 return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/stdout");
2098
2099 rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
2100 TXSEXECHNDID_STDERR);
2101 if (RT_FAILURE(rc))
2102 return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/stderr");
2103
2104 rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hTestPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
2105 TXSEXECHNDID_TESTPIPE);
2106 if (RT_FAILURE(rc))
2107 return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/test");
2108
2109 rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hWakeUpPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
2110 TXSEXECHNDID_THREAD);
2111 if (RT_FAILURE(rc))
2112 return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/wakeup");
2113
2114 if (g_pTransport->pfnPollSetAdd)
2115 {
2116 rc = g_pTransport->pfnPollSetAdd(pTxsExec->hPollSet, TXSEXECHNDID_TRANSPORT);
2117 if (RT_FAILURE(rc))
2118 return txsExecReplyRC(pTxsExec, rc, "%s->pfnPollSetAdd/stdin", g_pTransport->szName);
2119 }
2120
2121 return VINF_SUCCESS;
2122}
2123
2124/**
2125 * Thread that calls RTProcWait and signals the main thread when it returns.
2126 *
2127 * The thread is created before the process is started and is waiting for a user
2128 * signal from the main thread before it calls RTProcWait.
2129 *
2130 * @returns VINF_SUCCESS (ignored).
2131 * @param hThreadSelf The thread handle.
2132 * @param pvUser The TXEEXEC structure.
2133 */
2134static DECLCALLBACK(int) txsExecWaitThreadProc(RTTHREAD hThreadSelf, void *pvUser)
2135{
2136 PTXSEXEC pTxsExec = (PTXSEXEC)pvUser;
2137
2138 /* Wait for the go ahead... */
2139 int rc = RTThreadUserWait(hThreadSelf, RT_INDEFINITE_WAIT); AssertRC(rc);
2140
2141 RTCritSectEnter(&pTxsExec->CritSect);
2142 for (;;)
2143 {
2144 RTCritSectLeave(&pTxsExec->CritSect);
2145 rc = RTProcWaitNoResume(pTxsExec->hProcess, RTPROCWAIT_FLAGS_BLOCK, &pTxsExec->ProcessStatus);
2146 RTCritSectEnter(&pTxsExec->CritSect);
2147
2148 /* If the pipe is NIL, the destructor wants us to get lost ASAP. */
2149 if (pTxsExec->hWakeUpPipeW == NIL_RTPIPE)
2150 break;
2151
2152 if (RT_FAILURE(rc))
2153 {
2154 rc = RTProcWait(pTxsExec->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &pTxsExec->ProcessStatus);
2155 if (rc == VERR_PROCESS_RUNNING)
2156 continue;
2157
2158 if (RT_FAILURE(rc))
2159 {
2160 AssertRC(rc);
2161 pTxsExec->ProcessStatus.iStatus = rc;
2162 pTxsExec->ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
2163 }
2164 }
2165
2166 /* The process finished, signal the main thread over the pipe. */
2167 ASMAtomicWriteBool(&pTxsExec->fProcessAlive, false);
2168 size_t cbIgnored;
2169 RTPipeWrite(pTxsExec->hWakeUpPipeW, "done", 4, &cbIgnored);
2170 RTPipeClose(pTxsExec->hWakeUpPipeW);
2171 pTxsExec->hWakeUpPipeW = NIL_RTPIPE;
2172 break;
2173 }
2174 RTCritSectLeave(&pTxsExec->CritSect);
2175
2176 return VINF_SUCCESS;
2177}
2178
2179/**
2180 * Sets up the thread that waits for the process to complete.
2181 *
2182 * @returns IPRT status code, reply to client made on error.
2183 * @param pTxsExec The TXSEXEC instance.
2184 */
2185static int txsExecSetupThread(PTXSEXEC pTxsExec)
2186{
2187 int rc = RTPipeCreate(&pTxsExec->hWakeUpPipeR, &pTxsExec->hWakeUpPipeW, 0 /*fFlags*/);
2188 if (RT_FAILURE(rc))
2189 {
2190 pTxsExec->hWakeUpPipeR = pTxsExec->hWakeUpPipeW = NIL_RTPIPE;
2191 return txsExecReplyRC(pTxsExec, rc, "RTPipeCreate/wait");
2192 }
2193
2194 rc = RTThreadCreate(&pTxsExec->hThreadWaiter, txsExecWaitThreadProc,
2195 pTxsExec, 0 /*cbStack */, RTTHREADTYPE_DEFAULT,
2196 RTTHREADFLAGS_WAITABLE, "TxsProcW");
2197 if (RT_FAILURE(rc))
2198 {
2199 pTxsExec->hThreadWaiter = NIL_RTTHREAD;
2200 return txsExecReplyRC(pTxsExec, rc, "RTThreadCreate");
2201 }
2202
2203 return VINF_SUCCESS;
2204}
2205
2206/**
2207 * Sets up the test pipe.
2208 *
2209 * @returns IPRT status code, reply to client made on error.
2210 * @param pTxsExec The TXSEXEC instance.
2211 * @param pszTestPipe How to set up the test pipe.
2212 */
2213static int txsExecSetupTestPipe(PTXSEXEC pTxsExec, const char *pszTestPipe)
2214{
2215 if (strcmp(pszTestPipe, "|"))
2216 return VINF_SUCCESS;
2217
2218 int rc = RTPipeCreate(&pTxsExec->hTestPipeR, &pTxsExec->hTestPipeW, RTPIPE_C_INHERIT_WRITE);
2219 if (RT_FAILURE(rc))
2220 {
2221 pTxsExec->hTestPipeR = pTxsExec->hTestPipeW = NIL_RTPIPE;
2222 return txsExecReplyRC(pTxsExec, rc, "RTPipeCreate/test/%s", pszTestPipe);
2223 }
2224
2225 char szVal[64];
2226 RTStrPrintf(szVal, sizeof(szVal), "%#llx", (uint64_t)RTPipeToNative(pTxsExec->hTestPipeW));
2227 rc = RTEnvSetEx(pTxsExec->hEnv, "IPRT_TEST_PIPE", szVal);
2228 if (RT_FAILURE(rc))
2229 return txsExecReplyRC(pTxsExec, rc, "RTEnvSetEx/test/%s", pszTestPipe);
2230
2231 return VINF_SUCCESS;
2232}
2233
2234/**
2235 * Sets up the redirection / pipe / nothing for one of the standard handles.
2236 *
2237 * @returns IPRT status code, reply to client made on error.
2238 * @param pTxsExec The TXSEXEC instance.
2239 * @param pszHowTo How to set up this standard handle.
2240 * @param pszStdWhat For what to setup redirection (stdin/stdout/stderr).
2241 * @param fd Which standard handle it is (0 == stdin, 1 ==
2242 * stdout, 2 == stderr).
2243 * @param ph The generic handle that @a pph may be set
2244 * pointing to. Always set.
2245 * @param pph Pointer to the RTProcCreateExec argument.
2246 * Always set.
2247 * @param phPipe Where to return the end of the pipe that we
2248 * should service. Always set.
2249 */
2250static int txsExecSetupRedir(PTXSEXEC pTxsExec, const char *pszHowTo, const char *pszStdWhat, int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
2251{
2252 ph->enmType = RTHANDLETYPE_PIPE;
2253 ph->u.hPipe = NIL_RTPIPE;
2254 *pph = NULL;
2255 *phPipe = NIL_RTPIPE;
2256
2257 int rc;
2258 if (!strcmp(pszHowTo, "|"))
2259 {
2260 /*
2261 * Setup a pipe for forwarding to/from the client.
2262 */
2263 if (fd == 0)
2264 rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
2265 else
2266 rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
2267 if (RT_FAILURE(rc))
2268 return txsExecReplyRC(pTxsExec, rc, "RTPipeCreate/%s/%s", pszStdWhat, pszHowTo);
2269 ph->enmType = RTHANDLETYPE_PIPE;
2270 *pph = ph;
2271 }
2272 else if (!strcmp(pszHowTo, "/dev/null"))
2273 {
2274 /*
2275 * Redirect to/from /dev/null.
2276 */
2277 RTFILE hFile;
2278 rc = RTFileOpenBitBucket(&hFile, fd == 0 ? RTFILE_O_READ : RTFILE_O_WRITE);
2279 if (RT_FAILURE(rc))
2280 return txsExecReplyRC(pTxsExec, rc, "RTFileOpenBitBucket/%s/%s", pszStdWhat, pszHowTo);
2281
2282 ph->enmType = RTHANDLETYPE_FILE;
2283 ph->u.hFile = hFile;
2284 *pph = ph;
2285 }
2286 else if (*pszHowTo)
2287 {
2288 /*
2289 * Redirect to/from file.
2290 */
2291 uint32_t fFlags;
2292 if (fd == 0)
2293 fFlags = RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN;
2294 else
2295 {
2296 if (pszHowTo[0] != '>' || pszHowTo[1] != '>')
2297 fFlags = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE;
2298 else
2299 {
2300 /* append */
2301 pszHowTo += 2;
2302 fFlags = RTFILE_O_WRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
2303 }
2304 }
2305
2306 RTFILE hFile;
2307 rc = RTFileOpen(&hFile, pszHowTo, fFlags);
2308 if (RT_FAILURE(rc))
2309 return txsExecReplyRC(pTxsExec, rc, "RTFileOpen/%s/%s", pszStdWhat, pszHowTo);
2310
2311 ph->enmType = RTHANDLETYPE_FILE;
2312 ph->u.hFile = hFile;
2313 *pph = ph;
2314 }
2315 else
2316 /* same as parent (us) */
2317 rc = VINF_SUCCESS;
2318 return rc;
2319}
2320
2321/**
2322 * Create the environment.
2323 *
2324 * @returns IPRT status code, reply to client made on error.
2325 * @param pTxsExec The TXSEXEC instance.
2326 * @param cEnvVars The number of environment variables.
2327 * @param papszEnv The environment variables (var=value).
2328 */
2329static int txsExecSetupEnv(PTXSEXEC pTxsExec, uint32_t cEnvVars, const char * const *papszEnv)
2330{
2331 /*
2332 * Create the environment.
2333 */
2334 int rc = RTEnvClone(&pTxsExec->hEnv, RTENV_DEFAULT);
2335 if (RT_FAILURE(rc))
2336 return txsExecReplyRC(pTxsExec, rc, "RTEnvClone");
2337
2338 for (size_t i = 0; i < cEnvVars; i++)
2339 {
2340 rc = RTEnvPutEx(pTxsExec->hEnv, papszEnv[i]);
2341 if (RT_FAILURE(rc))
2342 return txsExecReplyRC(pTxsExec, rc, "RTEnvPutEx(,'%s')", papszEnv[i]);
2343 }
2344 return VINF_SUCCESS;
2345}
2346
2347/**
2348 * Deletes the TXSEXEC structure and frees the memory backing it.
2349 *
2350 * @param pTxsExec The structure to destroy.
2351 */
2352static void txsExecDestroy(PTXSEXEC pTxsExec)
2353{
2354 int rc2;
2355
2356 rc2 = RTEnvDestroy(pTxsExec->hEnv); AssertRC(rc2);
2357 pTxsExec->hEnv = NIL_RTENV;
2358 rc2 = RTPipeClose(pTxsExec->hTestPipeW); AssertRC(rc2);
2359 pTxsExec->hTestPipeW = NIL_RTPIPE;
2360 rc2 = RTHandleClose(pTxsExec->StdErr.phChild); AssertRC(rc2);
2361 pTxsExec->StdErr.phChild = NULL;
2362 rc2 = RTHandleClose(pTxsExec->StdOut.phChild); AssertRC(rc2);
2363 pTxsExec->StdOut.phChild = NULL;
2364 rc2 = RTHandleClose(pTxsExec->StdIn.phChild); AssertRC(rc2);
2365 pTxsExec->StdIn.phChild = NULL;
2366
2367 rc2 = RTPipeClose(pTxsExec->hTestPipeR); AssertRC(rc2);
2368 pTxsExec->hTestPipeR = NIL_RTPIPE;
2369 rc2 = RTPipeClose(pTxsExec->hStdErrR); AssertRC(rc2);
2370 pTxsExec->hStdErrR = NIL_RTPIPE;
2371 rc2 = RTPipeClose(pTxsExec->hStdOutR); AssertRC(rc2);
2372 pTxsExec->hStdOutR = NIL_RTPIPE;
2373 rc2 = RTPipeClose(pTxsExec->hStdInW); AssertRC(rc2);
2374 pTxsExec->hStdInW = NIL_RTPIPE;
2375
2376 RTPollSetDestroy(pTxsExec->hPollSet);
2377 pTxsExec->hPollSet = NIL_RTPOLLSET;
2378
2379 /*
2380 * If the process is still running we're in a bit of a fix... Try kill it,
2381 * although that's potentially racing process termination and reusage of
2382 * the pid.
2383 */
2384 RTCritSectEnter(&pTxsExec->CritSect);
2385
2386 RTPipeClose(pTxsExec->hWakeUpPipeW);
2387 pTxsExec->hWakeUpPipeW = NIL_RTPIPE;
2388 RTPipeClose(pTxsExec->hWakeUpPipeR);
2389 pTxsExec->hWakeUpPipeR = NIL_RTPIPE;
2390
2391 if ( pTxsExec->hProcess != NIL_RTPROCESS
2392 && pTxsExec->fProcessAlive)
2393 RTProcTerminate(pTxsExec->hProcess);
2394
2395 RTCritSectLeave(&pTxsExec->CritSect);
2396
2397 int rcThread = VINF_SUCCESS;
2398 if (pTxsExec->hThreadWaiter != NIL_RTTHREAD)
2399 rcThread = RTThreadWait(pTxsExec->hThreadWaiter, 5000, NULL);
2400 if (RT_SUCCESS(rcThread))
2401 {
2402 pTxsExec->hThreadWaiter = NIL_RTTHREAD;
2403 RTCritSectDelete(&pTxsExec->CritSect);
2404 RTMemFree(pTxsExec);
2405 }
2406 /* else: leak it or RTThreadWait may cause heap corruption later. */
2407}
2408
2409/**
2410 * Initializes the TXSEXEC structure.
2411 *
2412 * @returns VINF_SUCCESS and non-NULL *ppTxsExec on success, reply send status
2413 * and *ppTxsExec set to NULL on failure.
2414 * @param pPktHdr The exec packet.
2415 * @param cMsTimeout The time parameter.
2416 * @param ppTxsExec Where to return the structure.
2417 */
2418static int txsExecCreate(PCTXSPKTHDR pPktHdr, RTMSINTERVAL cMsTimeout, PTXSEXEC *ppTxsExec)
2419{
2420 *ppTxsExec = NULL;
2421
2422 /*
2423 * Allocate the basic resources.
2424 */
2425 PTXSEXEC pTxsExec = (PTXSEXEC)RTMemAlloc(sizeof(*pTxsExec));
2426 if (!pTxsExec)
2427 return txsReplyRC(pPktHdr, VERR_NO_MEMORY, "RTMemAlloc(%zu)", sizeof(*pTxsExec));
2428 int rc = RTCritSectInit(&pTxsExec->CritSect);
2429 if (RT_FAILURE(rc))
2430 {
2431 RTMemFree(pTxsExec);
2432 return txsReplyRC(pPktHdr, rc, "RTCritSectInit");
2433 }
2434
2435 /*
2436 * Initialize the member to NIL values.
2437 */
2438 pTxsExec->pPktHdr = pPktHdr;
2439 pTxsExec->cMsTimeout = cMsTimeout;
2440 pTxsExec->rcReplySend = VINF_SUCCESS;
2441
2442 pTxsExec->hPollSet = NIL_RTPOLLSET;
2443 pTxsExec->hStdInW = NIL_RTPIPE;
2444 pTxsExec->hStdOutR = NIL_RTPIPE;
2445 pTxsExec->hStdErrR = NIL_RTPIPE;
2446 pTxsExec->hTestPipeR = NIL_RTPIPE;
2447 pTxsExec->hWakeUpPipeR = NIL_RTPIPE;
2448 pTxsExec->hThreadWaiter = NIL_RTTHREAD;
2449
2450 pTxsExec->StdIn.phChild = NULL;
2451 pTxsExec->StdOut.phChild = NULL;
2452 pTxsExec->StdErr.phChild = NULL;
2453 pTxsExec->hTestPipeW = NIL_RTPIPE;
2454 pTxsExec->hEnv = NIL_RTENV;
2455
2456 pTxsExec->hProcess = NIL_RTPROCESS;
2457 pTxsExec->ProcessStatus.iStatus = 254;
2458 pTxsExec->ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
2459 pTxsExec->fProcessAlive = false;
2460 pTxsExec->hWakeUpPipeW = NIL_RTPIPE;
2461
2462 *ppTxsExec = pTxsExec;
2463 return VINF_SUCCESS;
2464}
2465
2466/**
2467 * txsDoExec helper that takes over when txsDoExec has expanded the packet.
2468 *
2469 * @returns IPRT status code from send.
2470 * @param pPktHdr The exec packet.
2471 * @param fFlags Flags, reserved for future use.
2472 * @param pszExecName The executable name.
2473 * @param cArgs The argument count.
2474 * @param papszArgs The arguments.
2475 * @param cEnvVars The environment variable count.
2476 * @param papszEnv The environment variables.
2477 * @param pszStdIn How to deal with standard in.
2478 * @param pszStdOut How to deal with standard out.
2479 * @param pszStdErr How to deal with standard err.
2480 * @param pszTestPipe How to deal with the test pipe.
2481 * @param pszUsername The user to run the program as.
2482 * @param cMillies The process time limit in milliseconds.
2483 */
2484static int txsDoExecHlp(PCTXSPKTHDR pPktHdr, uint32_t fFlags, const char *pszExecName,
2485 uint32_t cArgs, const char * const *papszArgs,
2486 uint32_t cEnvVars, const char * const *papszEnv,
2487 const char *pszStdIn, const char *pszStdOut, const char *pszStdErr, const char *pszTestPipe,
2488 const char *pszUsername, RTMSINTERVAL cMillies)
2489{
2490 int rc2;
2491 RT_NOREF_PV(fFlags);
2492
2493 /*
2494 * Input validation, filter out things we don't yet support..
2495 */
2496 Assert(!fFlags);
2497 if (!*pszExecName)
2498 return txsReplyFailure(pPktHdr, "STR ZERO", "Executable name is empty");
2499 if (!*pszStdIn)
2500 return txsReplyFailure(pPktHdr, "STR ZERO", "The stdin howto is empty");
2501 if (!*pszStdOut)
2502 return txsReplyFailure(pPktHdr, "STR ZERO", "The stdout howto is empty");
2503 if (!*pszStdErr)
2504 return txsReplyFailure(pPktHdr, "STR ZERO", "The stderr howto is empty");
2505 if (!*pszTestPipe)
2506 return txsReplyFailure(pPktHdr, "STR ZERO", "The testpipe howto is empty");
2507 if (strcmp(pszTestPipe, "|") && strcmp(pszTestPipe, "/dev/null"))
2508 return txsReplyFailure(pPktHdr, "BAD TSTP", "Only \"|\" and \"/dev/null\" are allowed as testpipe howtos ('%s')",
2509 pszTestPipe);
2510 if (*pszUsername)
2511 return txsReplyFailure(pPktHdr, "NOT IMPL", "Executing as a specific user is not implemented ('%s')", pszUsername);
2512
2513 /*
2514 * Prepare for process launch.
2515 */
2516 PTXSEXEC pTxsExec;
2517 int rc = txsExecCreate(pPktHdr, cMillies, &pTxsExec);
2518 if (pTxsExec == NULL)
2519 return rc;
2520 rc = txsExecSetupEnv(pTxsExec, cEnvVars, papszEnv);
2521 if (RT_SUCCESS(rc))
2522 rc = txsExecSetupRedir(pTxsExec, pszStdIn, "StdIn", 0, &pTxsExec->StdIn.hChild, &pTxsExec->StdIn.phChild, &pTxsExec->hStdInW);
2523 if (RT_SUCCESS(rc))
2524 rc = txsExecSetupRedir(pTxsExec, pszStdOut, "StdOut", 1, &pTxsExec->StdOut.hChild, &pTxsExec->StdOut.phChild, &pTxsExec->hStdOutR);
2525 if (RT_SUCCESS(rc))
2526 rc = txsExecSetupRedir(pTxsExec, pszStdErr, "StdErr", 2, &pTxsExec->StdErr.hChild, &pTxsExec->StdErr.phChild, &pTxsExec->hStdErrR);
2527 if (RT_SUCCESS(rc))
2528 rc = txsExecSetupTestPipe(pTxsExec, pszTestPipe);
2529 if (RT_SUCCESS(rc))
2530 rc = txsExecSetupThread(pTxsExec);
2531 if (RT_SUCCESS(rc))
2532 rc = txsExecSetupPollSet(pTxsExec);
2533 if (RT_SUCCESS(rc))
2534 {
2535 /*
2536 * Create the process.
2537 */
2538 if (g_fDisplayOutput)
2539 {
2540 RTPrintf("txs: Executing \"%s\": ", pszExecName);
2541 for (uint32_t i = 0; i < cArgs; i++)
2542 RTPrintf(" \"%s\"", papszArgs[i]);
2543 RTPrintf("\n");
2544 }
2545 rc = RTProcCreateEx(pszExecName, papszArgs, pTxsExec->hEnv, 0 /*fFlags*/,
2546 pTxsExec->StdIn.phChild, pTxsExec->StdOut.phChild, pTxsExec->StdErr.phChild,
2547 *pszUsername ? pszUsername : NULL, NULL,
2548 &pTxsExec->hProcess);
2549 if (RT_SUCCESS(rc))
2550 {
2551 ASMAtomicWriteBool(&pTxsExec->fProcessAlive, true);
2552 rc2 = RTThreadUserSignal(pTxsExec->hThreadWaiter); AssertRC(rc2);
2553
2554 /*
2555 * Close the child ends of any pipes and redirected files.
2556 */
2557 rc2 = RTHandleClose(pTxsExec->StdIn.phChild); AssertRC(rc2);
2558 pTxsExec->StdIn.phChild = NULL;
2559 rc2 = RTHandleClose(pTxsExec->StdOut.phChild); AssertRC(rc2);
2560 pTxsExec->StdOut.phChild = NULL;
2561 rc2 = RTHandleClose(pTxsExec->StdErr.phChild); AssertRC(rc2);
2562 pTxsExec->StdErr.phChild = NULL;
2563 rc2 = RTPipeClose(pTxsExec->hTestPipeW); AssertRC(rc2);
2564 pTxsExec->hTestPipeW = NIL_RTPIPE;
2565
2566 /*
2567 * Let another worker function funnel output and input to the
2568 * client as well as the process exit code.
2569 */
2570 rc = txsDoExecHlp2(pTxsExec);
2571 }
2572 else
2573 rc = txsReplyFailure(pPktHdr, "FAILED ", "Executing process \"%s\" failed with %Rrc",
2574 pszExecName, rc);
2575 }
2576 else
2577 rc = pTxsExec->rcReplySend;
2578 txsExecDestroy(pTxsExec);
2579 return rc;
2580}
2581
2582/**
2583 * Execute a program.
2584 *
2585 * @returns IPRT status code from send.
2586 * @param pPktHdr The exec packet.
2587 */
2588static int txsDoExec(PCTXSPKTHDR pPktHdr)
2589{
2590 /*
2591 * This packet has a lot of parameters, most of which are zero terminated
2592 * strings. The strings used in items 7 thru 10 are either file names,
2593 * "/dev/null" or a pipe char (|).
2594 *
2595 * Packet content:
2596 * 1. Flags reserved for future use (32-bit unsigned).
2597 * 2. The executable name (string).
2598 * 3. The argument count given as a 32-bit unsigned integer.
2599 * 4. The arguments strings.
2600 * 5. The number of environment strings (32-bit unsigned).
2601 * 6. The environment strings (var=val) to apply the environment.
2602 * 7. What to do about standard in (string).
2603 * 8. What to do about standard out (string).
2604 * 9. What to do about standard err (string).
2605 * 10. What to do about the test pipe (string).
2606 * 11. The name of the user to run the program as (string). Empty string
2607 * means running it as the current user.
2608 * 12. Process time limit in milliseconds (32-bit unsigned). Max == no limit.
2609 */
2610 size_t const cbMin = sizeof(TXSPKTHDR)
2611 + sizeof(uint32_t) /* flags */ + 2
2612 + sizeof(uint32_t) /* argc */ + 2 /* argv */
2613 + sizeof(uint32_t) + 0 /* environ */
2614 + 4 * 1
2615 + sizeof(uint32_t) /* timeout */;
2616 if (pPktHdr->cb < cbMin)
2617 return txsReplyBadMinSize(pPktHdr, cbMin);
2618
2619 /* unpack the packet */
2620 char const *pchEnd = (char const *)pPktHdr + pPktHdr->cb;
2621 char const *pch = (char const *)(pPktHdr + 1); /* cursor */
2622
2623 /* 1. flags */
2624 uint32_t const fFlags = *(uint32_t const *)pch;
2625 pch += sizeof(uint32_t);
2626 if (fFlags != 0)
2627 return txsReplyFailure(pPktHdr, "BAD FLAG", "Invalid EXEC flags %#x, expected 0", fFlags);
2628
2629 /* 2. exec name */
2630 int rc;
2631 char *pszExecName = NULL;
2632 if (!txsIsStringValid(pPktHdr, "execname", pch, &pszExecName, &pch, &rc))
2633 return rc;
2634
2635 /* 3. argc */
2636 uint32_t const cArgs = (size_t)(pchEnd - pch) > sizeof(uint32_t) ? *(uint32_t const *)pch : 0xff;
2637 pch += sizeof(uint32_t);
2638 if (cArgs * 1 >= (size_t)(pchEnd - pch))
2639 rc = txsReplyFailure(pPktHdr, "BAD ARGC", "Bad or missing argument count (%#x)", cArgs);
2640 else if (cArgs > 128)
2641 rc = txsReplyFailure(pPktHdr, "BAD ARGC", "Too many arguments (%#x)", cArgs);
2642 else
2643 {
2644 char **papszArgs = (char **)RTMemTmpAllocZ(sizeof(char *) * (cArgs + 1));
2645 if (papszArgs)
2646 {
2647 /* 4. argv */
2648 bool fOk = true;
2649 for (size_t i = 0; i < cArgs && fOk; i++)
2650 {
2651 fOk = txsIsStringValid(pPktHdr, "argvN", pch, &papszArgs[i], &pch, &rc);
2652 if (!fOk)
2653 break;
2654 }
2655 if (fOk)
2656 {
2657 /* 5. cEnvVars */
2658 uint32_t const cEnvVars = (size_t)(pchEnd - pch) > sizeof(uint32_t) ? *(uint32_t const *)pch : 0xfff;
2659 pch += sizeof(uint32_t);
2660 if (cEnvVars * 1 >= (size_t)(pchEnd - pch))
2661 rc = txsReplyFailure(pPktHdr, "BAD ENVC", "Bad or missing environment variable count (%#x)", cEnvVars);
2662 else if (cEnvVars > 256)
2663 rc = txsReplyFailure(pPktHdr, "BAD ENVC", "Too many environment variables (%#x)", cEnvVars);
2664 else
2665 {
2666 char **papszEnv = (char **)RTMemTmpAllocZ(sizeof(char *) * (cEnvVars + 1));
2667 if (papszEnv)
2668 {
2669 /* 6. environ */
2670 for (size_t i = 0; i < cEnvVars && fOk; i++)
2671 {
2672 fOk = txsIsStringValid(pPktHdr, "envN", pch, &papszEnv[i], &pch, &rc);
2673 if (!fOk) /* Bail out on error. */
2674 break;
2675 }
2676 if (fOk)
2677 {
2678 /* 7. stdout */
2679 char *pszStdIn;
2680 if (txsIsStringValid(pPktHdr, "stdin", pch, &pszStdIn, &pch, &rc))
2681 {
2682 /* 8. stdout */
2683 char *pszStdOut;
2684 if (txsIsStringValid(pPktHdr, "stdout", pch, &pszStdOut, &pch, &rc))
2685 {
2686 /* 9. stderr */
2687 char *pszStdErr;
2688 if (txsIsStringValid(pPktHdr, "stderr", pch, &pszStdErr, &pch, &rc))
2689 {
2690 /* 10. testpipe */
2691 char *pszTestPipe;
2692 if (txsIsStringValid(pPktHdr, "testpipe", pch, &pszTestPipe, &pch, &rc))
2693 {
2694 /* 11. username */
2695 char *pszUsername;
2696 if (txsIsStringValid(pPktHdr, "username", pch, &pszUsername, &pch, &rc))
2697 {
2698 /** @todo No password value? */
2699
2700 /* 12. time limit */
2701 uint32_t const cMillies = (size_t)(pchEnd - pch) >= sizeof(uint32_t)
2702 ? *(uint32_t const *)pch
2703 : 0;
2704 if ((size_t)(pchEnd - pch) > sizeof(uint32_t))
2705 rc = txsReplyFailure(pPktHdr, "BAD END ", "Timeout argument not at end of packet (%#x)", (size_t)(pchEnd - pch));
2706 else if ((size_t)(pchEnd - pch) < sizeof(uint32_t))
2707 rc = txsReplyFailure(pPktHdr, "BAD NOTO", "No timeout argument");
2708 else if (cMillies < 1000)
2709 rc = txsReplyFailure(pPktHdr, "BAD TO ", "Timeout is less than a second (%#x)", cMillies);
2710 else
2711 {
2712 pch += sizeof(uint32_t);
2713
2714 /*
2715 * Time to employ a helper here before we go way beyond
2716 * the right margin...
2717 */
2718 rc = txsDoExecHlp(pPktHdr, fFlags, pszExecName,
2719 cArgs, papszArgs,
2720 cEnvVars, papszEnv,
2721 pszStdIn, pszStdOut, pszStdErr, pszTestPipe,
2722 pszUsername,
2723 cMillies == UINT32_MAX ? RT_INDEFINITE_WAIT : cMillies);
2724 }
2725 RTStrFree(pszUsername);
2726 }
2727 RTStrFree(pszTestPipe);
2728 }
2729 RTStrFree(pszStdErr);
2730 }
2731 RTStrFree(pszStdOut);
2732 }
2733 RTStrFree(pszStdIn);
2734 }
2735 }
2736 for (size_t i = 0; i < cEnvVars; i++)
2737 RTStrFree(papszEnv[i]);
2738 RTMemTmpFree(papszEnv);
2739 }
2740 else
2741 rc = txsReplyFailure(pPktHdr, "NO MEM ", "Failed to allocate %zu bytes environ", sizeof(char *) * (cEnvVars + 1));
2742 }
2743 }
2744 for (size_t i = 0; i < cArgs; i++)
2745 RTStrFree(papszArgs[i]);
2746 RTMemTmpFree(papszArgs);
2747 }
2748 else
2749 rc = txsReplyFailure(pPktHdr, "NO MEM ", "Failed to allocate %zu bytes for argv", sizeof(char *) * (cArgs + 1));
2750 }
2751 RTStrFree(pszExecName);
2752
2753 return rc;
2754}
2755
2756/**
2757 * The main loop.
2758 *
2759 * @returns exit code.
2760 */
2761static RTEXITCODE txsMainLoop(void)
2762{
2763 if (g_cVerbose > 0)
2764 RTMsgInfo("txsMainLoop: start...\n");
2765 RTEXITCODE enmExitCode = RTEXITCODE_SUCCESS;
2766 while (!g_fTerminate)
2767 {
2768 /*
2769 * Read client command packet and process it.
2770 */
2771 PTXSPKTHDR pPktHdr;
2772 int rc = txsRecvPkt(&pPktHdr, true /*fAutoRetryOnFailure*/);
2773 if (RT_FAILURE(rc))
2774 continue;
2775 if (g_cVerbose > 0)
2776 RTMsgInfo("txsMainLoop: CMD: %.8s...", pPktHdr->achOpcode);
2777
2778 /*
2779 * Do a string switch on the opcode bit.
2780 */
2781 /* Connection: */
2782 if ( txsIsSameOpcode(pPktHdr, "HOWDY "))
2783 rc = txsDoHowdy(pPktHdr);
2784 else if (txsIsSameOpcode(pPktHdr, "BYE "))
2785 rc = txsDoBye(pPktHdr);
2786 else if (txsIsSameOpcode(pPktHdr, "UUID "))
2787 rc = txsDoUuid(pPktHdr);
2788 /* Process: */
2789 else if (txsIsSameOpcode(pPktHdr, "EXEC "))
2790 rc = txsDoExec(pPktHdr);
2791 /* Admin: */
2792 else if (txsIsSameOpcode(pPktHdr, "REBOOT "))
2793 rc = txsDoReboot(pPktHdr);
2794 else if (txsIsSameOpcode(pPktHdr, "SHUTDOWN"))
2795 rc = txsDoShutdown(pPktHdr);
2796 /* CD/DVD control: */
2797 else if (txsIsSameOpcode(pPktHdr, "CD EJECT"))
2798 rc = txsDoCdEject(pPktHdr);
2799 /* File system: */
2800 else if (txsIsSameOpcode(pPktHdr, "CLEANUP "))
2801 rc = txsDoCleanup(pPktHdr);
2802 else if (txsIsSameOpcode(pPktHdr, "MKDIR "))
2803 rc = txsDoMkDir(pPktHdr);
2804 else if (txsIsSameOpcode(pPktHdr, "MKDRPATH"))
2805 rc = txsDoMkDrPath(pPktHdr);
2806 else if (txsIsSameOpcode(pPktHdr, "MKSYMLNK"))
2807 rc = txsDoMkSymlnk(pPktHdr);
2808 else if (txsIsSameOpcode(pPktHdr, "RMDIR "))
2809 rc = txsDoRmDir(pPktHdr);
2810 else if (txsIsSameOpcode(pPktHdr, "RMFILE "))
2811 rc = txsDoRmFile(pPktHdr);
2812 else if (txsIsSameOpcode(pPktHdr, "RMSYMLNK"))
2813 rc = txsDoRmSymlnk(pPktHdr);
2814 else if (txsIsSameOpcode(pPktHdr, "RMTREE "))
2815 rc = txsDoRmTree(pPktHdr);
2816 else if (txsIsSameOpcode(pPktHdr, "CHMOD "))
2817 rc = txsDoChMod(pPktHdr);
2818 else if (txsIsSameOpcode(pPktHdr, "CHOWN "))
2819 rc = txsDoChOwn(pPktHdr);
2820 else if (txsIsSameOpcode(pPktHdr, "CHGRP "))
2821 rc = txsDoChGrp(pPktHdr);
2822 else if (txsIsSameOpcode(pPktHdr, "ISDIR "))
2823 rc = txsDoIsDir(pPktHdr);
2824 else if (txsIsSameOpcode(pPktHdr, "ISFILE "))
2825 rc = txsDoIsFile(pPktHdr);
2826 else if (txsIsSameOpcode(pPktHdr, "ISSYMLNK"))
2827 rc = txsDoIsSymlnk(pPktHdr);
2828 else if (txsIsSameOpcode(pPktHdr, "STAT "))
2829 rc = txsDoStat(pPktHdr);
2830 else if (txsIsSameOpcode(pPktHdr, "LSTAT "))
2831 rc = txsDoLStat(pPktHdr);
2832 else if (txsIsSameOpcode(pPktHdr, "LIST "))
2833 rc = txsDoList(pPktHdr);
2834 else if (txsIsSameOpcode(pPktHdr, "PUT FILE"))
2835 rc = txsDoPutFile(pPktHdr);
2836 else if (txsIsSameOpcode(pPktHdr, "GET FILE"))
2837 rc = txsDoGetFile(pPktHdr);
2838 else if (txsIsSameOpcode(pPktHdr, "UNPKFILE"))
2839 rc = txsDoUnpackFile(pPktHdr);
2840 /* Misc: */
2841 else
2842 rc = txsReplyUnknown(pPktHdr);
2843
2844 if (g_cVerbose > 0)
2845 RTMsgInfo("txsMainLoop: CMD: %.8s -> %Rrc", pPktHdr->achOpcode, rc);
2846 RTMemFree(pPktHdr);
2847 }
2848
2849 if (g_cVerbose > 0)
2850 RTMsgInfo("txsMainLoop: end\n");
2851 return enmExitCode;
2852}
2853
2854
2855/**
2856 * Finalizes the scratch directory, making sure it exists.
2857 *
2858 * @returns exit code.
2859 */
2860static RTEXITCODE txsFinalizeScratch(void)
2861{
2862 RTPathStripTrailingSlash(g_szScratchPath);
2863 char *pszFilename = RTPathFilename(g_szScratchPath);
2864 if (!pszFilename)
2865 return RTMsgErrorExit(RTEXITCODE_FAILURE, "cannot use root for scratch (%s)\n", g_szScratchPath);
2866
2867 int rc;
2868 if (strchr(pszFilename, 'X'))
2869 {
2870 char ch = *pszFilename;
2871 rc = RTDirCreateFullPath(g_szScratchPath, 0700);
2872 *pszFilename = ch;
2873 if (RT_SUCCESS(rc))
2874 rc = RTDirCreateTemp(g_szScratchPath, 0700);
2875 }
2876 else
2877 {
2878 if (RTDirExists(g_szScratchPath))
2879 rc = VINF_SUCCESS;
2880 else
2881 rc = RTDirCreateFullPath(g_szScratchPath, 0700);
2882 }
2883 if (RT_FAILURE(rc))
2884 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create scratch directory: %Rrc (%s)\n", rc, g_szScratchPath);
2885 return RTEXITCODE_SUCCESS;
2886}
2887
2888/**
2889 * Attempts to complete an upgrade by updating the original and relaunching
2890 * ourselves from there again.
2891 *
2892 * On failure, we'll continue running as the temporary copy.
2893 *
2894 * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.
2895 * @param argc The number of arguments.
2896 * @param argv The argument vector.
2897 * @param pfExit For indicating exit when the exit code is zero.
2898 * @param pszUpgrading The upgraded image path.
2899 */
2900static RTEXITCODE txsAutoUpdateStage2(int argc, char **argv, bool *pfExit, const char *pszUpgrading)
2901{
2902 if (g_cVerbose > 0)
2903 RTMsgInfo("Auto update stage 2...");
2904
2905 /*
2906 * Copy the current executable onto the original.
2907 * Note that we're racing the original program on some platforms, thus the
2908 * 60 sec sleep mess.
2909 */
2910 char szUpgradePath[RTPATH_MAX];
2911 if (!RTProcGetExecutablePath(szUpgradePath, sizeof(szUpgradePath)))
2912 {
2913 RTMsgError("RTProcGetExecutablePath failed (step 2)\n");
2914 return RTEXITCODE_SUCCESS;
2915 }
2916 void *pvUpgrade;
2917 size_t cbUpgrade;
2918 int rc = RTFileReadAll(szUpgradePath, &pvUpgrade, &cbUpgrade);
2919 if (RT_FAILURE(rc))
2920 {
2921 RTMsgError("RTFileReadAllEx(\"%s\"): %Rrc (step 2)\n", szUpgradePath, rc);
2922 return RTEXITCODE_SUCCESS;
2923 }
2924
2925 uint64_t StartMilliTS = RTTimeMilliTS();
2926 RTFILE hFile;
2927 rc = RTFileOpen(&hFile, pszUpgrading,
2928 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE
2929 | (0755 << RTFILE_O_CREATE_MODE_SHIFT));
2930 while ( RT_FAILURE(rc)
2931 && RTTimeMilliTS() - StartMilliTS < 60000)
2932 {
2933 RTThreadSleep(1000);
2934 rc = RTFileOpen(&hFile, pszUpgrading,
2935 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE
2936 | (0755 << RTFILE_O_CREATE_MODE_SHIFT));
2937 }
2938 if (RT_SUCCESS(rc))
2939 {
2940 rc = RTFileWrite(hFile, pvUpgrade, cbUpgrade, NULL);
2941 RTFileClose(hFile);
2942 if (RT_SUCCESS(rc))
2943 {
2944 /*
2945 * Relaunch the service with the original name, foricbly barring
2946 * further upgrade cycles in case of bugs (and simplifying the code).
2947 */
2948 const char **papszArgs = (const char **)RTMemAlloc((argc + 1 + 1) * sizeof(char **));
2949 if (papszArgs)
2950 {
2951 papszArgs[0] = pszUpgrading;
2952 for (int i = 1; i < argc; i++)
2953 papszArgs[i] = argv[i];
2954 papszArgs[argc] = "--no-auto-upgrade";
2955 papszArgs[argc + 1] = NULL;
2956
2957 RTMsgInfo("Launching upgraded image: \"%s\"\n", pszUpgrading);
2958 RTPROCESS hProc;
2959 rc = RTProcCreate(pszUpgrading, papszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &hProc);
2960 if (RT_SUCCESS(rc))
2961 *pfExit = true;
2962 else
2963 RTMsgError("RTProcCreate(\"%s\"): %Rrc (upgrade stage 2)\n", pszUpgrading, rc);
2964 RTMemFree(papszArgs);
2965 }
2966 else
2967 RTMsgError("RTMemAlloc failed during upgrade attempt (stage 2)\n");
2968 }
2969 else
2970 RTMsgError("RTFileWrite(%s,,%zu): %Rrc (step 2) - BAD\n", pszUpgrading, cbUpgrade, rc);
2971 }
2972 else
2973 RTMsgError("RTFileOpen(,%s,): %Rrc\n", pszUpgrading, rc);
2974 RTFileReadAllFree(pvUpgrade, cbUpgrade);
2975 return RTEXITCODE_SUCCESS;
2976}
2977
2978/**
2979 * Checks for an upgrade and respawns if there is.
2980 *
2981 * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.
2982 * @param argc The number of arguments.
2983 * @param argv The argument vector.
2984 * @param cSecsCdWait Number of seconds to wait on the CD.
2985 * @param pfExit For indicating exit when the exit code is zero.
2986 */
2987static RTEXITCODE txsAutoUpdateStage1(int argc, char **argv, uint32_t cSecsCdWait, bool *pfExit)
2988{
2989 if (g_cVerbose > 1)
2990 RTMsgInfo("Auto update stage 1...");
2991
2992 /*
2993 * Figure names of the current service image and the potential upgrade.
2994 */
2995 char szOrgPath[RTPATH_MAX];
2996 if (!RTProcGetExecutablePath(szOrgPath, sizeof(szOrgPath)))
2997 {
2998 RTMsgError("RTProcGetExecutablePath failed\n");
2999 return RTEXITCODE_SUCCESS;
3000 }
3001
3002 char szUpgradePath[RTPATH_MAX];
3003 int rc = RTPathJoin(szUpgradePath, sizeof(szUpgradePath), g_szCdRomPath, g_szOsSlashArchShortName);
3004 if (RT_SUCCESS(rc))
3005 rc = RTPathAppend(szUpgradePath, sizeof(szUpgradePath), RTPathFilename(szOrgPath));
3006 if (RT_FAILURE(rc))
3007 {
3008 RTMsgError("Failed to construct path to potential service upgrade: %Rrc\n", rc);
3009 return RTEXITCODE_SUCCESS;
3010 }
3011
3012 /*
3013 * Query information about the two images and read the entire potential source file.
3014 * Because the CD may take a little time to be mounted when the system boots, we
3015 * need to do some fudging here.
3016 */
3017 uint64_t nsStart = RTTimeNanoTS();
3018 RTFSOBJINFO UpgradeInfo;
3019 for (;;)
3020 {
3021 rc = RTPathQueryInfo(szUpgradePath, &UpgradeInfo, RTFSOBJATTRADD_NOTHING);
3022 if (RT_SUCCESS(rc))
3023 break;
3024 if ( rc != VERR_FILE_NOT_FOUND
3025 && rc != VERR_PATH_NOT_FOUND
3026 && rc != VERR_MEDIA_NOT_PRESENT
3027 && rc != VERR_MEDIA_NOT_RECOGNIZED)
3028 {
3029 RTMsgError("RTPathQueryInfo(\"%s\"): %Rrc (upgrade)\n", szUpgradePath, rc);
3030 return RTEXITCODE_SUCCESS;
3031 }
3032 uint64_t cNsElapsed = RTTimeNanoTS() - nsStart;
3033 if (cNsElapsed >= cSecsCdWait * RT_NS_1SEC_64)
3034 {
3035 if (g_cVerbose > 0)
3036 RTMsgInfo("Auto update: Giving up waiting for media.");
3037 return RTEXITCODE_SUCCESS;
3038 }
3039 RTThreadSleep(500);
3040 }
3041
3042 RTFSOBJINFO OrgInfo;
3043 rc = RTPathQueryInfo(szOrgPath, &OrgInfo, RTFSOBJATTRADD_NOTHING);
3044 if (RT_FAILURE(rc))
3045 {
3046 RTMsgError("RTPathQueryInfo(\"%s\"): %Rrc (old)\n", szOrgPath, rc);
3047 return RTEXITCODE_SUCCESS;
3048 }
3049
3050 void *pvUpgrade;
3051 size_t cbUpgrade;
3052 rc = RTFileReadAllEx(szUpgradePath, 0, UpgradeInfo.cbObject, RTFILE_RDALL_O_DENY_NONE, &pvUpgrade, &cbUpgrade);
3053 if (RT_FAILURE(rc))
3054 {
3055 RTMsgError("RTPathQueryInfo(\"%s\"): %Rrc (old)\n", szOrgPath, rc);
3056 return RTEXITCODE_SUCCESS;
3057 }
3058
3059 /*
3060 * Compare and see if we've got a different service image or not.
3061 */
3062 if (OrgInfo.cbObject == UpgradeInfo.cbObject)
3063 {
3064 /* must compare bytes. */
3065 void *pvOrg;
3066 size_t cbOrg;
3067 rc = RTFileReadAllEx(szOrgPath, 0, OrgInfo.cbObject, RTFILE_RDALL_O_DENY_NONE, &pvOrg, &cbOrg);
3068 if (RT_FAILURE(rc))
3069 {
3070 RTMsgError("RTFileReadAllEx(\"%s\"): %Rrc\n", szOrgPath, rc);
3071 RTFileReadAllFree(pvUpgrade, cbUpgrade);
3072 return RTEXITCODE_SUCCESS;
3073 }
3074 bool fSame = !memcmp(pvUpgrade, pvOrg, OrgInfo.cbObject);
3075 RTFileReadAllFree(pvOrg, cbOrg);
3076 if (fSame)
3077 {
3078 RTFileReadAllFree(pvUpgrade, cbUpgrade);
3079 if (g_cVerbose > 0)
3080 RTMsgInfo("Auto update: Not necessary.");
3081 return RTEXITCODE_SUCCESS;
3082 }
3083 }
3084
3085 /*
3086 * Should upgrade. Start by creating an executable copy of the update
3087 * image in the scratch area.
3088 */
3089 RTEXITCODE rcExit = txsFinalizeScratch();
3090 if (rcExit == RTEXITCODE_SUCCESS)
3091 {
3092 char szTmpPath[RTPATH_MAX];
3093 rc = RTPathJoin(szTmpPath, sizeof(szTmpPath), g_szScratchPath, RTPathFilename(szOrgPath));
3094 if (RT_SUCCESS(rc))
3095 {
3096 RTFileDelete(szTmpPath); /* shouldn't hurt. */
3097
3098 RTFILE hFile;
3099 rc = RTFileOpen(&hFile, szTmpPath,
3100 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE
3101 | (0755 << RTFILE_O_CREATE_MODE_SHIFT));
3102 if (RT_SUCCESS(rc))
3103 {
3104 rc = RTFileWrite(hFile, pvUpgrade, UpgradeInfo.cbObject, NULL);
3105 RTFileClose(hFile);
3106 if (RT_SUCCESS(rc))
3107 {
3108 /*
3109 * Try execute the new image and quit if it works.
3110 */
3111 const char **papszArgs = (const char **)RTMemAlloc((argc + 2 + 1) * sizeof(char **));
3112 if (papszArgs)
3113 {
3114 papszArgs[0] = szTmpPath;
3115 for (int i = 1; i < argc; i++)
3116 papszArgs[i] = argv[i];
3117 papszArgs[argc] = "--upgrading";
3118 papszArgs[argc + 1] = szOrgPath;
3119 papszArgs[argc + 2] = NULL;
3120
3121 RTMsgInfo("Launching intermediate automatic upgrade stage: \"%s\"\n", szTmpPath);
3122 RTPROCESS hProc;
3123 rc = RTProcCreate(szTmpPath, papszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &hProc);
3124 if (RT_SUCCESS(rc))
3125 *pfExit = true;
3126 else
3127 RTMsgError("RTProcCreate(\"%s\"): %Rrc (upgrade stage 1)\n", szTmpPath, rc);
3128 RTMemFree(papszArgs);
3129 }
3130 else
3131 RTMsgError("RTMemAlloc failed during upgrade attempt (stage)\n");
3132 }
3133 else
3134 RTMsgError("RTFileWrite(%s,,%zu): %Rrc\n", szTmpPath, UpgradeInfo.cbObject, rc);
3135 }
3136 else
3137 RTMsgError("RTFileOpen(,%s,): %Rrc\n", szTmpPath, rc);
3138 }
3139 else
3140 RTMsgError("Failed to construct path to temporary upgrade image: %Rrc\n", rc);
3141 }
3142
3143 RTFileReadAllFree(pvUpgrade, cbUpgrade);
3144 return rcExit;
3145}
3146
3147/**
3148 * Determines the default configuration.
3149 */
3150static void txsSetDefaults(void)
3151{
3152 /*
3153 * OS and ARCH.
3154 */
3155 AssertCompile(sizeof(KBUILD_TARGET) <= sizeof(g_szOsShortName));
3156 strcpy(g_szOsShortName, KBUILD_TARGET);
3157
3158 AssertCompile(sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szArchShortName));
3159 strcpy(g_szArchShortName, KBUILD_TARGET_ARCH);
3160
3161 AssertCompile(sizeof(KBUILD_TARGET) + sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szOsDotArchShortName));
3162 strcpy(g_szOsDotArchShortName, KBUILD_TARGET);
3163 g_szOsDotArchShortName[sizeof(KBUILD_TARGET) - 1] = '.';
3164 strcpy(&g_szOsDotArchShortName[sizeof(KBUILD_TARGET)], KBUILD_TARGET_ARCH);
3165
3166 AssertCompile(sizeof(KBUILD_TARGET) + sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szOsSlashArchShortName));
3167 strcpy(g_szOsSlashArchShortName, KBUILD_TARGET);
3168 g_szOsSlashArchShortName[sizeof(KBUILD_TARGET) - 1] = '/';
3169 strcpy(&g_szOsSlashArchShortName[sizeof(KBUILD_TARGET)], KBUILD_TARGET_ARCH);
3170
3171#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
3172 strcpy(g_szExeSuff, ".exe");
3173 strcpy(g_szScriptSuff, ".cmd");
3174#else
3175 strcpy(g_szExeSuff, "");
3176 strcpy(g_szScriptSuff, ".sh");
3177#endif
3178
3179 /*
3180 * The CD/DVD-ROM location.
3181 */
3182 /** @todo do a better job here :-) */
3183#ifdef RT_OS_WINDOWS
3184 strcpy(g_szDefCdRomPath, "D:/");
3185#elif defined(RT_OS_OS2)
3186 strcpy(g_szDefCdRomPath, "D:/");
3187#else
3188 if (RTDirExists("/media"))
3189 strcpy(g_szDefCdRomPath, "/media/cdrom");
3190 else
3191 strcpy(g_szDefCdRomPath, "/mnt/cdrom");
3192#endif
3193 strcpy(g_szCdRomPath, g_szDefCdRomPath);
3194
3195 /*
3196 * Temporary directory.
3197 */
3198 int rc = RTPathTemp(g_szDefScratchPath, sizeof(g_szDefScratchPath));
3199 if (RT_SUCCESS(rc))
3200#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(RT_OS_DOS)
3201 rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "txs-XXXX.tmp");
3202#else
3203 rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "txs-XXXXXXXXX.tmp");
3204#endif
3205 if (RT_FAILURE(rc))
3206 {
3207 RTMsgError("RTPathTemp/Append failed when constructing scratch path: %Rrc\n", rc);
3208 strcpy(g_szDefScratchPath, "/tmp/txs-XXXX.tmp");
3209 }
3210 strcpy(g_szScratchPath, g_szDefScratchPath);
3211
3212 /*
3213 * The default transporter is the first one.
3214 */
3215 g_pTransport = g_apTransports[0];
3216}
3217
3218/**
3219 * Prints the usage.
3220 *
3221 * @param pStrm Where to print it.
3222 * @param pszArgv0 The program name (argv[0]).
3223 */
3224static void txsUsage(PRTSTREAM pStrm, const char *pszArgv0)
3225{
3226 RTStrmPrintf(pStrm,
3227 "Usage: %Rbn [options]\n"
3228 "\n"
3229 "Options:\n"
3230 " --cdrom <path>\n"
3231 " Where the CD/DVD-ROM will be mounted.\n"
3232 " Default: %s\n"
3233 " --scratch <path>\n"
3234 " Where to put scratch files.\n"
3235 " Default: %s \n"
3236 ,
3237 pszArgv0,
3238 g_szDefCdRomPath,
3239 g_szDefScratchPath);
3240 RTStrmPrintf(pStrm,
3241 " --transport <name>\n"
3242 " Use the specified transport layer, one of the following:\n");
3243 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
3244 RTStrmPrintf(pStrm, " %s - %s\n", g_apTransports[i]->szName, g_apTransports[i]->pszDesc);
3245 RTStrmPrintf(pStrm, " Default: %s\n", g_pTransport->szName);
3246 RTStrmPrintf(pStrm,
3247 " --auto-upgrade, --no-auto-upgrade\n"
3248 " To enable or disable the automatic upgrade mechanism where any different\n"
3249 " version found on the CD-ROM on startup will replace the initial copy.\n"
3250 " Default: --auto-upgrade\n"
3251 " --wait-cdrom <secs>\n"
3252 " Number of seconds to wait for the CD-ROM to be mounted before giving up\n"
3253 " on automatic upgrading.\n"
3254 " Default: --wait-cdrom 1; solaris: --wait-cdrom 8\n"
3255 " --upgrading <org-path>\n"
3256 " Internal use only.\n");
3257 RTStrmPrintf(pStrm,
3258 " --display-output, --no-display-output\n"
3259 " Display the output and the result of all child processes.\n");
3260 RTStrmPrintf(pStrm,
3261 " --foreground\n"
3262 " Don't daemonize, run in the foreground.\n");
3263 RTStrmPrintf(pStrm,
3264 " --help, -h, -?\n"
3265 " Display this message and exit.\n"
3266 " --version, -V\n"
3267 " Display the version and exit.\n");
3268
3269 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
3270 if (g_apTransports[i]->cOpts)
3271 {
3272 RTStrmPrintf(pStrm,
3273 "\n"
3274 "Options for %s:\n", g_apTransports[i]->szName);
3275 g_apTransports[i]->pfnUsage(g_pStdOut);
3276 }
3277}
3278
3279/**
3280 * Parses the arguments.
3281 *
3282 * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.
3283 * @param argc The number of arguments.
3284 * @param argv The argument vector.
3285 * @param pfExit For indicating exit when the exit code is zero.
3286 */
3287static RTEXITCODE txsParseArgv(int argc, char **argv, bool *pfExit)
3288{
3289 *pfExit = false;
3290
3291 /*
3292 * Storage for locally handled options.
3293 */
3294 bool fAutoUpgrade = true;
3295 bool fDaemonize = true;
3296 bool fDaemonized = false;
3297 const char *pszUpgrading = NULL;
3298#ifdef RT_OS_SOLARIS
3299 uint32_t cSecsCdWait = 8;
3300#else
3301 uint32_t cSecsCdWait = 1;
3302#endif
3303
3304 /*
3305 * Combine the base and transport layer option arrays.
3306 */
3307 static const RTGETOPTDEF s_aBaseOptions[] =
3308 {
3309 { "--transport", 't', RTGETOPT_REQ_STRING },
3310 { "--cdrom", 'c', RTGETOPT_REQ_STRING },
3311 { "--wait-cdrom", 'w', RTGETOPT_REQ_UINT32 },
3312 { "--scratch", 's', RTGETOPT_REQ_STRING },
3313 { "--auto-upgrade", 'a', RTGETOPT_REQ_NOTHING },
3314 { "--no-auto-upgrade", 'A', RTGETOPT_REQ_NOTHING },
3315 { "--upgrading", 'U', RTGETOPT_REQ_STRING },
3316 { "--display-output", 'd', RTGETOPT_REQ_NOTHING },
3317 { "--no-display-output",'D', RTGETOPT_REQ_NOTHING },
3318 { "--foreground", 'f', RTGETOPT_REQ_NOTHING },
3319 { "--daemonized", 'Z', RTGETOPT_REQ_NOTHING },
3320 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
3321 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3322 };
3323
3324 size_t cOptions = RT_ELEMENTS(s_aBaseOptions);
3325 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
3326 cOptions += g_apTransports[i]->cOpts;
3327
3328 PRTGETOPTDEF paOptions = (PRTGETOPTDEF)alloca(cOptions * sizeof(RTGETOPTDEF));
3329 if (!paOptions)
3330 return RTMsgErrorExit(RTEXITCODE_FAILURE, "alloca failed\n");
3331
3332 memcpy(paOptions, s_aBaseOptions, sizeof(s_aBaseOptions));
3333 cOptions = RT_ELEMENTS(s_aBaseOptions);
3334 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
3335 {
3336 memcpy(&paOptions[cOptions], g_apTransports[i]->paOpts, g_apTransports[i]->cOpts * sizeof(RTGETOPTDEF));
3337 cOptions += g_apTransports[i]->cOpts;
3338 }
3339
3340 /*
3341 * Parse the arguments.
3342 */
3343 RTGETOPTSTATE GetState;
3344 int rc = RTGetOptInit(&GetState, argc, argv, paOptions, cOptions, 1, 0 /* fFlags */);
3345 AssertRC(rc);
3346
3347 int ch;
3348 RTGETOPTUNION Val;
3349 while ((ch = RTGetOpt(&GetState, &Val)))
3350 {
3351 switch (ch)
3352 {
3353 case 'a':
3354 fAutoUpgrade = true;
3355 break;
3356
3357 case 'A':
3358 fAutoUpgrade = false;
3359 break;
3360
3361 case 'c':
3362 rc = RTStrCopy(g_szCdRomPath, sizeof(g_szCdRomPath), Val.psz);
3363 if (RT_FAILURE(rc))
3364 return RTMsgErrorExit(RTEXITCODE_FAILURE, "CD/DVD-ROM is path too long (%Rrc)\n", rc);
3365 break;
3366
3367 case 'd':
3368 g_fDisplayOutput = true;
3369 break;
3370
3371 case 'D':
3372 g_fDisplayOutput = false;
3373 break;
3374
3375 case 'f':
3376 fDaemonize = false;
3377 break;
3378
3379 case 'h':
3380 txsUsage(g_pStdOut, argv[0]);
3381 *pfExit = true;
3382 return RTEXITCODE_SUCCESS;
3383
3384 case 's':
3385 rc = RTStrCopy(g_szScratchPath, sizeof(g_szScratchPath), Val.psz);
3386 if (RT_FAILURE(rc))
3387 return RTMsgErrorExit(RTEXITCODE_FAILURE, "scratch path is too long (%Rrc)\n", rc);
3388 break;
3389
3390 case 't':
3391 {
3392 PCTXSTRANSPORT pTransport = NULL;
3393 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
3394 if (!strcmp(g_apTransports[i]->szName, Val.psz))
3395 {
3396 pTransport = g_apTransports[i];
3397 break;
3398 }
3399 if (!pTransport)
3400 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown transport layer name '%s'\n", Val.psz);
3401 g_pTransport = pTransport;
3402 break;
3403 }
3404
3405 case 'U':
3406 pszUpgrading = Val.psz;
3407 break;
3408
3409 case 'w':
3410 cSecsCdWait = Val.u32;
3411 break;
3412
3413 case 'q':
3414 g_cVerbose = 0;
3415 break;
3416
3417 case 'v':
3418 g_cVerbose++;
3419 break;
3420
3421 case 'V':
3422 RTPrintf("$Revision: 70488 $\n");
3423 *pfExit = true;
3424 return RTEXITCODE_SUCCESS;
3425
3426 case 'Z':
3427 fDaemonized = true;
3428 fDaemonize = false;
3429 break;
3430
3431 default:
3432 {
3433 rc = VERR_TRY_AGAIN;
3434 for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
3435 if (g_apTransports[i]->cOpts)
3436 {
3437 rc = g_apTransports[i]->pfnOption(ch, &Val);
3438 if (RT_SUCCESS(rc))
3439 break;
3440 if (rc != VERR_TRY_AGAIN)
3441 {
3442 *pfExit = true;
3443 return RTEXITCODE_SYNTAX;
3444 }
3445 }
3446 if (rc == VERR_TRY_AGAIN)
3447 {
3448 *pfExit = true;
3449 return RTGetOptPrintError(ch, &Val);
3450 }
3451 break;
3452 }
3453 }
3454 }
3455
3456 /*
3457 * Handle automatic upgrading of the service.
3458 */
3459 if (fAutoUpgrade && !*pfExit)
3460 {
3461 RTEXITCODE rcExit;
3462 if (pszUpgrading)
3463 rcExit = txsAutoUpdateStage2(argc, argv, pfExit, pszUpgrading);
3464 else
3465 rcExit = txsAutoUpdateStage1(argc, argv, cSecsCdWait, pfExit);
3466 if ( *pfExit
3467 || rcExit != RTEXITCODE_SUCCESS)
3468 return rcExit;
3469 }
3470
3471 /*
3472 * Daemonize ourselves if asked to.
3473 */
3474 if (fDaemonize && !*pfExit)
3475 {
3476 if (g_cVerbose > 0)
3477 RTMsgInfo("Daemonizing...");
3478 rc = RTProcDaemonize(argv, "--daemonized");
3479 if (RT_FAILURE(rc))
3480 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize: %Rrc\n", rc);
3481 *pfExit = true;
3482 }
3483
3484 return RTEXITCODE_SUCCESS;
3485}
3486
3487
3488int main(int argc, char **argv)
3489{
3490 /*
3491 * Initialize the runtime.
3492 */
3493 int rc = RTR3InitExe(argc, &argv, 0);
3494 if (RT_FAILURE(rc))
3495 return RTMsgInitFailure(rc);
3496
3497 /*
3498 * Determine defaults and parse the arguments.
3499 */
3500 txsSetDefaults();
3501 bool fExit;
3502 RTEXITCODE rcExit = txsParseArgv(argc, argv, &fExit);
3503 if (rcExit != RTEXITCODE_SUCCESS || fExit)
3504 return rcExit;
3505
3506 /*
3507 * Generate a UUID for this TXS instance.
3508 */
3509 rc = RTUuidCreate(&g_InstanceUuid);
3510 if (RT_FAILURE(rc))
3511 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTUuidCreate failed: %Rrc", rc);
3512 if (g_cVerbose > 0)
3513 RTMsgInfo("Instance UUID: %RTuuid", &g_InstanceUuid);
3514
3515 /*
3516 * Finalize the scratch directory and initialize the transport layer.
3517 */
3518 rcExit = txsFinalizeScratch();
3519 if (rcExit != RTEXITCODE_SUCCESS)
3520 return rcExit;
3521
3522 rc = g_pTransport->pfnInit();
3523 if (RT_FAILURE(rc))
3524 return RTEXITCODE_FAILURE;
3525
3526 /*
3527 * Ok, start working
3528 */
3529 rcExit = txsMainLoop();
3530
3531 /*
3532 * Cleanup.
3533 */
3534 g_pTransport->pfnTerm();
3535
3536 return rcExit;
3537}
3538
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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