VirtualBox

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

Last change on this file since 92124 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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