VirtualBox

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

Last change on this file since 84683 was 84683, checked in by vboxsync, 4 years ago

Validation Kit/TestExecService: Use RTFILE_O_DENY_NONE in txsDoGetFile() to avoid sharing violations for log files which might be still open on the guest for writing.

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