VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxBugReport/VBoxBugReport.cpp@ 98419

Last change on this file since 98419 was 98320, checked in by vboxsync, 23 months ago

FE/VBoxBugReport: Move away from deprecated RTTar* API and use the VFS implementation instead, allows to skip creation of the temporary archive before compression

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.9 KB
Line 
1/* $Id: VBoxBugReport.cpp 98320 2023-01-26 15:50:47Z vboxsync $ */
2/** @file
3 * VBoxBugReport - VirtualBox command-line diagnostics tool, main file.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29#include <VBox/com/com.h>
30#include <VBox/com/string.h>
31#include <VBox/com/array.h>
32#include <VBox/com/ErrorInfo.h>
33#include <VBox/com/errorprint.h>
34#include <VBox/com/VirtualBox.h>
35
36#include <VBox/version.h>
37
38#include <iprt/buildconfig.h>
39#include <iprt/err.h>
40#include <iprt/env.h>
41#include <iprt/file.h>
42#include <iprt/getopt.h>
43#include <iprt/initterm.h>
44#include <iprt/path.h>
45#include <iprt/process.h>
46#include <iprt/zip.h>
47#include <iprt/cpp/exception.h>
48
49#include <list>
50
51#include "VBoxBugReport.h"
52
53/* Implementation - Base */
54
55#ifndef RT_OS_WINDOWS
56/** @todo Replace with platform-specific implementations. */
57void createBugReportOsSpecific(BugReport *pReport, const char *pszHome)
58{
59 RT_NOREF(pReport, pszHome);
60}
61#endif /* !RT_OS_WINDOWS */
62
63
64/* Globals */
65
66static char *g_pszVBoxManage = NULL;
67
68static const RTGETOPTDEF g_aOptions[] =
69{
70 { "-all", 'A', RTGETOPT_REQ_NOTHING },
71 { "--all", 'A', RTGETOPT_REQ_NOTHING },
72 { "-output", 'o', RTGETOPT_REQ_STRING },
73 { "--output", 'o', RTGETOPT_REQ_STRING },
74 { "-text", 't', RTGETOPT_REQ_NOTHING },
75 { "--text", 't', RTGETOPT_REQ_NOTHING }
76};
77
78static const char g_szUsage[] =
79 "Usage: %s [-h|-?|--help] [-A|--all|<vmname>...] [-o <file>|--output=<file>]\n"
80 " Several VM names can be specified at once to be included into single report.\n"
81 " If none is given then no machines will be included. Specifying -A overrides\n"
82 " any VM names provided and includes all registered machines.\n"
83 "Options:\n"
84 " -h, -help, --help Print usage information\n"
85 " -A, -all, --all Include all registered machines\n"
86 " -o, -output, --output Specifies the name of the output file\n"
87 " -t, -text, --text Produce a single text file instead of compressed TAR\n"
88 " -V, -version, --version Print version information\n"
89 "\n";
90
91
92/*
93 * This class stores machine-specific file paths that are obtained via
94 * VirtualBox API. In case API is not functioning properly these paths
95 * will be deduced on the best effort basis.
96 */
97class MachineInfo
98{
99public:
100 MachineInfo(const char *name, const char *logFolder, const char *settingsFile);
101 ~MachineInfo();
102 const char *getName() const { return m_name; };
103 const char *getLogPath() const { return m_logpath; };
104 const char *getSettingsFile() const { return m_settings; };
105private:
106 char *m_name;
107 char *m_logpath;
108 char *m_settings;
109};
110
111MachineInfo::MachineInfo(const char *name, const char *logFolder, const char *settingsFile)
112{
113 m_name = RTStrDup(name);
114 m_logpath = RTStrDup(logFolder);
115 m_settings = RTStrDup(settingsFile);
116}
117
118MachineInfo::~MachineInfo()
119{
120 RTStrFree(m_logpath);
121 RTStrFree(m_name);
122 RTStrFree(m_settings);
123 m_logpath = m_name = m_settings = 0;
124}
125
126typedef std::list<MachineInfo*> MachineInfoList;
127
128
129class VBRDir
130{
131public:
132 VBRDir(const char *pcszPath) : m_hDir(NIL_RTDIR)
133 {
134 int rc = RTDirOpenFiltered(&m_hDir, pcszPath, RTDIRFILTER_WINNT, 0);
135 if (RT_FAILURE(rc) && rc != VERR_FILE_NOT_FOUND && rc != VERR_PATH_NOT_FOUND)
136 throw RTCError(com::Utf8StrFmt("Failed to open directory '%s'\n", pcszPath));
137 };
138 ~VBRDir()
139 {
140 if (RT_VALID_PTR(m_hDir))
141 {
142 int rc = RTDirClose(m_hDir);
143 AssertRC(rc);
144 }
145 };
146 const char *next(void)
147 {
148 if (!RT_VALID_PTR(m_hDir))
149 return NULL;
150
151 int rc = RTDirRead(m_hDir, &m_DirEntry, NULL);
152 if (RT_SUCCESS(rc))
153 return m_DirEntry.szName;
154 if (rc == VERR_NO_MORE_FILES)
155 return NULL;
156 throw RTCError("Failed to read directory element\n");
157 };
158
159private:
160 RTDIR m_hDir;
161 RTDIRENTRY m_DirEntry;
162};
163
164
165BugReportFilter::BugReportFilter() : m_pvBuffer(0), m_cbBuffer(0)
166{
167}
168
169BugReportFilter::~BugReportFilter()
170{
171 if (m_pvBuffer)
172 RTMemFree(m_pvBuffer);
173}
174
175void *BugReportFilter::allocateBuffer(size_t cbNeeded)
176{
177 if (m_pvBuffer)
178 {
179 if (cbNeeded > m_cbBuffer)
180 RTMemFree(m_pvBuffer);
181 else
182 return m_pvBuffer;
183 }
184 m_pvBuffer = RTMemAlloc(cbNeeded);
185 if (!m_pvBuffer)
186 throw RTCError(com::Utf8StrFmt("Failed to allocate %ld bytes\n", cbNeeded));
187 m_cbBuffer = cbNeeded;
188 return m_pvBuffer;
189}
190
191
192/*
193 * An abstract class serving as the root of the bug report item tree.
194 */
195BugReportItem::BugReportItem(const char *pszTitle)
196{
197 m_pszTitle = RTStrDup(pszTitle);
198 m_filter = 0;
199}
200
201BugReportItem::~BugReportItem()
202{
203 if (m_filter)
204 delete m_filter;
205 RTStrFree(m_pszTitle);
206}
207
208void BugReportItem::addFilter(BugReportFilter *filter)
209{
210 m_filter = filter;
211}
212
213void *BugReportItem::applyFilter(void *pvSource, size_t *pcbInOut)
214{
215 if (m_filter)
216 return m_filter->apply(pvSource, pcbInOut);
217 return pvSource;
218}
219
220const char * BugReportItem::getTitle(void)
221{
222 return m_pszTitle;
223}
224
225
226BugReport::BugReport(const char *pszFileName)
227{
228 m_pszFileName = RTStrDup(pszFileName);
229}
230
231BugReport::~BugReport()
232{
233 for (unsigned i = 0; i < m_Items.size(); ++i)
234 {
235 delete m_Items[i];
236 }
237 RTStrFree(m_pszFileName);
238}
239
240int BugReport::getItemCount(void)
241{
242 return (int)m_Items.size();
243}
244
245void BugReport::addItem(BugReportItem* item, BugReportFilter *filter)
246{
247 if (filter)
248 item->addFilter(filter);
249 if (item)
250 m_Items.append(item);
251}
252
253void BugReport::process(void)
254{
255 for (unsigned i = 0; i < m_Items.size(); ++i)
256 {
257 BugReportItem *pItem = m_Items[i];
258 RTPrintf("%3u%% - collecting %s...\n", i * 100 / m_Items.size(), pItem->getTitle());
259 processItem(pItem);
260 }
261 RTPrintf("100%% - compressing...\n\n");
262}
263
264void *BugReport::applyFilters(BugReportItem* item, void *pvSource, size_t *pcbInOut)
265{
266 return item->applyFilter(pvSource, pcbInOut);
267}
268
269
270BugReportStream::BugReportStream(const char *pszTitle) : BugReportItem(pszTitle)
271{
272 m_hVfsIos = NIL_RTVFSIOSTREAM;
273 handleRtError(RTPathTemp(m_szFileName, RTPATH_MAX),
274 "Failed to obtain path to temporary folder");
275 handleRtError(RTPathAppend(m_szFileName, RTPATH_MAX, "BugRepXXXXX.tmp"),
276 "Failed to append path");
277 handleRtError(RTFileCreateTemp(m_szFileName, 0600),
278 "Failed to create temporary file '%s'", m_szFileName);
279 handleRtError(RTVfsIoStrmOpenNormal(m_szFileName, RTFILE_O_OPEN | RTFILE_O_WRITE | RTFILE_O_DENY_NONE, &m_hVfsIos),
280 "Failed to open '%s'", m_szFileName);
281}
282
283BugReportStream::~BugReportStream()
284{
285 if (m_hVfsIos != NIL_RTVFSIOSTREAM)
286 RTVfsIoStrmRelease(m_hVfsIos);
287 RTFileDelete(m_szFileName);
288}
289
290int BugReportStream::printf(const char *pszFmt, ...)
291{
292 va_list va;
293 va_start(va, pszFmt);
294 int cb = RTVfsIoStrmPrintfV(m_hVfsIos, pszFmt, va);
295 va_end(va);
296 return cb;
297}
298
299int BugReportStream::putStr(const char *pszString)
300{
301 return RTVfsIoStrmPrintf(m_hVfsIos, "%s", pszString);
302}
303
304RTVFSIOSTREAM BugReportStream::getStream(void)
305{
306 RTVfsIoStrmRelease(m_hVfsIos);
307 handleRtError(RTVfsIoStrmOpenNormal(m_szFileName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &m_hVfsIos),
308 "Failed to open '%s'", m_szFileName);
309 return m_hVfsIos;
310}
311
312
313/* Implementation - Generic */
314
315BugReportFile::BugReportFile(const char *pszPath, const char *pszShortName) : BugReportItem(pszShortName)
316{
317 m_hVfsIos = NIL_RTVFSIOSTREAM;
318 m_pszPath = RTStrDup(pszPath);
319}
320
321BugReportFile::~BugReportFile()
322{
323 if (m_hVfsIos != NIL_RTVFSIOSTREAM)
324 RTVfsIoStrmRelease(m_hVfsIos);
325 if (m_pszPath)
326 RTStrFree(m_pszPath);
327}
328
329RTVFSIOSTREAM BugReportFile::getStream(void)
330{
331 handleRtError(RTVfsIoStrmOpenNormal(m_pszPath, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &m_hVfsIos),
332 "Failed to open '%s'", m_pszPath);
333 return m_hVfsIos;
334}
335
336
337BugReportCommand::BugReportCommand(const char *pszTitle, const char *pszExec, ...)
338 : BugReportItem(pszTitle), m_hVfsIos(NIL_RTVFSIOSTREAM)
339{
340 unsigned cArgs = 0;
341 m_papszArgs[cArgs++] = RTStrDup(pszExec);
342
343 const char *pszArg;
344 va_list va;
345 va_start(va, pszExec);
346 do
347 {
348 if (cArgs >= RT_ELEMENTS(m_papszArgs))
349 {
350 va_end(va);
351 throw RTCError(com::Utf8StrFmt("Too many arguments (%u > %u)\n", cArgs+1, RT_ELEMENTS(m_papszArgs)));
352 }
353 pszArg = va_arg(va, const char *);
354 m_papszArgs[cArgs++] = pszArg ? RTStrDup(pszArg) : NULL;
355 } while (pszArg);
356 va_end(va);
357}
358
359BugReportCommand::~BugReportCommand()
360{
361 if (m_hVfsIos != NIL_RTVFSIOSTREAM)
362 RTVfsIoStrmRelease(m_hVfsIos);
363 RTFileDelete(m_szFileName);
364 for (size_t i = 0; i < RT_ELEMENTS(m_papszArgs) && m_papszArgs[i]; ++i)
365 RTStrFree(m_papszArgs[i]);
366}
367
368RTVFSIOSTREAM BugReportCommand::getStream(void)
369{
370 handleRtError(RTPathTemp(m_szFileName, RTPATH_MAX),
371 "Failed to obtain path to temporary folder");
372 handleRtError(RTPathAppend(m_szFileName, RTPATH_MAX, "BugRepXXXXX.tmp"),
373 "Failed to append path");
374 handleRtError(RTFileCreateTemp(m_szFileName, 0600),
375 "Failed to create temporary file '%s'", m_szFileName);
376
377 RTHANDLE hStdOutErr;
378 hStdOutErr.enmType = RTHANDLETYPE_FILE;
379 handleRtError(RTFileOpen(&hStdOutErr.u.hFile, m_szFileName,
380 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE),
381 "Failed to open temporary file '%s'", m_szFileName);
382
383 RTPROCESS hProcess;
384 handleRtError(RTProcCreateEx(m_papszArgs[0], m_papszArgs, RTENV_DEFAULT, 0,
385 NULL, &hStdOutErr, &hStdOutErr,
386 NULL, NULL, NULL, &hProcess),
387 "Failed to create process '%s'", m_papszArgs[0]);
388 RTPROCSTATUS status;
389 handleRtError(RTProcWait(hProcess, RTPROCWAIT_FLAGS_BLOCK, &status),
390 "Process wait failed");
391 //if (status.enmReason == RTPROCEXITREASON_NORMAL) {}
392 RTFileClose(hStdOutErr.u.hFile);
393
394 handleRtError(RTVfsIoStrmOpenNormal(m_szFileName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &m_hVfsIos),
395 "Failed to open '%s'", m_szFileName);
396 return m_hVfsIos;
397}
398
399
400BugReportCommandTemp::BugReportCommandTemp(const char *pszTitle, const char *pszExec, ...)
401 : BugReportItem(pszTitle), m_hVfsIos(NIL_RTVFSIOSTREAM)
402{
403 handleRtError(RTPathTemp(m_szFileName, RTPATH_MAX),
404 "Failed to obtain path to temporary folder");
405 handleRtError(RTPathAppend(m_szFileName, RTPATH_MAX, "BugRepXXXXX.tmp"),
406 "Failed to append path");
407 handleRtError(RTFileCreateTemp(m_szFileName, 0600),
408 "Failed to create temporary file '%s'", m_szFileName);
409
410 unsigned cArgs = 0;
411 m_papszArgs[cArgs++] = RTStrDup(pszExec);
412
413 const char *pszArg;
414 va_list va;
415 va_start(va, pszExec);
416 do
417 {
418 if (cArgs >= RT_ELEMENTS(m_papszArgs) - 1)
419 {
420 va_end(va);
421 throw RTCError(com::Utf8StrFmt("Too many arguments (%u > %u)\n", cArgs+1, RT_ELEMENTS(m_papszArgs)));
422 }
423 pszArg = va_arg(va, const char *);
424 m_papszArgs[cArgs++] = RTStrDup(pszArg ? pszArg : m_szFileName);
425 } while (pszArg);
426 va_end(va);
427
428 m_papszArgs[cArgs++] = NULL;
429}
430
431BugReportCommandTemp::~BugReportCommandTemp()
432{
433 if (m_hVfsIos != NIL_RTVFSIOSTREAM)
434 RTVfsIoStrmRelease(m_hVfsIos);
435 RTFileDelete(m_szErrFileName);
436 RTFileDelete(m_szFileName);
437 for (size_t i = 0; i < RT_ELEMENTS(m_papszArgs) && m_papszArgs[i]; ++i)
438 RTStrFree(m_papszArgs[i]);
439}
440
441RTVFSIOSTREAM BugReportCommandTemp::getStream(void)
442{
443 handleRtError(RTPathTemp(m_szErrFileName, RTPATH_MAX),
444 "Failed to obtain path to temporary folder");
445 handleRtError(RTPathAppend(m_szErrFileName, RTPATH_MAX, "BugRepErrXXXXX.tmp"),
446 "Failed to append path");
447 handleRtError(RTFileCreateTemp(m_szErrFileName, 0600),
448 "Failed to create temporary file '%s'", m_szErrFileName);
449
450 RTHANDLE hStdOutErr;
451 hStdOutErr.enmType = RTHANDLETYPE_FILE;
452 handleRtError(RTFileOpen(&hStdOutErr.u.hFile, m_szErrFileName,
453 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE),
454 "Failed to open temporary file '%s'", m_szErrFileName);
455
456 /* Remove the output file to prevent errors or confirmation prompts */
457 handleRtError(RTFileDelete(m_szFileName),
458 "Failed to delete temporary file '%s'", m_szFileName);
459
460 RTPROCESS hProcess;
461 handleRtError(RTProcCreateEx(m_papszArgs[0], m_papszArgs, RTENV_DEFAULT, 0,
462 NULL, &hStdOutErr, &hStdOutErr,
463 NULL, NULL, NULL, &hProcess),
464 "Failed to create process '%s'", m_papszArgs[0]);
465 RTPROCSTATUS status;
466 handleRtError(RTProcWait(hProcess, RTPROCWAIT_FLAGS_BLOCK, &status),
467 "Process wait failed");
468 RTFileClose(hStdOutErr.u.hFile);
469
470 if (status.enmReason == RTPROCEXITREASON_NORMAL && status.iStatus == 0)
471 handleRtError(RTVfsIoStrmOpenNormal(m_szFileName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &m_hVfsIos),
472 "Failed to open '%s'", m_szFileName);
473 else
474 handleRtError(RTVfsIoStrmOpenNormal(m_szErrFileName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &m_hVfsIos),
475 "Failed to open '%s'", m_szErrFileName);
476 return m_hVfsIos;
477}
478
479
480BugReportText::BugReportText(const char *pszFileName) : BugReport(pszFileName)
481{
482 handleRtError(RTStrmOpen(pszFileName, "w", &m_StrmTxt),
483 "Failed to open '%s'", pszFileName);
484}
485
486BugReportText::~BugReportText()
487{
488 if (m_StrmTxt)
489 RTStrmClose(m_StrmTxt);
490}
491
492void BugReportText::processItem(BugReportItem* item)
493{
494 int cb = RTStrmPrintf(m_StrmTxt, "[ %s ] -------------------------------------------\n", item->getTitle());
495 if (!cb)
496 throw RTCError(com::Utf8StrFmt("Write failure (cb=%d)\n", cb));
497
498 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
499 try
500 {
501 hVfsIos = item->getStream();
502 }
503 catch (RTCError &e)
504 {
505 hVfsIos = NIL_RTVFSIOSTREAM;
506 RTStrmPutStr(m_StrmTxt, e.what());
507 }
508
509 int rc = VINF_SUCCESS;
510
511 if (hVfsIos != NIL_RTVFSIOSTREAM)
512 {
513 char buf[64*1024];
514 size_t cbRead, cbWritten;
515 cbRead = cbWritten = 0;
516 while (RT_SUCCESS(rc = RTVfsIoStrmRead(hVfsIos, buf, sizeof(buf), true /*fBlocking*/, &cbRead)) && cbRead)
517 {
518 rc = RTStrmWriteEx(m_StrmTxt, applyFilters(item, buf, &cbRead), cbRead, &cbWritten);
519 if (RT_FAILURE(rc) || cbRead != cbWritten)
520 throw RTCError(com::Utf8StrFmt("Write failure (rc=%d, cbRead=%lu, cbWritten=%lu)\n",
521 rc, cbRead, cbWritten));
522 }
523 }
524
525 handleRtError(RTStrmPutCh(m_StrmTxt, '\n'), "Write failure");
526}
527
528
529BugReportTarGzip::BugReportTarGzip(const char *pszFileName)
530 : BugReport(pszFileName), m_hTarFss(NIL_RTVFSFSSTREAM)
531{
532 VfsIoStreamHandle hVfsOut;
533 handleRtError(RTVfsIoStrmOpenNormal(pszFileName, RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE,
534 hVfsOut.getPtr()),
535 "Failed to create output file '%s'", pszFileName);
536 handleRtError(RTZipGzipCompressIoStream(hVfsOut.get(), 0, 6, m_hVfsGzip.getPtr()),
537 "Failed to create compressed stream for '%s'", pszFileName);
538
539 int rc = RTZipTarFsStreamToIoStream(m_hVfsGzip.get(), RTZIPTARFORMAT_DEFAULT, RTZIPTAR_C_SPARSE, &m_hTarFss);
540 handleRtError(rc, "Failed to create TAR file '%s'", m_szTarName);
541}
542
543BugReportTarGzip::~BugReportTarGzip()
544{
545 if (m_hTarFss != NIL_RTVFSFSSTREAM)
546 RTVfsFsStrmRelease(m_hTarFss);
547}
548
549void BugReportTarGzip::dumpExceptionToArchive(RTCString &strTarFile, RTCError &e)
550{
551 RTVFSFILE hVfsFile;
552 int rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, _1K/*cbEstimate*/, &hVfsFile);
553 if (RT_SUCCESS(rc))
554 {
555 rc = RTVfsFileWrite(hVfsFile, e.what(), RTStrNLen(e.what(), 1024), NULL /*pcbWritten*/);
556 if (RT_SUCCESS(rc))
557 {
558 rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
559 if (RT_SUCCESS(rc))
560 {
561 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFile);
562 rc = RTVfsFsStrmAdd(m_hTarFss, strTarFile.c_str(), hVfsObj, 0 /*fFlags*/);
563 RTVfsObjRelease(hVfsObj);
564 }
565 }
566 RTVfsFileRelease(hVfsFile);
567 }
568 handleRtError(rc, "Failed to add exception text to TAR archive '%s'", m_szTarName);
569}
570
571void BugReportTarGzip::processItem(BugReportItem* item)
572{
573 /*
574 * @todo Our TAR implementation does not support names larger than 100 characters.
575 * We truncate the title to make sure it will fit into 100-character field of TAR header.
576 */
577 RTCString strTarFile = RTCString(item->getTitle()).substr(0, RTStrNLen(item->getTitle(), 99));
578 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
579 try
580 {
581 hVfsIos = item->getStream();
582 }
583 catch (RTCError &e)
584 {
585 hVfsIos = NIL_RTVFSIOSTREAM;
586 dumpExceptionToArchive(strTarFile, e);
587 }
588
589 if (hVfsIos != NIL_RTVFSIOSTREAM)
590 {
591 RTVFSOBJ hVfsObjIos = RTVfsObjFromIoStream(hVfsIos);
592 int rc = RTVfsFsStrmAdd(m_hTarFss, strTarFile.c_str(), hVfsObjIos, 0 /*fFlags*/);
593 RTVfsObjRelease(hVfsObjIos);
594 handleRtError(rc, "Failed to add file to TAR archive '%s'", m_szTarName);
595 }
596}
597
598void BugReportTarGzip::complete(void)
599{
600 if (m_hTarFss != NIL_RTVFSFSSTREAM)
601 {
602 RTVfsFsStrmRelease(m_hTarFss);
603 m_hTarFss = NIL_RTVFSFSSTREAM;
604 }
605 handleRtError(RTVfsIoStrmFlush(m_hVfsGzip.get()), "Failed to flush output stream");
606 m_hVfsGzip.release();
607}
608
609
610/* Implementation - Main */
611
612void createBugReport(BugReport* report, const char *pszHome, MachineInfoList& machines)
613{
614 /* Collect all log files from VBoxSVC */
615 VBRDir HomeDir(PathJoin(pszHome, "VBoxSVC.log*"));
616 const char *pcszSvcLogFile = HomeDir.next();
617 while (pcszSvcLogFile)
618 {
619 report->addItem(new BugReportFile(PathJoin(pszHome, pcszSvcLogFile), pcszSvcLogFile));
620 pcszSvcLogFile = HomeDir.next();
621 }
622
623 report->addItem(new BugReportFile(PathJoin(pszHome, "VirtualBox.xml"), "VirtualBox.xml"));
624 report->addItem(new BugReportCommand("HostUsbDevices", g_pszVBoxManage, "list", "usbhost", NULL));
625 report->addItem(new BugReportCommand("HostUsbFilters", g_pszVBoxManage, "list", "usbfilters", NULL));
626 for (MachineInfoList::iterator it = machines.begin(); it != machines.end(); ++it)
627 {
628 VBRDir VmDir(PathJoin((*it)->getLogPath(), "VBox.log*"));
629 const char *pcszVmLogFile = VmDir.next();
630 while (pcszVmLogFile)
631 {
632 report->addItem(new BugReportFile(PathJoin((*it)->getLogPath(), pcszVmLogFile),
633 PathJoin((*it)->getName(), pcszVmLogFile)));
634 pcszVmLogFile = VmDir.next();
635 }
636 report->addItem(new BugReportFile((*it)->getSettingsFile(),
637 PathJoin((*it)->getName(), RTPathFilename((*it)->getSettingsFile()))));
638 report->addItem(new BugReportCommand(PathJoin((*it)->getName(), "GuestProperties"),
639 g_pszVBoxManage, "guestproperty", "enumerate",
640 (*it)->getName(), NULL));
641 }
642
643 createBugReportOsSpecific(report, pszHome);
644}
645
646void addMachine(MachineInfoList& list, ComPtr<IMachine> machine)
647{
648 BOOL fAccessible = FALSE;
649 HRESULT hrc = machine->COMGETTER(Accessible)(&fAccessible);
650 if (SUCCEEDED(hrc) && !fAccessible)
651 return
652 handleComError(hrc, "Failed to get accessible status of VM");
653
654 com::Bstr name, logFolder, settingsFile;
655 handleComError(machine->COMGETTER(Name)(name.asOutParam()),
656 "Failed to get VM name");
657 handleComError(machine->COMGETTER(LogFolder)(logFolder.asOutParam()),
658 "Failed to get VM log folder");
659 handleComError(machine->COMGETTER(SettingsFilePath)(settingsFile.asOutParam()),
660 "Failed to get VM settings file path");
661 list.push_back(new MachineInfo(com::Utf8Str(name).c_str(),
662 com::Utf8Str(logFolder).c_str(),
663 com::Utf8Str(settingsFile).c_str()));
664}
665
666
667static void printHeader(void)
668{
669 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Bug Report Tool " VBOX_VERSION_STRING "\n"
670 "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
671}
672
673int main(int argc, char *argv[])
674{
675 /*
676 * Initialize the VBox runtime without loading
677 * the support driver.
678 */
679 RTR3InitExe(argc, &argv, 0);
680
681 bool fAllMachines = false;
682 bool fTextOutput = false;
683 const char *pszOutputFile = NULL;
684 std::list<const char *> nameList;
685 RTGETOPTUNION ValueUnion;
686 RTGETOPTSTATE GetState;
687 int ret = RTGetOptInit(&GetState, argc, argv,
688 g_aOptions, RT_ELEMENTS(g_aOptions),
689 1 /* First */, 0 /*fFlags*/);
690 if (RT_FAILURE(ret))
691 return ret;
692 int ch;
693 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
694 {
695 switch(ch)
696 {
697 case 'h':
698 printHeader();
699 RTStrmPrintf(g_pStdErr, g_szUsage, argv[0]);
700 return 0;
701 case 'A':
702 fAllMachines = true;
703 break;
704 case 'o':
705 pszOutputFile = ValueUnion.psz;
706 break;
707 case 't':
708 fTextOutput = true;
709 break;
710 case 'V':
711 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
712 return 0;
713 case VINF_GETOPT_NOT_OPTION:
714 nameList.push_back(ValueUnion.psz);
715 break;
716 default:
717 return RTGetOptPrintError(ch, &ValueUnion);
718 }
719 }
720
721 printHeader();
722
723 HRESULT hr = S_OK;
724 char homeDir[RTPATH_MAX];
725 com::GetVBoxUserHomeDirectory(homeDir, sizeof(homeDir));
726
727 try
728 {
729 /* Figure out the full path to VBoxManage */
730 char szVBoxBin[RTPATH_MAX];
731 if (!RTProcGetExecutablePath(szVBoxBin, sizeof(szVBoxBin)))
732 throw RTCError("RTProcGetExecutablePath failed\n");
733 RTPathStripFilename(szVBoxBin);
734 g_pszVBoxManage = RTPathJoinA(szVBoxBin, VBOXMANAGE);
735 if (!g_pszVBoxManage)
736 throw RTCError("Out of memory\n");
737
738 handleComError(com::Initialize(VBOX_COM_INIT_F_DEFAULT | VBOX_COM_INIT_F_NO_COM_PATCHING), "Failed to initialize COM");
739
740 MachineInfoList list;
741
742 do
743 {
744 ComPtr<IVirtualBoxClient> virtualBoxClient;
745 ComPtr<IVirtualBox> virtualBox;
746 ComPtr<ISession> session;
747
748 hr = virtualBoxClient.createLocalObject(CLSID_VirtualBoxClient);
749 if (SUCCEEDED(hr))
750 hr = virtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
751 else
752 hr = virtualBox.createLocalObject(CLSID_VirtualBox);
753 if (FAILED(hr))
754 RTStrmPrintf(g_pStdErr, "WARNING: Failed to create the VirtualBox object (hr=0x%x)\n", hr);
755 else
756 {
757 hr = session.createInprocObject(CLSID_Session);
758 if (FAILED(hr))
759 RTStrmPrintf(g_pStdErr, "WARNING: Failed to create a session object (hr=0x%x)\n", hr);
760 }
761
762 if (SUCCEEDED(hr))
763 {
764 if (fAllMachines)
765 {
766 com::SafeIfaceArray<IMachine> machines;
767 hr = virtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines));
768 if (SUCCEEDED(hr))
769 {
770 for (size_t i = 0; i < machines.size(); ++i)
771 {
772 if (machines[i])
773 addMachine(list, machines[i]);
774 }
775 }
776 }
777 else
778 {
779 for ( std::list<const char *>::iterator it = nameList.begin(); it != nameList.end(); ++it)
780 {
781 ComPtr<IMachine> machine;
782 handleComError(virtualBox->FindMachine(com::Bstr(*it).raw(), machine.asOutParam()),
783 "No such machine '%s'", *it);
784 addMachine(list, machine);
785 }
786 }
787 }
788
789 }
790 while(0);
791
792 RTTIMESPEC TimeSpec;
793 RTTIME Time;
794 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
795 RTCStringFmt strOutFile("%04d-%02d-%02d-%02d-%02d-%02d-bugreport.%s",
796 Time.i32Year, Time.u8Month, Time.u8MonthDay,
797 Time.u8Hour, Time.u8Minute, Time.u8Second,
798 fTextOutput ? "txt" : "tgz");
799 RTCString strFallbackOutFile;
800 if (!pszOutputFile)
801 {
802 RTFILE tmp;
803 pszOutputFile = strOutFile.c_str();
804 int rc = RTFileOpen(&tmp, pszOutputFile, RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
805 if (rc == VERR_ACCESS_DENIED)
806 {
807 char szUserHome[RTPATH_MAX];
808 handleRtError(RTPathUserHome(szUserHome, sizeof(szUserHome)), "Failed to obtain home directory");
809 strFallbackOutFile.printf("%s/%s", szUserHome, strOutFile.c_str());
810 pszOutputFile = strFallbackOutFile.c_str();
811 }
812 else if (RT_SUCCESS(rc))
813 {
814 RTFileClose(tmp);
815 RTFileDelete(pszOutputFile);
816 }
817 }
818 BugReport *pReport;
819 if (fTextOutput)
820 pReport = new BugReportText(pszOutputFile);
821 else
822 pReport = new BugReportTarGzip(pszOutputFile);
823 createBugReport(pReport, homeDir, list);
824 pReport->process();
825 pReport->complete();
826 RTPrintf("Report was written to '%s'\n", pszOutputFile);
827 delete pReport;
828 }
829 catch (RTCError &e)
830 {
831 RTStrmPrintf(g_pStdErr, "ERROR: %s\n", e.what());
832 }
833
834 com::Shutdown();
835
836 if (g_pszVBoxManage)
837 RTStrFree(g_pszVBoxManage);
838
839 return SUCCEEDED(hr) ? 0 : 1;
840}
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