VirtualBox

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

Last change on this file since 105266 was 99775, checked in by vboxsync, 19 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.1 KB
Line 
1/* $Id: VBoxBugReport.cpp 99775 2023-05-12 12:21:58Z 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
612static void 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
627 for (MachineInfoList::iterator it = machines.begin(); it != machines.end(); ++it)
628 {
629 static const char * const s_apszLogFilePatterns[] = { "VBox.log*", "VBoxHardening.log" };
630 for (size_t iPat = 0; iPat < RT_ELEMENTS(s_apszLogFilePatterns); iPat++)
631 {
632 VBRDir VmLogFiles(PathJoin((*it)->getLogPath(), s_apszLogFilePatterns[iPat]));
633 const char *pcszVmLogFile = VmLogFiles.next();
634 while (pcszVmLogFile)
635 {
636 report->addItem(new BugReportFile(PathJoin((*it)->getLogPath(), pcszVmLogFile),
637 PathJoin((*it)->getName(), pcszVmLogFile)));
638 pcszVmLogFile = VmLogFiles.next();
639 }
640 }
641 report->addItem(new BugReportFile((*it)->getSettingsFile(),
642 PathJoin((*it)->getName(), RTPathFilename((*it)->getSettingsFile()))));
643 report->addItem(new BugReportCommand(PathJoin((*it)->getName(), "GuestProperties"),
644 g_pszVBoxManage, "guestproperty", "enumerate",
645 (*it)->getName(), NULL));
646 }
647
648 createBugReportOsSpecific(report, pszHome);
649}
650
651
652static void addMachine(MachineInfoList& list, ComPtr<IMachine> machine)
653{
654 BOOL fAccessible = FALSE;
655 HRESULT hrc = machine->COMGETTER(Accessible)(&fAccessible);
656 if (SUCCEEDED(hrc) && !fAccessible)
657 return
658 handleComError(hrc, "Failed to get accessible status of VM");
659
660 com::Bstr name, logFolder, settingsFile;
661 handleComError(machine->COMGETTER(Name)(name.asOutParam()),
662 "Failed to get VM name");
663 handleComError(machine->COMGETTER(LogFolder)(logFolder.asOutParam()),
664 "Failed to get VM log folder");
665 handleComError(machine->COMGETTER(SettingsFilePath)(settingsFile.asOutParam()),
666 "Failed to get VM settings file path");
667 list.push_back(new MachineInfo(com::Utf8Str(name).c_str(),
668 com::Utf8Str(logFolder).c_str(),
669 com::Utf8Str(settingsFile).c_str()));
670}
671
672
673static void printHeader(void)
674{
675 RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Bug Report Tool " VBOX_VERSION_STRING "\n"
676 "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
677}
678
679int main(int argc, char *argv[])
680{
681 /*
682 * Initialize the VBox runtime without loading
683 * the support driver.
684 */
685 RTR3InitExe(argc, &argv, 0);
686
687 bool fAllMachines = false;
688 bool fTextOutput = false;
689 const char *pszOutputFile = NULL;
690 std::list<const char *> nameList;
691 RTGETOPTUNION ValueUnion;
692 RTGETOPTSTATE GetState;
693 int ret = RTGetOptInit(&GetState, argc, argv,
694 g_aOptions, RT_ELEMENTS(g_aOptions),
695 1 /* First */, 0 /*fFlags*/);
696 if (RT_FAILURE(ret))
697 return ret;
698 int ch;
699 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
700 {
701 switch(ch)
702 {
703 case 'h':
704 printHeader();
705 RTStrmPrintf(g_pStdErr, g_szUsage, argv[0]);
706 return 0;
707 case 'A':
708 fAllMachines = true;
709 break;
710 case 'o':
711 pszOutputFile = ValueUnion.psz;
712 break;
713 case 't':
714 fTextOutput = true;
715 break;
716 case 'V':
717 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
718 return 0;
719 case VINF_GETOPT_NOT_OPTION:
720 nameList.push_back(ValueUnion.psz);
721 break;
722 default:
723 return RTGetOptPrintError(ch, &ValueUnion);
724 }
725 }
726
727 printHeader();
728
729 HRESULT hr = S_OK;
730 char homeDir[RTPATH_MAX];
731 com::GetVBoxUserHomeDirectory(homeDir, sizeof(homeDir));
732
733 try
734 {
735 /* Figure out the full path to VBoxManage */
736 char szVBoxBin[RTPATH_MAX];
737 if (!RTProcGetExecutablePath(szVBoxBin, sizeof(szVBoxBin)))
738 throw RTCError("RTProcGetExecutablePath failed\n");
739 RTPathStripFilename(szVBoxBin);
740 g_pszVBoxManage = RTPathJoinA(szVBoxBin, VBOXMANAGE);
741 if (!g_pszVBoxManage)
742 throw RTCError("Out of memory\n");
743
744 handleComError(com::Initialize(VBOX_COM_INIT_F_DEFAULT | VBOX_COM_INIT_F_NO_COM_PATCHING), "Failed to initialize COM");
745
746 MachineInfoList list;
747
748 do
749 {
750 ComPtr<IVirtualBoxClient> virtualBoxClient;
751 ComPtr<IVirtualBox> virtualBox;
752 ComPtr<ISession> session;
753
754 hr = virtualBoxClient.createLocalObject(CLSID_VirtualBoxClient);
755 if (SUCCEEDED(hr))
756 hr = virtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
757 else
758 hr = virtualBox.createLocalObject(CLSID_VirtualBox);
759 if (FAILED(hr))
760 RTStrmPrintf(g_pStdErr, "WARNING: Failed to create the VirtualBox object (hr=0x%x)\n", hr);
761 else
762 {
763 hr = session.createInprocObject(CLSID_Session);
764 if (FAILED(hr))
765 RTStrmPrintf(g_pStdErr, "WARNING: Failed to create a session object (hr=0x%x)\n", hr);
766 }
767
768 if (SUCCEEDED(hr))
769 {
770 if (fAllMachines)
771 {
772 com::SafeIfaceArray<IMachine> machines;
773 hr = virtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines));
774 if (SUCCEEDED(hr))
775 {
776 for (size_t i = 0; i < machines.size(); ++i)
777 {
778 if (machines[i])
779 addMachine(list, machines[i]);
780 }
781 }
782 }
783 else
784 {
785 for ( std::list<const char *>::iterator it = nameList.begin(); it != nameList.end(); ++it)
786 {
787 ComPtr<IMachine> machine;
788 handleComError(virtualBox->FindMachine(com::Bstr(*it).raw(), machine.asOutParam()),
789 "No such machine '%s'", *it);
790 addMachine(list, machine);
791 }
792 }
793 }
794
795 }
796 while(0);
797
798 RTTIMESPEC TimeSpec;
799 RTTIME Time;
800 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
801 RTCStringFmt strOutFile("%04d-%02d-%02d-%02d-%02d-%02d-bugreport.%s",
802 Time.i32Year, Time.u8Month, Time.u8MonthDay,
803 Time.u8Hour, Time.u8Minute, Time.u8Second,
804 fTextOutput ? "txt" : "tgz");
805 RTCString strFallbackOutFile;
806 if (!pszOutputFile)
807 {
808 RTFILE tmp;
809 pszOutputFile = strOutFile.c_str();
810 int rc = RTFileOpen(&tmp, pszOutputFile, RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
811 if (rc == VERR_ACCESS_DENIED)
812 {
813 char szUserHome[RTPATH_MAX];
814 handleRtError(RTPathUserHome(szUserHome, sizeof(szUserHome)), "Failed to obtain home directory");
815 strFallbackOutFile.printf("%s/%s", szUserHome, strOutFile.c_str());
816 pszOutputFile = strFallbackOutFile.c_str();
817 }
818 else if (RT_SUCCESS(rc))
819 {
820 RTFileClose(tmp);
821 RTFileDelete(pszOutputFile);
822 }
823 }
824 BugReport *pReport;
825 if (fTextOutput)
826 pReport = new BugReportText(pszOutputFile);
827 else
828 pReport = new BugReportTarGzip(pszOutputFile);
829 createBugReport(pReport, homeDir, list);
830 pReport->process();
831 pReport->complete();
832 RTPrintf("Report was written to '%s'\n", pszOutputFile);
833 delete pReport;
834 }
835 catch (RTCError &e)
836 {
837 RTStrmPrintf(g_pStdErr, "ERROR: %s\n", e.what());
838 }
839
840 com::Shutdown();
841
842 if (g_pszVBoxManage)
843 RTStrFree(g_pszVBoxManage);
844
845 return SUCCEEDED(hr) ? 0 : 1;
846}
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