VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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

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