VirtualBox

source: vbox/trunk/src/VBox/Main/src-helper-apps/os2/os2_util.c@ 93231

Last change on this file since 93231 was 93231, checked in by vboxsync, 3 years ago

Main/Unattended/os2_util: Fix wait method selection, don't always go for the queue.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.6 KB
Line 
1/* $Id: os2_util.c 93231 2022-01-14 02:05:23Z vboxsync $ */
2/** @file
3 * Os2Util - Unattended Installation Helper Utility for OS/2.
4 *
5 * Helps TEE'ing the installation script output to VBox.log and guest side log
6 * files. Also helps with displaying program exit codes, something CMD.exe can't.
7 */
8
9/*
10 * Copyright (C) 2015-2022 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21
22/*********************************************************************************************************************************
23* Header Files *
24*********************************************************************************************************************************/
25#define INCL_BASE
26#include <os2.h>
27#include <iprt/asm-amd64-x86.h>
28#include <VBox/log.h>
29
30#include "VBox/version.h"
31
32
33/*********************************************************************************************************************************
34* Defined Constants And Macros *
35*********************************************************************************************************************************/
36#define IS_BLANK(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\r' || (ch) == '\n')
37
38/** NIL HQUEUE value. */
39#define NIL_HQUEUE (~(HQUEUE)0)
40
41
42/*********************************************************************************************************************************
43* Structures and Typedefs *
44*********************************************************************************************************************************/
45/** Pointer to buffered output. */
46typedef struct MYBUFFER __far *PMYBUFFER;
47
48/** Buffered output. */
49typedef struct MYBUFFER
50{
51 PMYBUFFER pNext;
52 USHORT cb;
53 USHORT off;
54 CHAR sz[65536 - sizeof(USHORT) * 2 - sizeof(PMYBUFFER) - 2];
55} MYBUFFER;
56
57
58/*********************************************************************************************************************************
59* Internal Functions *
60*********************************************************************************************************************************/
61void __far VBoxBackdoorPrint(PSZ psz, unsigned cch);
62static PSZ MyGetOptValue(PSZ psz, PSZ pszOption, PSZ *ppszValue);
63
64
65/*********************************************************************************************************************************
66* Global Variables *
67*********************************************************************************************************************************/
68static HFILE g_hStdOut = 1;
69static HFILE g_hStdErr = 2;
70static BOOL g_fOutputToBackdoor = FALSE;
71static USHORT g_cBuffers = 0;
72static PMYBUFFER g_pBufferHead = NULL;
73static PMYBUFFER g_pBufferTail = NULL;
74
75
76
77/** strlen-like function. */
78static unsigned MyStrLen(PSZ psz)
79{
80 unsigned cch = 0;
81 while (psz[cch] != '\0')
82 cch++;
83 return cch;
84}
85
86
87/** strchr-like function. */
88static char __far *MyStrChr(const char __far *psz, char chNeedle)
89{
90 char ch;
91 while ((ch = *psz) != '\0')
92 {
93 if (ch == chNeedle)
94 return (char __far *)psz;
95 psz++;
96 }
97 return NULL;
98}
99
100
101/** memcpy-like function. */
102static void *MyMemCopy(void __far *pvDst, void const __far *pvSrc, USHORT cb)
103{
104 BYTE __far *pbDst = (BYTE __far *)pvDst;
105 BYTE const __far *pbSrc = (BYTE const __far *)pvSrc;
106 while (cb-- > 0)
107 *pbDst++ = *pbSrc++;
108 return pvDst;
109}
110
111
112static void MyOutStr(PSZ psz)
113{
114 unsigned const cch = MyStrLen(psz);
115 USHORT usIgnored;
116 DosWrite(g_hStdErr, psz, cch, &usIgnored);
117 if (g_fOutputToBackdoor)
118 VBoxBackdoorPrint(psz, cch);
119}
120
121
122static PSZ MyNumToString(PSZ pszBuf, unsigned uNum)
123{
124 /* Convert to decimal and inverted digit order: */
125 char szTmp[32];
126 unsigned off = 0;
127 do
128 {
129 szTmp[off++] = uNum % 10 + '0';
130 uNum /= 10;
131 } while (uNum);
132
133 /* Copy it out to the destination buffer in the right order and add a terminator: */
134 while (off-- > 0)
135 *pszBuf++ = szTmp[off];
136 *pszBuf = '\0';
137 return pszBuf;
138}
139
140
141static void MyOutNum(unsigned uNum)
142{
143 char szTmp[32];
144 MyNumToString(szTmp, uNum);
145 MyOutStr(szTmp);
146}
147
148
149static DECL_NO_RETURN(void) MyApiErrorAndQuit(PSZ pszOperation, USHORT rc)
150{
151 MyOutStr("Os2Util: error: ");
152 MyOutStr(pszOperation);
153 MyOutStr(" failed: ");
154 MyOutNum(rc);
155 MyOutStr("\r\n");
156 DosExit(EXIT_PROCESS, 1);
157}
158
159
160static DECL_NO_RETURN(void) MyApiError3AndQuit(PSZ pszOperation, PSZ psz2, PSZ psz3, USHORT rc)
161{
162 MyOutStr("Os2Util: error: ");
163 MyOutStr(pszOperation);
164 MyOutStr(psz2);
165 MyOutStr(psz3);
166 MyOutStr(" failed: ");
167 MyOutNum(rc);
168 MyOutStr("\r\n");
169 DosExit(EXIT_PROCESS, 1);
170}
171
172
173static DECL_NO_RETURN(void) MySyntaxErrorAndQuit(PSZ pszMsg)
174{
175 MyOutStr("Os2Util: syntax error: ");
176 MyOutStr(pszMsg);
177 MyOutStr("\r\n");
178 DosExit(EXIT_PROCESS, 1);
179}
180
181
182static HFILE OpenTeeFile(PSZ pszTeeToFile, BOOL fAppend, PSZ pszToWrite, USHORT cchToWrite)
183{
184 PMYBUFFER pBuf, pNext;
185 USHORT usIgnored;
186 USHORT usAction = 0;
187 HFILE hFile = -1;
188 USHORT rc;
189 rc = DosOpen(pszTeeToFile, &hFile, &usAction, 0 /*cbInitial*/, 0 /*fFileAttribs*/,
190 OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
191 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_SEQUENTIAL, 0 /*Reserved*/);
192 if (rc == NO_ERROR)
193 {
194
195 if (fAppend)
196 {
197 ULONG offNew = 0;
198 DosChgFilePtr(hFile, 0, FILE_END, &offNew);
199 }
200
201 /*
202 * Write out buffered data
203 */
204 /** @todo this does not seem to work. */
205 pBuf = g_pBufferHead;
206 while (pBuf)
207 {
208 do
209 rc = DosWrite(hFile, pBuf->sz, pBuf->off, &usIgnored);
210 while (rc == ERROR_INTERRUPT);
211 pNext = pBuf->pNext;
212 DosFreeSeg((__segment)pBuf);
213 pBuf = pNext;
214 }
215 g_pBufferTail = g_pBufferHead = NULL;
216
217 /*
218 * Write the current output.
219 */
220 do
221 rc = DosWrite(hFile, pszToWrite, cchToWrite, &usIgnored);
222 while (rc == ERROR_INTERRUPT);
223 }
224 else
225 {
226 /*
227 * Failed to open the file. Buffer a bit in case the file can be
228 * opened later (like when we've formatted the disk).
229 */
230 pBuf = g_pBufferTail;
231 if (pBuf && pBuf->off < pBuf->cb)
232 {
233 USHORT cbToCopy = pBuf->cb - pBuf->off;
234 if (cbToCopy > cchToWrite)
235 cbToCopy = cchToWrite;
236 MyMemCopy(&pBuf->sz[pBuf->off], pszToWrite, cbToCopy);
237 pszToWrite += cbToCopy;
238 cchToWrite -= cbToCopy;
239 }
240 if (cchToWrite > 0)
241 {
242 USHORT uSel = 0xffff;
243 if ( g_cBuffers < 10
244 && (rc = DosAllocSeg(0 /*64KiB*/, &uSel, 0 /*fFlags*/)) == NO_ERROR)
245 {
246 pBuf = ((__segment)uSel) :> ((MYBUFFER __near *)0);
247 pBuf->pNext = NULL;
248 pBuf->cb = sizeof(pBuf->sz);
249 pBuf->off = cchToWrite;
250 MyMemCopy(&pBuf->sz[0], pszToWrite, cchToWrite);
251
252 if (g_pBufferTail)
253 g_pBufferTail->pNext = pBuf;
254 else
255 g_pBufferHead = pBuf;
256 g_pBufferTail = pBuf;
257 }
258 else if (g_cBuffers > 0)
259 {
260 pBuf = g_pBufferHead;
261 pBuf->off = cchToWrite;
262 MyMemCopy(&pBuf->sz[0], pszToWrite, cchToWrite);
263
264 if (g_pBufferTail != pBuf)
265 {
266 g_pBufferHead = pBuf->pNext;
267 pBuf->pNext = NULL;
268 g_pBufferTail->pNext = pBuf;
269 g_pBufferTail = pBuf;
270 }
271 }
272 }
273 hFile = -1;
274 }
275 return hFile;
276}
277
278
279/**
280 * Waits for the child progress to complete, returning it's status code.
281 */
282static void DoWait(PID pidChild, USHORT idSession, HQUEUE hQueue, PRESULTCODES pResultCodes)
283{
284 /*
285 * Can we use DosCwait?
286 */
287 if (hQueue == NIL_HQUEUE)
288 {
289 for (;;)
290 {
291 PID pidIgnored;
292 USHORT rc = DosCwait(DCWA_PROCESS, DCWW_WAIT, pResultCodes, &pidIgnored, pidChild);
293 if (rc == NO_ERROR)
294 break;
295 if (rc != ERROR_INTERRUPT)
296 {
297 MyOutStr("Os2Util: error: DosCwait(DCWA_PROCESS,DCWW_WAIT,,,");
298 MyOutNum(pidChild);
299 MyOutStr(") failed: ");
300 MyOutNum(rc);
301 MyOutStr("\r\n");
302 }
303 }
304 }
305 else
306 {
307 /*
308 * No we have to use the queue interface to the session manager.
309 */
310 for (;;)
311 {
312 ULONG ulAdderPidAndEvent = 0;
313 PUSHORT pausData = NULL;
314 USHORT cbData = 0;
315 BYTE bPriority = 0;
316 HSEM hSem = NULL;
317 USHORT rc = DosReadQueue(hQueue, &ulAdderPidAndEvent, &cbData, (PULONG)&pausData,
318 0 /*uElementCode*/, 0 /* fNoWait */, &bPriority, &hSem);
319 if (rc == NO_ERROR)
320 {
321 if (cbData >= sizeof(USHORT) * 2)
322 {
323 USHORT idTermSession = pausData[0];
324 USHORT uExitCode = pausData[1];
325 if (idTermSession == idSession)
326 {
327 pResultCodes->codeTerminate = 0;
328 pResultCodes->codeResult = uExitCode;
329 break;
330 }
331 if (1)
332 {
333 MyOutStr("OutUtil: info: idTermSession=");
334 MyOutNum(idTermSession);
335 MyOutStr(" uExitCode=");
336 MyOutNum(uExitCode);
337 MyOutStr("\r\n");
338 }
339 }
340 else
341 {
342 MyOutStr("OutUtil: warning: bogus queue element size: cbData=");
343 MyOutNum(cbData);
344 MyOutStr("\r\n");
345 }
346 DosFreeSeg((__segment)pausData);
347 }
348 else if (rc != ERROR_INTERRUPT)
349 {
350 DosCloseQueue(hQueue);
351 MyApiErrorAndQuit("DosReadQueue", rc);
352 }
353 }
354 }
355}
356
357
358/**
359 * Handles --file-to-backdoor / -c.
360 */
361static void CopyFileToBackdoorAndQuit(PSZ psz, BOOL fLongOpt, PSZ pszBuf, USHORT cbBuf)
362{
363 HFILE hFile = 0;
364 USHORT usAction = 0;
365 USHORT rc;
366
367 /*
368 * Get the filename and check that it is the last thing on the commandline.
369 */
370 PSZ pszFilename = NULL;
371 CHAR ch;
372 psz = MyGetOptValue(psz, fLongOpt ? "--file-to-backdoor" : "-c", &pszFilename);
373 while ((ch = *psz) != '\0' && IS_BLANK(ch))
374 psz++;
375 if (ch != '\0')
376 MySyntaxErrorAndQuit("No options allowed after -c/--file-to-backdoor");
377
378 /*
379 * Open the file
380 */
381 rc = DosOpen(pszFilename, &hFile, &usAction, 0 /*cbInitial*/, 0 /*fFileAttribs*/,
382 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
383 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_SEQUENTIAL, 0 /*Reserved*/);
384 if (rc != NO_ERROR)
385 MyApiError3AndQuit("Failed to open \"", pszFilename, "\" for reading", rc);
386
387 VBoxBackdoorPrint(RT_STR_TUPLE("--- BEGIN OF \""));
388 VBoxBackdoorPrint(pszFilename, MyStrLen(pszFilename));
389 VBoxBackdoorPrint(RT_STR_TUPLE("\" ---\n"));
390
391 for (;;)
392 {
393 USHORT cbRead = 0;
394 rc = DosRead(hFile, pszBuf, cbBuf, &cbRead);
395 if (rc == NO_ERROR)
396 {
397 if (cbRead == 0)
398 break;
399 VBoxBackdoorPrint(pszBuf, cbRead);
400 }
401 else if (rc != ERROR_INTERRUPT)
402 MyApiError3AndQuit("Reading \"", pszFilename, "\"", rc);
403 }
404
405 VBoxBackdoorPrint(RT_STR_TUPLE("--- END OF \""));
406 VBoxBackdoorPrint(pszFilename, MyStrLen(pszFilename));
407 VBoxBackdoorPrint(RT_STR_TUPLE("\" ---\n"));
408
409 DosClose(hFile);
410 DosExit(EXIT_PROCESS, 1);
411}
412
413
414/** Displays version string and quits. */
415static DECL_NO_RETURN(void) ShowVersionAndQuit(void)
416{
417 CHAR szVer[] = "$Rev: 93231 $\r\n";
418 USHORT usIgnored;
419 DosWrite(g_hStdOut, szVer, sizeof(szVer) - 1, &usIgnored);
420 DosExit(EXIT_PROCESS, 0);
421}
422
423
424/** Displays usage info and quits. */
425static DECL_NO_RETURN(void) ShowUsageAndQuit(void)
426{
427 static char s_szHelp[] =
428 VBOX_PRODUCT " OS/2 Unattended Helper Version " VBOX_VERSION_STRING "\r\n"
429 "(C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\r\n"
430 "\r\n"
431 "Os2Util.exe is tiny helper utility that implements TEE'ing to the VBox release\r\n"
432 "log, files and shows the actual exit code of a program. Standard error and\r\n"
433 "output will be merged into one for simplicity reasons.\r\n"
434 "\r\n"
435 "Usage: Os2Util.exe [-a|--append] [-f<filename>|--tee-to-file <filename>] \\\r\n"
436 " [-b|--tee-to-backdoor] [-z<exit>|--as-zero <exit> [..]] \\\r\n"
437 " -- <prog> [args]\r\n"
438 " or Os2Util.exe <-w<msg>|--write-backdoor <msg>>\r\n"
439 " or Os2Util.exe <-c<file>|--file-to-backdoor <file>>\r\n"
440 "\r\n"
441 "Note! Does not supported any kind of quoting before the child arguments.\r\n"
442 ;
443 USHORT usIgnored;
444 DosWrite(g_hStdOut, s_szHelp, sizeof(s_szHelp) - 1, &usIgnored);
445 DosExit(EXIT_PROCESS, 0);
446}
447
448
449/**
450 * Gets the an option value.
451 *
452 * The option value string will be terminated.
453 */
454static PSZ MyGetOptValue(PSZ psz, PSZ pszOption, PSZ *ppszValue)
455{
456 CHAR ch;
457 while ((ch = *psz) != '\0' && IS_BLANK(ch))
458 psz++;
459 if (*psz == '\0')
460 {
461 MyOutStr("Os2Util: syntax error: Option '");
462 MyOutStr(pszOption);
463 MyOutStr("' takes a value\r\n");
464 DosExit(EXIT_PROCESS, 2);
465 }
466
467 *ppszValue = psz;
468
469 while ((ch = *psz) != '\0' && !IS_BLANK(ch))
470 psz++;
471 if (ch != '\0')
472 *psz++ = '\0';
473 return psz;
474}
475
476
477/**
478 * Gets the an numeric option value.
479 */
480static PSZ MyGetOptNum(PSZ psz, PSZ pszOption, PUSHORT puValue)
481{
482 PSZ pszError = NULL;
483 PSZ pszValue = NULL;
484 PSZ const pszRet = MyGetOptValue(psz, pszOption, &pszValue);
485 PSZ const pszValueStart = pszValue;
486 USHORT uValue = 0;
487 CHAR ch;
488 if (pszValue[0] == '0' && ((ch = pszValue[1]) == 'x' || ch == 'X'))
489 {
490 pszValue += 2;
491 while ((ch = *pszValue++) != '\0')
492 {
493 BYTE bDigit;
494 if (ch <= '9' && ch >= '0')
495 bDigit = ch - '0';
496 else if (ch <= 'f' && ch >= 'a')
497 bDigit = ch - 'a' + 10;
498 else if (ch <= 'F' && ch >= 'A')
499 bDigit = ch - 'A' + 10;
500 else
501 {
502 pszError = "': invalid hex value\r\n";
503 break;
504 }
505 if (uValue >> 12)
506 {
507 pszError = "': hex value out of range\r\n";
508 break;
509 }
510 uValue <<= 4;
511 uValue |= bDigit;
512 }
513 }
514 else
515 {
516 while ((ch = *pszValue++) != '\0')
517 {
518 BYTE bDigit;
519 if (ch <= '9' && ch >= '0')
520 bDigit = ch - '0';
521 else
522 {
523 pszError = "': invalid decimal value\r\n";
524 break;
525 }
526 if (uValue * 10 / 10 != uValue)
527 {
528 pszError = "': decimal value out of range\r\n";
529 break;
530 }
531 uValue *= 10;
532 uValue += bDigit;
533 }
534 }
535
536 if (pszError)
537 {
538 MyOutStr("Os2Util: syntax error: Option '");
539 MyOutStr(pszOption);
540 MyOutStr("' with value '");
541 MyOutStr(pszValueStart);
542 MyOutStr(pszError);
543 DosExit(EXIT_PROCESS, 2);
544 }
545
546 *puValue = uValue;
547 return pszRet;
548}
549
550
551/**
552 * Checks if @a pszOption matches @a *ppsz, advance *ppsz if TRUE.
553 */
554static BOOL MyMatchLongOption(PSZ _far *ppsz, PSZ pszOption, unsigned cchOption)
555{
556 /* Match option and command line strings: */
557 PSZ psz = *ppsz;
558 while (cchOption-- > 0)
559 {
560 if (*psz != *pszOption)
561 return FALSE;
562 psz++;
563 pszOption++;
564 }
565
566 /* Is this the end of a word on the command line? */
567 if (*psz == '\0')
568 *ppsz = psz;
569 else if (IS_BLANK(*psz))
570 *ppsz = psz + 1;
571 else
572 return FALSE;
573 return TRUE;
574}
575
576
577/**
578 * The entrypoint (no crt).
579 */
580#pragma aux Os2UtilMain "_*" parm caller [ ax ] [ bx ];
581void Os2UtilMain(USHORT uSelEnv, USHORT offCmdLine)
582{
583 PSZ pszzEnv = ((__segment)uSelEnv) :> ((char _near *)0);
584 PSZ pszzCmdLine = ((__segment)uSelEnv) :> ((char _near *)offCmdLine);
585 USHORT uExitCode = 1;
586 BOOL fTeeToBackdoor = FALSE;
587 BOOL fAppend = FALSE;
588 PSZ pszTeeToFile = NULL;
589 HFILE hTeeToFile = -1;
590 HFILE hPipeRead = -1;
591 PSZ pszzNewCmdLine;
592 PSZ psz;
593 CHAR ch;
594 USHORT usIgnored;
595 USHORT rc;
596 RESULTCODES ResultCodes = { 0xffff, 0xffff };
597 CHAR szBuf[512];
598 CHAR szExeFull[CCHMAXPATH];
599 PSZ pszExe;
600 USHORT uExeType;
601 USHORT idSession = 0;
602 PID pidChild = 0;
603 HQUEUE hQueue = NIL_HQUEUE;
604 CHAR szQueueName[64];
605 unsigned cAsZero = 0;
606 USHORT auAsZero[16];
607
608 /*
609 * Parse the command line.
610 * Note! We do not accept any kind of quoting.
611 */
612 /* Skip the executable filename: */
613 psz = pszzCmdLine;
614 while (*psz != '\0')
615 psz++;
616 psz++;
617
618 /* Now parse arguments. */
619 while ((ch = *psz) != '\0')
620 {
621 if (IS_BLANK(ch))
622 psz++;
623 else if (ch != '-')
624 break;
625 else
626 {
627 PSZ const pszOptStart = psz;
628 ch = *++psz;
629 if (ch == '-')
630 {
631 ch = *++psz;
632 if (IS_BLANK(ch))
633 {
634 /* Found end-of-arguments marker "--" */
635 psz++;
636 break;
637 }
638 if (ch == 'a' && MyMatchLongOption(&psz, RT_STR_TUPLE("append")))
639 fAppend = TRUE;
640 else if (ch == 'a' && MyMatchLongOption(&psz, RT_STR_TUPLE("as-zero")))
641 {
642 if (cAsZero > RT_ELEMENTS(auAsZero))
643 MySyntaxErrorAndQuit("Too many --as-zero/-z options");
644 psz = MyGetOptNum(psz, "--as-zero", &auAsZero[cAsZero]);
645 cAsZero++;
646 }
647 else if (ch == 'f' && MyMatchLongOption(&psz, RT_STR_TUPLE("file-to-backdoor")))
648 CopyFileToBackdoorAndQuit(psz, TRUE /*fLongOpt*/, szBuf, sizeof(szBuf));
649 else if (ch == 'h' && MyMatchLongOption(&psz, RT_STR_TUPLE("help")))
650 ShowUsageAndQuit();
651 else if (ch == 't' && MyMatchLongOption(&psz, RT_STR_TUPLE("tee-to-backdoor")))
652 g_fOutputToBackdoor = fTeeToBackdoor = TRUE;
653 else if (ch == 't' && MyMatchLongOption(&psz, RT_STR_TUPLE("tee-to-file")))
654 psz = MyGetOptValue(psz, "--tee-to-file", &pszTeeToFile);
655 else if (ch == 'v' && MyMatchLongOption(&psz, RT_STR_TUPLE("version")))
656 ShowVersionAndQuit();
657 else if (ch == 'w' && MyMatchLongOption(&psz, RT_STR_TUPLE("write-backdoor")))
658 {
659 VBoxBackdoorPrint(psz, MyStrLen(psz));
660 VBoxBackdoorPrint("\n", 1);
661 DosExit(EXIT_PROCESS, 0);
662 }
663 else
664 {
665 MyOutStr("Os2util: syntax error: ");
666 MyOutStr(pszOptStart);
667 MyOutStr("\r\n");
668 DosExit(EXIT_PROCESS, 2);
669 }
670 }
671 else
672 {
673 do
674 {
675 if (ch == 'a')
676 fAppend = TRUE;
677 else if (ch == 'b')
678 g_fOutputToBackdoor = fTeeToBackdoor = TRUE;
679 else if (ch == 'c')
680 CopyFileToBackdoorAndQuit(psz + 1, FALSE /*fLongOpt*/, szBuf, sizeof(szBuf));
681 else if (ch == 'f')
682 {
683 psz = MyGetOptValue(psz + 1, "-f", &pszTeeToFile);
684 break;
685 }
686 else if (ch == 'w')
687 {
688 psz++;
689 VBoxBackdoorPrint(psz, MyStrLen(psz));
690 VBoxBackdoorPrint("\n", 1);
691 DosExit(EXIT_PROCESS, 0);
692 }
693 else if (ch == 'z')
694 {
695 if (cAsZero > RT_ELEMENTS(auAsZero))
696 MySyntaxErrorAndQuit("Too many --as-zero/-z options");
697 psz = MyGetOptNum(psz + 1, "-z", &auAsZero[cAsZero]);
698 cAsZero++;
699 }
700 else if (ch == '?' || ch == 'h' || ch == 'H')
701 ShowUsageAndQuit();
702 else if (ch == 'V')
703 ShowVersionAndQuit();
704 else
705 {
706 MyOutStr("Os2util: syntax error: ");
707 if (ch)
708 DosWrite(g_hStdErr, &ch, 1, &usIgnored);
709 else
710 MyOutStr("lone dash");
711 MyOutStr(" (");
712 MyOutStr(pszOptStart);
713 MyOutStr(")\r\n");
714 DosExit(EXIT_PROCESS, 2);
715 }
716 ch = *++psz;
717 } while (!IS_BLANK(ch) && ch != '\0');
718 }
719 }
720 }
721
722 /*
723 * Zero terminate the executable name in the command line.
724 */
725 pszzNewCmdLine = psz;
726 if (ch == '\0')
727 {
728 MyOutStr("Os2Util: syntax error: No program specified\r\n");
729 DosExit(EXIT_PROCESS, 2);
730 }
731 psz++;
732 while ((ch = *psz) != '\0' && !IS_BLANK(ch))
733 psz++;
734 *psz++ = '\0';
735
736 /*
737 * Find the executable and check its type.
738 */
739 if ( pszzNewCmdLine[1] == ':'
740 || MyStrChr(pszzNewCmdLine, '\\')
741 || MyStrChr(pszzNewCmdLine, '/'))
742 pszExe = pszzNewCmdLine;
743 else
744 {
745 rc = DosSearchPath(SEARCH_CUR_DIRECTORY | SEARCH_ENVIRONMENT | SEARCH_IGNORENETERRS, "PATH",
746 pszzNewCmdLine, szExeFull, sizeof(szExeFull));
747 if (rc != NO_ERROR)
748 MyApiError3AndQuit("DosSearchPath(7, \"PATH\", \"", pszzNewCmdLine, "\",,)", rc);
749 pszExe = &szExeFull[0];
750 }
751
752 /* Perhapse we should use WinQueryProgramType here instead? */
753 rc = DosQAppType(pszExe, &uExeType);
754 if (rc != NO_ERROR)
755 MyApiErrorAndQuit("DosQAppType(pszExe, &uExeType)", rc);
756#ifdef DEBUG
757 MyOutStr("Os2Util: debug: uExeType="); MyOutNum(uExeType); MyOutStr("\r\n");
758#endif
759 /** @todo deal with launching winos2 programs too... */
760
761 /*
762 * Prepare redirection.
763 */
764 if (fTeeToBackdoor || pszTeeToFile != NULL)
765 {
766 HFILE hPipeWrite = -1;
767 HFILE hDup;
768
769 /* Make new copies of the standard handles. */
770 hDup = 0xffff;
771 rc = DosDupHandle(g_hStdErr, &hDup);
772 if (rc != NO_ERROR)
773 MyApiErrorAndQuit("DosDupHandle(g_hStdErr, &hDup)", rc);
774 g_hStdErr = hDup;
775 DosSetFHandState(hDup, OPEN_FLAGS_NOINHERIT); /* not strictly necessary, so ignore errors */
776
777 hDup = 0xffff;
778 rc = DosDupHandle(g_hStdOut, &hDup);
779 if (rc != NO_ERROR)
780 MyApiErrorAndQuit("DosDupHandle(g_hStdOut, &hDup)", rc);
781 g_hStdOut = hDup;
782 DosSetFHandState(hDup, OPEN_FLAGS_NOINHERIT); /* not strictly necessary, so ignore errors */
783
784 /* Create the pipe and make the read-end non-inheritable (we'll hang otherwise). */
785 rc = DosMakePipe(&hPipeRead, &hPipeWrite, 0 /*default size*/);
786 if (rc != NO_ERROR)
787 MyApiErrorAndQuit("DosMakePipe", rc);
788
789 rc = DosSetFHandState(hPipeRead, OPEN_FLAGS_NOINHERIT);
790 if (rc != NO_ERROR)
791 MyApiErrorAndQuit("DosSetFHandState(hPipeRead, OPEN_FLAGS_NOINHERIT)", rc);
792
793 /* Replace standard output and standard error with the write end of the pipe. */
794 hDup = 1;
795 rc = DosDupHandle(hPipeWrite, &hDup);
796 if (rc != NO_ERROR)
797 MyApiErrorAndQuit("DosDupHandle(hPipeWrite, &hDup[=1])", rc);
798
799 hDup = 2;
800 rc = DosDupHandle(hPipeWrite, &hDup);
801 if (rc != NO_ERROR)
802 MyApiErrorAndQuit("DosDupHandle(hPipeWrite, &hDup[=2])", rc);
803
804 /* We can close the write end of the pipe as we don't need the original handle any more. */
805 DosClose(hPipeWrite);
806 }
807
808 /*
809 * Execute the program.
810 */
811 szBuf[0] = '\0';
812#define FAPPTYP_TYPE_MASK 7
813 if ((uExeType & FAPPTYP_TYPE_MASK) == PT_WINDOWABLEVIO) /** @todo what if we're in fullscreen ourselves? */
814 {
815 /* For same type programs we can use DosExecPgm: */
816 rc = DosExecPgm(szBuf, sizeof(szBuf), hPipeRead == -1 ? EXEC_SYNC : EXEC_ASYNCRESULT,
817 pszzNewCmdLine, pszzEnv, &ResultCodes, pszExe);
818 if (rc != NO_ERROR)
819 {
820 MyOutStr("Os2Util: error: DosExecPgm failed for \"");
821 MyOutStr(pszzNewCmdLine);
822 MyOutStr("\": ");
823 MyOutNum(rc);
824 if (szBuf[0])
825 {
826 MyOutStr(" ErrObj=");
827 szBuf[sizeof(szBuf) - 1] = '\0';
828 MyOutStr(szBuf);
829 }
830 MyOutStr("\r\n");
831 DosExit(EXIT_PROCESS, 1);
832 }
833 if (hPipeRead != -1)
834 {
835 pidChild = ResultCodes.codeTerminate;
836 MyOutStr("info: started pid ");
837 MyOutNum(pidChild);
838 MyOutStr("\r\n");
839 }
840 }
841 else
842 {
843 /* For different typed programs we have to use DosStartSession, which
844 is a lot more tedious to use. */
845 static const char s_szQueueBase[] = "\\QUEUES\\OS2_UTIL-";
846 union
847 {
848 STARTDATA StartData;
849 BYTE abPadding[sizeof(STARTDATA) + 64];
850 struct
851 {
852 STARTDATA Core;
853 ULONG ulReserved;
854 PSZ pszBuf;
855 USHORT cbBuf;
856 } s;
857 } u;
858 PIDINFO PidInfo = {0, 0, 0};
859
860 /* Create the wait queue first. */
861 DosGetPID(&PidInfo);
862 MyMemCopy(szQueueName, s_szQueueBase, sizeof(s_szQueueBase));
863 MyNumToString(&szQueueName[sizeof(s_szQueueBase) - 1], PidInfo.pid);
864
865 rc = DosCreateQueue(&hQueue, 0 /*FIFO*/, szQueueName);
866 if (rc != NO_ERROR)
867 MyApiError3AndQuit("DosCreateQueue(&hQueue, 0, \"", szQueueName, "\")", rc);
868
869 u.StartData.Length = sizeof(u.StartData);
870 u.StartData.Related = 1 /* SSF_RELATED_CHILD */;
871 u.StartData.FgBg = (uExeType & FAPPTYP_TYPE_MASK) == PT_PM
872 ? 1 /* SSF_FGBG_BACK - try avoid ERROR_SMG_START_IN_BACKGROUND */
873 : 0 /* SSF_FGBG_FORE */;
874 u.StartData.TraceOpt = 0 /* SSF_TRACEOPT_NONE */;
875 u.StartData.PgmTitle = NULL;
876 u.StartData.PgmName = pszExe;
877 u.StartData.PgmInputs = psz; /* just arguments, not exec apparently.*/
878 u.StartData.TermQ = szQueueName;
879 u.StartData.Environment = NULL; /* Inherit our env. Note! Using pszzEnv causes it to be freed
880 and we'll crash reporting the error. */
881 u.StartData.InheritOpt = 1 /* SSF_INHERTOPT_PARENT */;
882 u.StartData.SessionType = uExeType & FAPPTYP_TYPE_MASK;
883 if (uExeType & 0x20 /*FAPPTYP_DOS*/)
884 u.StartData.SessionType = 4 /* SSF_TYPE_VDM */;
885 u.StartData.IconFile = NULL;
886 u.StartData.PgmHandle = 0;
887 u.StartData.PgmControl = 0 /* SSF_CONTROL_VISIBLE */;
888 u.StartData.InitXPos = 0;
889 u.StartData.InitYPos = 0;
890 u.StartData.InitXSize = 0;
891 u.StartData.InitYSize = 0;
892 u.s.ulReserved = 0;
893 u.s.pszBuf = NULL;
894 u.s.cbBuf = 0;
895
896 rc = DosStartSession(&u.StartData, &idSession, &pidChild);
897 if (rc != NO_ERROR && rc != ERROR_SMG_START_IN_BACKGROUND)
898 {
899 DosCloseQueue(hQueue);
900 MyApiError3AndQuit("DosStartSession for \"", pszExe, "\"", rc);
901 }
902
903 if (1)
904 {
905 MyOutStr("info: started session ");
906 MyOutNum(idSession);
907 MyOutStr(", pid ");
908 MyOutNum(pidChild);
909 MyOutStr("\r\n");
910 }
911 }
912
913 /*
914 * Wait for the child process to complete.
915 */
916 if (hPipeRead != -1)
917 {
918
919 /* Close the write handles or we'll hang in the read loop. */
920 DosClose(1);
921 DosClose(2);
922
923 /* Disable hard error popups (file output to unformatted disks). */
924 DosError(2 /* only exceptions */);
925
926 /*
927 * Read the pipe and tee it to the desired outputs
928 */
929 for (;;)
930 {
931 USHORT cbRead = 0;
932 rc = DosRead(hPipeRead, szBuf, sizeof(szBuf), &cbRead);
933 if (rc == NO_ERROR)
934 {
935 if (cbRead == 0)
936 break; /* No more writers. */
937
938 /* Standard output: */
939 do
940 rc = DosWrite(g_hStdOut, szBuf, cbRead, &usIgnored);
941 while (rc == ERROR_INTERRUPT);
942
943 /* Backdoor: */
944 if (fTeeToBackdoor)
945 VBoxBackdoorPrint(szBuf, cbRead);
946
947 /* File: */
948 if (hTeeToFile != -1)
949 do
950 rc = DosWrite(hTeeToFile, szBuf, cbRead, &usIgnored);
951 while (rc == ERROR_INTERRUPT);
952 else if (pszTeeToFile != NULL)
953 hTeeToFile = OpenTeeFile(pszTeeToFile, fAppend, szBuf, cbRead);
954 }
955 else if (rc == ERROR_BROKEN_PIPE)
956 break;
957 else
958 {
959 MyOutStr("Os2Util: error: Error reading pipe: ");
960 MyOutNum(rc);
961 MyOutStr("\r\n");
962 break;
963 }
964 }
965
966 DosClose(hPipeRead);
967
968 /*
969 * Wait for the process to complete.
970 */
971 DoWait(pidChild, idSession, hQueue, &ResultCodes);
972 }
973 /*
974 * Must wait for the session completion too.
975 */
976 else if (idSession != 0)
977 DoWait(pidChild, idSession, hQueue, &ResultCodes);
978
979 /*
980 * Report the status code and quit.
981 */
982 MyOutStr("Os2Util: Child: ");
983 MyOutStr(pszzNewCmdLine);
984 MyOutStr(" ");
985 MyOutStr(psz);
986 MyOutStr("\r\n"
987 "Os2Util: codeTerminate=");
988 MyOutNum(ResultCodes.codeTerminate);
989 MyOutStr(" codeResult=");
990 MyOutNum(ResultCodes.codeResult);
991 MyOutStr("\r\n");
992
993 /* Treat it as zero? */
994 if (ResultCodes.codeTerminate == 0)
995 {
996 unsigned i = cAsZero;
997 while (i-- > 0)
998 if (auAsZero[i] == ResultCodes.codeResult)
999 {
1000 MyOutStr("Os2Util: info: treating status as zero\r\n");
1001 ResultCodes.codeResult = 0;
1002 break;
1003 }
1004 }
1005
1006 if (idSession != 0)
1007 DosCloseQueue(hQueue);
1008 for (;;)
1009 DosExit(EXIT_PROCESS, ResultCodes.codeTerminate == 0 ? ResultCodes.codeResult : 127);
1010}
1011
1012
1013/**
1014 * Backdoor print function living in an IOPL=2 segment.
1015 */
1016#pragma code_seg("IOPL", "CODE")
1017void __far VBoxBackdoorPrint(PSZ psz, unsigned cch)
1018{
1019 ASMOutStrU8(RTLOG_DEBUG_PORT, psz, cch);
1020}
1021
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