VirtualBox

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

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

Main/UnattendedInstall: Added a 'tee VBox.log' kind of utility for simplifying OS/2 work. Will check in binary later.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.3 KB
Line 
1/* $Id: os2_util.c 93116 2022-01-02 04:58:13Z 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
31/*********************************************************************************************************************************
32* Defined Constants And Macros *
33*********************************************************************************************************************************/
34#define IS_BLANK(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\r' || (ch) == '\n')
35
36
37/*********************************************************************************************************************************
38* Structures and Typedefs *
39*********************************************************************************************************************************/
40/** Pointer to buffered output. */
41typedef struct MYBUFFER __far *PMYBUFFER;
42
43/** Buffered output. */
44typedef struct MYBUFFER
45{
46 PMYBUFFER pNext;
47 USHORT cb;
48 USHORT off;
49 CHAR sz[65536 - sizeof(USHORT) * 2 - sizeof(PMYBUFFER) - 2];
50} MYBUFFER;
51
52
53/*********************************************************************************************************************************
54* Internal Functions *
55*********************************************************************************************************************************/
56void __far VBoxBackdoorPrint(PSZ psz, unsigned cch);
57
58
59/*********************************************************************************************************************************
60* Global Variables *
61*********************************************************************************************************************************/
62static HFILE g_hStdOut = 1;
63static HFILE g_hStdErr = 2;
64static BOOL g_fOutputToBackdoor = FALSE;
65static USHORT g_cBuffers = 0;
66static PMYBUFFER g_pBufferHead = NULL;
67static PMYBUFFER g_pBufferTail = NULL;
68
69
70
71/** strlen-like function. */
72static unsigned MyStrLen(PSZ psz)
73{
74 unsigned cch = 0;
75 while (psz[cch] != '\0')
76 cch++;
77 return cch;
78}
79
80
81/** memcpy-like function. */
82static void *MyMemCopy(void __far *pvDst, void const __far *pvSrc, USHORT cb)
83{
84 BYTE __far *pbDst = (BYTE __far *)pvDst;
85 BYTE const __far *pbSrc = (BYTE const __far *)pvSrc;
86 while (cb-- > 0)
87 *pbDst++ = *pbSrc++;
88 return pvDst;
89}
90
91
92static void MyOutStr(PSZ psz)
93{
94 unsigned const cch = MyStrLen(psz);
95 USHORT usIgnored;
96 DosWrite(g_hStdErr, psz, cch, &usIgnored);
97 if (g_fOutputToBackdoor)
98 VBoxBackdoorPrint(psz, cch);
99}
100
101
102static PSZ MyNumToString(PSZ pszBuf, unsigned uNum)
103{
104 /* Convert to decimal and inverted digit order: */
105 char szTmp[32];
106 unsigned off = 0;
107 do
108 {
109 szTmp[off++] = uNum % 10 + '0';
110 uNum /= 10;
111 } while (uNum);
112
113 /* Copy it out to the destination buffer in the right order and add a terminator: */
114 while (off-- > 0)
115 *pszBuf++ = szTmp[off];
116 *pszBuf = '\0';
117 return pszBuf;
118}
119
120
121static void MyOutNum(unsigned uNum)
122{
123 char szTmp[32];
124 MyNumToString(szTmp, uNum);
125 MyOutStr(szTmp);
126}
127
128
129static void MyApiErrorAndQuit(PSZ pszOperation, USHORT rc)
130{
131 MyOutStr("Os2Util: error: ");
132 MyOutStr(pszOperation);
133 MyOutStr(" failed: ");
134 MyOutNum(rc);
135 MyOutStr("\r\n");
136 DosExit(EXIT_PROCESS, 0);
137}
138
139
140static HFILE OpenTeeFile(PSZ pszTeeToFile, BOOL fAppend, PSZ pszToWrite, USHORT cchToWrite)
141{
142 PMYBUFFER pBuf, pNext;
143 USHORT usIgnored;
144 USHORT usAction = 0;
145 HFILE hFile = -1;
146 USHORT rc;
147 rc = DosOpen(pszTeeToFile, &hFile, &usAction, 0 /*cbInitial*/, 0 /*fFileAttribs*/,
148 OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
149 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_SEQUENTIAL, 0 /*Reserved*/);
150 if (rc == NO_ERROR)
151 {
152
153 if (fAppend)
154 {
155 ULONG offNew = 0;
156 DosChgFilePtr(hFile, 0, FILE_END, &offNew);
157 }
158
159 /*
160 * Write out buffered data
161 */
162 pBuf = g_pBufferHead;
163 while (pBuf)
164 {
165 do
166 rc = DosWrite(hFile, pBuf->sz, pBuf->off, &usIgnored);
167 while (rc == ERROR_INTERRUPT);
168 pNext = pBuf->pNext;
169 DosFreeSeg((__segment)pBuf);
170 pBuf = pNext;
171 }
172 g_pBufferTail = g_pBufferHead = NULL;
173
174 /*
175 * Write the current output.
176 */
177 do
178 rc = DosWrite(hFile, pszToWrite, cchToWrite, &usIgnored);
179 while (rc == ERROR_INTERRUPT);
180 }
181 else
182 {
183 /*
184 * Failed to open the file. Buffer a bit in case the file can be
185 * opened later (like when we've formatted the disk).
186 */
187 pBuf = g_pBufferTail;
188 if (pBuf && pBuf->off < pBuf->cb)
189 {
190 USHORT cbToCopy = pBuf->cb - pBuf->off;
191 if (cbToCopy > cchToWrite)
192 cbToCopy = cchToWrite;
193 MyMemCopy(&pBuf->sz[pBuf->off], pszToWrite, cbToCopy);
194 pszToWrite += cbToCopy;
195 cchToWrite -= cbToCopy;
196 }
197 if (cchToWrite > 0)
198 {
199 USHORT uSel = 0xffff;
200 if ( g_cBuffers < 10
201 && (rc = DosAllocSeg(0 /*64KiB*/, &uSel, 0 /*fFlags*/)) == NO_ERROR)
202 {
203 pBuf = ((__segment)uSel) :> ((MYBUFFER __near *)0);
204 pBuf->pNext = NULL;
205 pBuf->cb = sizeof(pBuf->sz);
206 pBuf->off = cchToWrite;
207 MyMemCopy(&pBuf->sz[0], pszToWrite, cchToWrite);
208
209 if (g_pBufferTail)
210 g_pBufferTail->pNext = pBuf;
211 else
212 g_pBufferHead = pBuf;
213 g_pBufferTail = pBuf;
214 }
215 else if (g_cBuffers > 0)
216 {
217 pBuf = g_pBufferHead;
218 pBuf->off = cchToWrite;
219 MyMemCopy(&pBuf->sz[0], pszToWrite, cchToWrite);
220
221 if (g_pBufferTail != pBuf)
222 {
223 g_pBufferHead = pBuf->pNext;
224 pBuf->pNext = NULL;
225 g_pBufferTail->pNext = pBuf;
226 g_pBufferTail = pBuf;
227 }
228 }
229 }
230 hFile = -1;
231 }
232 return hFile;
233}
234
235
236static void ShowUsageAndQuit(void)
237{
238 static char s_szHelp[] =
239 "Os2Util.exe is tiny helper utility that implements TEE'ing to the VBox release log,\r\n"
240 "files and shows the actual exit code of a program. Standard error and output will\r\n"
241 "be merged into one for simplicity reasons.\r\n"
242 "\r\n"
243 "usage: Os2Util.exe [-a|--append] [-f<filename>|--tee-to-file <filename>] \\\r\n"
244 " [-b|--tee-to-backdoor] -- <prog> [args]\r\n"
245 " or Os2Util.exe <-w<msg>|--write-backdoor <msg>> \r\n";
246 USHORT usIgnored;
247 DosWrite(g_hStdErr, s_szHelp, sizeof(s_szHelp) - 1, &usIgnored);
248 DosExit(EXIT_PROCESS, 0);
249}
250
251
252/**
253 * Gets the an option value.
254 *
255 * The option value string will be terminated.
256 */
257static PSZ MyGetOptValue(PSZ psz, PSZ pszOption, PSZ *ppszValue)
258{
259 CHAR ch;
260 while ((ch = *psz) != '\0' && IS_BLANK(ch))
261 psz++;
262 if (*psz == '\0')
263 {
264 USHORT usIgnored;
265 DosWrite(g_hStdErr, RT_STR_TUPLE("Os2Util: syntax error: Option '"), &usIgnored);
266 DosWrite(g_hStdErr, pszOption, MyStrLen(pszOption), &usIgnored);
267 DosWrite(g_hStdErr, RT_STR_TUPLE("' takes a value\r\n"), &usIgnored);
268 DosExit(EXIT_PROCESS, 2);
269 }
270
271 *ppszValue = psz;
272
273 while ((ch = *psz) != '\0' && !IS_BLANK(ch))
274 psz++;
275 if (ch != '\0')
276 *psz++ = '\0';
277 return psz;
278}
279
280
281/**
282 * Checks if @a pszOption matches @a *ppsz, advance *ppsz if TRUE.
283 */
284static BOOL MyMatchLongOption(PSZ _far *ppsz, PSZ pszOption, unsigned cchOption)
285{
286 /* Match option and command line strings: */
287 PSZ psz = *ppsz;
288 while (cchOption-- > 0)
289 {
290 if (*psz != *pszOption)
291 return FALSE;
292 psz++;
293 pszOption++;
294 }
295
296 /* Is this the end of a word on the command line? */
297 if (*psz == '\0')
298 *ppsz = psz;
299 else if (IS_BLANK(*psz))
300 *ppsz = psz + 1;
301 else
302 return FALSE;
303 return TRUE;
304}
305
306
307/**
308 * The entrypoint (no crt).
309 */
310#pragma aux Os2UtilMain "_*" parm caller [ ax ] [ bx ];
311void Os2UtilMain(USHORT uSelEnv, USHORT offCmdLine)
312{
313 PSZ pszzEnv = ((__segment)uSelEnv) :> ((char _near *)0);
314 PSZ pszzCmdLine = ((__segment)uSelEnv) :> ((char _near *)offCmdLine);
315 USHORT uExitCode = 1;
316 BOOL fTeeToBackdoor = FALSE;
317 BOOL fAppend = FALSE;
318 PSZ pszTeeToFile = NULL;
319 HFILE hTeeToFile = -1;
320 HFILE hPipeRead = -1;
321 PSZ pszzNewCmdLine;
322 PSZ psz;
323 CHAR ch;
324 USHORT usIgnored;
325 USHORT rc;
326 RESULTCODES ResultCodes = { 0xffff, 0xffff };
327 CHAR szBuf[512];
328
329 /*
330 * Parse the command line.
331 * Note! We do not accept any kind of quoting.
332 */
333 /* Skip the executable filename: */
334 psz = pszzCmdLine;
335 while (*psz != '\0')
336 psz++;
337 psz++;
338
339 /* Now parse arguments. */
340 while ((ch = *psz) != '\0')
341 {
342 if (IS_BLANK(ch))
343 psz++;
344 else if (ch != '-')
345 break;
346 else
347 {
348 PSZ const pszOptStart = psz;
349 ch = *++psz;
350 if (ch == '-')
351 {
352 ch = *++psz;
353 if (IS_BLANK(ch))
354 {
355 /* Found end-of-arguments marker "--" */
356 psz++;
357 break;
358 }
359 if (ch == 'a' && MyMatchLongOption(&psz, RT_STR_TUPLE("append")))
360 fAppend = TRUE;
361 else if (ch == 'h' && MyMatchLongOption(&psz, RT_STR_TUPLE("help")))
362 ShowUsageAndQuit();
363 else if (ch == 't' && MyMatchLongOption(&psz, RT_STR_TUPLE("tee-to-backdoor")))
364 g_fOutputToBackdoor = fTeeToBackdoor = TRUE;
365 else if (ch == 't' && MyMatchLongOption(&psz, RT_STR_TUPLE("tee-to-file")))
366 psz = MyGetOptValue(psz, "--tee-to-file", &pszTeeToFile);
367 else if (ch == 'w' && MyMatchLongOption(&psz, RT_STR_TUPLE("write-backdoor")))
368 {
369 VBoxBackdoorPrint(psz, MyStrLen(psz));
370 VBoxBackdoorPrint("\n", 1);
371 DosExit(EXIT_PROCESS, 0);
372 }
373 else
374 {
375 DosWrite(g_hStdErr, RT_STR_TUPLE("Os2util: syntax error: "), &usIgnored);
376 DosWrite(g_hStdErr, pszOptStart, MyStrLen(pszOptStart), &usIgnored);
377 DosWrite(g_hStdErr, RT_STR_TUPLE("\r\n"), &usIgnored);
378 DosExit(EXIT_PROCESS, 2);
379 }
380 }
381 else
382 {
383 do
384 {
385 if (ch == 'a')
386 fAppend = TRUE;
387 else if (ch == 'b')
388 g_fOutputToBackdoor = fTeeToBackdoor = TRUE;
389 else if (ch == 'f')
390 {
391 psz = MyGetOptValue(psz + 1, "-f", &pszTeeToFile);
392 break;
393 }
394 else if (ch == 'w')
395 {
396 psz++;
397 VBoxBackdoorPrint(psz, MyStrLen(psz));
398 VBoxBackdoorPrint("\n", 1);
399 DosExit(EXIT_PROCESS, 0);
400 }
401 else if (ch == '?' || ch == 'h' || ch == 'H')
402 ShowUsageAndQuit();
403 else
404 {
405 DosWrite(g_hStdErr, RT_STR_TUPLE("Os2util: syntax error: "), &usIgnored);
406 if (ch)
407 DosWrite(g_hStdErr, &ch, 1, &usIgnored);
408 else
409 DosWrite(g_hStdErr, RT_STR_TUPLE("lone dash"), &usIgnored);
410 DosWrite(g_hStdErr, RT_STR_TUPLE(" ("), &usIgnored);
411 DosWrite(g_hStdErr, pszOptStart, MyStrLen(pszOptStart), &usIgnored);
412 DosWrite(g_hStdErr, RT_STR_TUPLE(")\r\n"), &usIgnored);
413 DosExit(EXIT_PROCESS, 2);
414 }
415 ch = *++psz;
416 } while (!IS_BLANK(ch) && ch != '\0');
417 }
418 }
419 }
420
421 /*
422 * Zero terminate the executable name in the command line.
423 */
424 pszzNewCmdLine = psz;
425 if (ch == '\0')
426 {
427 DosWrite(g_hStdErr, RT_STR_TUPLE("Os2Util: syntax error: No program specified\r\n"), &usIgnored);
428 DosExit(EXIT_PROCESS, 2);
429 }
430 psz++;
431 while ((ch = *psz) != '\0' && !IS_BLANK(ch))
432 psz++;
433 *psz++ = '\0';
434
435 /*
436 * Prepare redirection.
437 */
438 if (fTeeToBackdoor || pszTeeToFile != NULL)
439 {
440 HFILE hPipeWrite = -1;
441 HFILE hDup;
442
443 /* Make new copies of the standard handles. */
444 hDup = 0xffff;
445 rc = DosDupHandle(g_hStdErr, &hDup);
446 if (rc != NO_ERROR)
447 MyApiErrorAndQuit("DosDupHandle(g_hStdErr, &hDup);", rc);
448 g_hStdErr = hDup;
449 DosSetFHandState(hDup, OPEN_FLAGS_NOINHERIT); /* not strictly necessary, so ignore errors */
450
451 hDup = 0xffff;
452 rc = DosDupHandle(g_hStdOut, &hDup);
453 if (rc != NO_ERROR)
454 MyApiErrorAndQuit("DosDupHandle(g_hStdOut, &hDup);", rc);
455 g_hStdOut = hDup;
456 DosSetFHandState(hDup, OPEN_FLAGS_NOINHERIT); /* not strictly necessary, so ignore errors */
457
458 /* Create the pipe and make the read-end non-inheritable (we'll hang otherwise). */
459 rc = DosMakePipe(&hPipeRead, &hPipeWrite, 0 /*default size*/);
460 if (rc != NO_ERROR)
461 MyApiErrorAndQuit("DosMakePipe", rc);
462
463 rc = DosSetFHandState(hPipeRead, OPEN_FLAGS_NOINHERIT);
464 if (rc != NO_ERROR)
465 MyApiErrorAndQuit("DosSetFHandState(hPipeRead, OPEN_FLAGS_NOINHERIT)", rc);
466
467 /* Replace standard output and standard error with the write end of the pipe. */
468 hDup = 1;
469 rc = DosDupHandle(hPipeWrite, &hDup);
470 if (rc != NO_ERROR)
471 MyApiErrorAndQuit("DosDupHandle(hPipeWrite, &hDup[=1]);", rc);
472
473 hDup = 2;
474 rc = DosDupHandle(hPipeWrite, &hDup);
475 if (rc != NO_ERROR)
476 MyApiErrorAndQuit("DosDupHandle(hPipeWrite, &hDup[=2]);", rc);
477
478 /* We can close the write end of the pipe as we don't need the original handle any more. */
479 DosClose(hPipeWrite);
480 }
481
482 /*
483 * Execute the program.
484 */
485 szBuf[0] = '\0';
486 rc = DosExecPgm(szBuf, sizeof(szBuf), hPipeRead == -1 ? EXEC_SYNC : EXEC_ASYNCRESULT,
487 pszzNewCmdLine, pszzEnv, &ResultCodes, pszzNewCmdLine);
488 if (rc != NO_ERROR)
489 {
490 MyOutStr("Os2Util: error: DosExecPgm failed for \"");
491 MyOutStr(pszzNewCmdLine);
492 MyOutStr("\": ");
493 MyOutNum(rc);
494 if (szBuf[0])
495 {
496 MyOutStr(" ErrObj=");
497 szBuf[sizeof(szBuf) - 1] = '\0';
498 MyOutStr(szBuf);
499 }
500 MyOutStr("\r\n");
501 DosExit(EXIT_PROCESS, 1);
502 }
503
504 /*
505 * Wait for the child process to complete.
506 */
507 if (hPipeRead != -1)
508 {
509 /* Save the child pid. */
510 PID const pidChild = ResultCodes.codeTerminate;
511 PID pidIgnored;
512
513 /* Close the write handles or we'll hang in the read loop. */
514 DosClose(1);
515 DosClose(2);
516
517 /* Disable hard error popups (file output to unformatted disks). */
518 DosError(2 /* only exceptions */);
519
520
521 /*
522 * Read the pipe and tee it to the desired outputs
523 */
524 for (;;)
525 {
526 USHORT cbRead = 0;
527 rc = DosRead(hPipeRead, szBuf, sizeof(szBuf), &cbRead);
528 if (rc == NO_ERROR)
529 {
530 if (cbRead == 0)
531 break; /* No more writers. */
532
533 /* Standard output: */
534 do
535 rc = DosWrite(g_hStdOut, szBuf, cbRead, &usIgnored);
536 while (rc == ERROR_INTERRUPT);
537
538 /* Backdoor: */
539 if (fTeeToBackdoor)
540 VBoxBackdoorPrint(psz, cbRead);
541
542 /* File: */
543 if (hTeeToFile != -1)
544 do
545 rc = DosWrite(hTeeToFile, szBuf, cbRead, &usIgnored);
546 while (rc == ERROR_INTERRUPT);
547 else if (pszTeeToFile != NULL)
548 hTeeToFile = OpenTeeFile(pszTeeToFile, fAppend, szBuf, cbRead);
549 }
550 else if (rc == ERROR_BROKEN_PIPE)
551 break;
552 else
553 {
554 MyOutStr("Os2Util: error: Error reading pipe: ");
555 MyOutNum(rc);
556 MyOutStr("\r\n");
557 break;
558 }
559 }
560
561 DosClose(hPipeRead);
562 rc = DosCwait(DCWA_PROCESS, DCWW_WAIT, &ResultCodes, &pidIgnored, pidChild);
563 if (rc != NO_ERROR)
564 {
565 MyOutStr("Os2Util: error: DosCwait(DCWA_PROCESS,DCWW_WAIT,,,");
566 MyOutNum(pidChild);
567 MyOutStr(") failed: ");
568 MyOutNum(rc);
569 MyOutStr("\r\n");
570 }
571 }
572
573 /*
574 * Report the status code and quit.
575 */
576 MyOutStr("Os2Util: Child: ");
577 MyOutStr(pszzNewCmdLine);
578 MyOutStr(" ");
579 MyOutStr(psz);
580 MyOutStr("\r\n"
581 "Os2Util: codeTerminate=");
582 MyOutNum(ResultCodes.codeTerminate);
583 MyOutStr(" codeResult=");
584 MyOutNum(ResultCodes.codeResult);
585 MyOutStr("\r\n");
586
587 for (;;)
588 DosExit(EXIT_PROCESS, ResultCodes.codeTerminate == 0 ? ResultCodes.codeResult : 127);
589}
590
591
592/**
593 * Backdoor print function living in an IOPL=2 segment.
594 */
595#pragma code_seg("IOPL", "CODE")
596void __far VBoxBackdoorPrint(PSZ psz, unsigned cch)
597{
598 ASMOutStrU8(RTLOG_DEBUG_PORT, psz, cch);
599}
600
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