VirtualBox

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

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

Copyright year updates by scm.

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