diff options
Diffstat (limited to 'src/VBox/Frontends/VBoxBugReport')
-rw-r--r-- | src/VBox/Frontends/VBoxBugReport/Makefile.kmk | 48 | ||||
-rw-r--r-- | src/VBox/Frontends/VBoxBugReport/VBoxBugReport.cpp | 845 | ||||
-rw-r--r-- | src/VBox/Frontends/VBoxBugReport/VBoxBugReport.h | 287 | ||||
-rw-r--r-- | src/VBox/Frontends/VBoxBugReport/VBoxBugReportWin.cpp | 786 |
4 files changed, 1966 insertions, 0 deletions
diff --git a/src/VBox/Frontends/VBoxBugReport/Makefile.kmk b/src/VBox/Frontends/VBoxBugReport/Makefile.kmk new file mode 100644 index 00000000..df69518d --- /dev/null +++ b/src/VBox/Frontends/VBoxBugReport/Makefile.kmk @@ -0,0 +1,48 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for VBoxBugReport +# +# VBoxBugReport is a tool for collecting useful information on the host. +# + +# +# Copyright (C) 2006-2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# SPDX-License-Identifier: GPL-3.0-only +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +PROGRAMS += VBoxBugReport +VBoxBugReport_TEMPLATE = VBoxMainClientExe +ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING + # VBoxBugReport_DEFS = VBOX_WATCHDOG_GLOBAL_PERFCOL VBOX_BUILD_TARGET="$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)" +else + # VBoxBugReport_DEFS = VBOX_WATCHDOG_GLOBAL_PERFCOL VBOX_BUILD_TARGET=\"$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)\" +endif +VBoxBugReport_DEFS.win = _WIN32_WINNT=0x0501 PSAPI_VERSION=1 +VBoxBugReport_LIBS.win = psapi.lib +VBoxBugReport_SOURCES = \ + VBoxBugReport.cpp +VBoxBugReport_SOURCES.win = \ + VBoxBugReportWin.cpp + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/Frontends/VBoxBugReport/VBoxBugReport.cpp b/src/VBox/Frontends/VBoxBugReport/VBoxBugReport.cpp new file mode 100644 index 00000000..5da89ed2 --- /dev/null +++ b/src/VBox/Frontends/VBoxBugReport/VBoxBugReport.cpp @@ -0,0 +1,845 @@ +/* $Id: VBoxBugReport.cpp $ */ +/** @file + * VBoxBugReport - VirtualBox command-line diagnostics tool, main file. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +#include <VBox/com/com.h> +#include <VBox/com/string.h> +#include <VBox/com/array.h> +#include <VBox/com/ErrorInfo.h> +#include <VBox/com/errorprint.h> +#include <VBox/com/VirtualBox.h> + +#include <VBox/version.h> + +#include <iprt/buildconfig.h> +#include <iprt/err.h> +#include <iprt/env.h> +#include <iprt/file.h> +#include <iprt/getopt.h> +#include <iprt/initterm.h> +#include <iprt/path.h> +#include <iprt/process.h> +#include <iprt/zip.h> +#include <iprt/cpp/exception.h> + +#include <list> + +#include "VBoxBugReport.h" + +/* Implementation - Base */ + +#ifndef RT_OS_WINDOWS +/** @todo Replace with platform-specific implementations. */ +void createBugReportOsSpecific(BugReport *pReport, const char *pszHome) +{ + RT_NOREF(pReport, pszHome); +} +#endif /* !RT_OS_WINDOWS */ + + +/* Globals */ + +static char *g_pszVBoxManage = NULL; + +static const RTGETOPTDEF g_aOptions[] = +{ + { "-all", 'A', RTGETOPT_REQ_NOTHING }, + { "--all", 'A', RTGETOPT_REQ_NOTHING }, + { "-output", 'o', RTGETOPT_REQ_STRING }, + { "--output", 'o', RTGETOPT_REQ_STRING }, + { "-text", 't', RTGETOPT_REQ_NOTHING }, + { "--text", 't', RTGETOPT_REQ_NOTHING } +}; + +static const char g_szUsage[] = + "Usage: %s [-h|-?|--help] [-A|--all|<vmname>...] [-o <file>|--output=<file>]\n" + " Several VM names can be specified at once to be included into single report.\n" + " If none is given then no machines will be included. Specifying -A overrides\n" + " any VM names provided and includes all registered machines.\n" + "Options:\n" + " -h, -help, --help Print usage information\n" + " -A, -all, --all Include all registered machines\n" + " -o, -output, --output Specifies the name of the output file\n" + " -t, -text, --text Produce a single text file instead of compressed TAR\n" + " -V, -version, --version Print version information\n" + "\n"; + + +/* + * This class stores machine-specific file paths that are obtained via + * VirtualBox API. In case API is not functioning properly these paths + * will be deduced on the best effort basis. + */ +class MachineInfo +{ +public: + MachineInfo(const char *name, const char *logFolder, const char *settingsFile); + ~MachineInfo(); + const char *getName() const { return m_name; }; + const char *getLogPath() const { return m_logpath; }; + const char *getSettingsFile() const { return m_settings; }; +private: + char *m_name; + char *m_logpath; + char *m_settings; +}; + +MachineInfo::MachineInfo(const char *name, const char *logFolder, const char *settingsFile) +{ + m_name = RTStrDup(name); + m_logpath = RTStrDup(logFolder); + m_settings = RTStrDup(settingsFile); +} + +MachineInfo::~MachineInfo() +{ + RTStrFree(m_logpath); + RTStrFree(m_name); + RTStrFree(m_settings); + m_logpath = m_name = m_settings = 0; +} + +typedef std::list<MachineInfo*> MachineInfoList; + + +class VBRDir +{ +public: + VBRDir(const char *pcszPath) : m_hDir(NIL_RTDIR) + { + int rc = RTDirOpenFiltered(&m_hDir, pcszPath, RTDIRFILTER_WINNT, 0); + if (RT_FAILURE(rc) && rc != VERR_FILE_NOT_FOUND && rc != VERR_PATH_NOT_FOUND) + throw RTCError(com::Utf8StrFmt("Failed to open directory '%s'\n", pcszPath)); + }; + ~VBRDir() + { + if (RT_VALID_PTR(m_hDir)) + { + int rc = RTDirClose(m_hDir); + AssertRC(rc); + } + }; + const char *next(void) + { + if (!RT_VALID_PTR(m_hDir)) + return NULL; + + int rc = RTDirRead(m_hDir, &m_DirEntry, NULL); + if (RT_SUCCESS(rc)) + return m_DirEntry.szName; + if (rc == VERR_NO_MORE_FILES) + return NULL; + throw RTCError("Failed to read directory element\n"); + }; + +private: + RTDIR m_hDir; + RTDIRENTRY m_DirEntry; +}; + + +BugReportFilter::BugReportFilter() : m_pvBuffer(0), m_cbBuffer(0) +{ +} + +BugReportFilter::~BugReportFilter() +{ + if (m_pvBuffer) + RTMemFree(m_pvBuffer); +} + +void *BugReportFilter::allocateBuffer(size_t cbNeeded) +{ + if (m_pvBuffer) + { + if (cbNeeded > m_cbBuffer) + RTMemFree(m_pvBuffer); + else + return m_pvBuffer; + } + m_pvBuffer = RTMemAlloc(cbNeeded); + if (!m_pvBuffer) + throw RTCError(com::Utf8StrFmt("Failed to allocate %ld bytes\n", cbNeeded)); + m_cbBuffer = cbNeeded; + return m_pvBuffer; +} + + +/* + * An abstract class serving as the root of the bug report item tree. + */ +BugReportItem::BugReportItem(const char *pszTitle) +{ + m_pszTitle = RTStrDup(pszTitle); + m_filter = 0; +} + +BugReportItem::~BugReportItem() +{ + if (m_filter) + delete m_filter; + RTStrFree(m_pszTitle); +} + +void BugReportItem::addFilter(BugReportFilter *filter) +{ + m_filter = filter; +} + +void *BugReportItem::applyFilter(void *pvSource, size_t *pcbInOut) +{ + if (m_filter) + return m_filter->apply(pvSource, pcbInOut); + return pvSource; +} + +const char * BugReportItem::getTitle(void) +{ + return m_pszTitle; +} + + +BugReport::BugReport(const char *pszFileName) +{ + m_pszFileName = RTStrDup(pszFileName); +} + +BugReport::~BugReport() +{ + for (unsigned i = 0; i < m_Items.size(); ++i) + { + delete m_Items[i]; + } + RTStrFree(m_pszFileName); +} + +int BugReport::getItemCount(void) +{ + return (int)m_Items.size(); +} + +void BugReport::addItem(BugReportItem* item, BugReportFilter *filter) +{ + if (filter) + item->addFilter(filter); + if (item) + m_Items.append(item); +} + +void BugReport::process(void) +{ + for (unsigned i = 0; i < m_Items.size(); ++i) + { + BugReportItem *pItem = m_Items[i]; + RTPrintf("%3u%% - collecting %s...\n", i * 100 / m_Items.size(), pItem->getTitle()); + processItem(pItem); + } + RTPrintf("100%% - compressing...\n\n"); +} + +void *BugReport::applyFilters(BugReportItem* item, void *pvSource, size_t *pcbInOut) +{ + return item->applyFilter(pvSource, pcbInOut); +} + + +BugReportStream::BugReportStream(const char *pszTitle) : BugReportItem(pszTitle) +{ + m_hVfsIos = NIL_RTVFSIOSTREAM; + handleRtError(RTPathTemp(m_szFileName, RTPATH_MAX), + "Failed to obtain path to temporary folder"); + handleRtError(RTPathAppend(m_szFileName, RTPATH_MAX, "BugRepXXXXX.tmp"), + "Failed to append path"); + handleRtError(RTFileCreateTemp(m_szFileName, 0600), + "Failed to create temporary file '%s'", m_szFileName); + handleRtError(RTVfsIoStrmOpenNormal(m_szFileName, RTFILE_O_OPEN | RTFILE_O_WRITE | RTFILE_O_DENY_NONE, &m_hVfsIos), + "Failed to open '%s'", m_szFileName); +} + +BugReportStream::~BugReportStream() +{ + if (m_hVfsIos != NIL_RTVFSIOSTREAM) + RTVfsIoStrmRelease(m_hVfsIos); + RTFileDelete(m_szFileName); +} + +int BugReportStream::printf(const char *pszFmt, ...) +{ + va_list va; + va_start(va, pszFmt); + int cb = RTVfsIoStrmPrintfV(m_hVfsIos, pszFmt, va); + va_end(va); + return cb; +} + +int BugReportStream::putStr(const char *pszString) +{ + return RTVfsIoStrmPrintf(m_hVfsIos, "%s", pszString); +} + +RTVFSIOSTREAM BugReportStream::getStream(void) +{ + RTVfsIoStrmRelease(m_hVfsIos); + handleRtError(RTVfsIoStrmOpenNormal(m_szFileName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &m_hVfsIos), + "Failed to open '%s'", m_szFileName); + return m_hVfsIos; +} + + +/* Implementation - Generic */ + +BugReportFile::BugReportFile(const char *pszPath, const char *pszShortName) : BugReportItem(pszShortName) +{ + m_hVfsIos = NIL_RTVFSIOSTREAM; + m_pszPath = RTStrDup(pszPath); +} + +BugReportFile::~BugReportFile() +{ + if (m_hVfsIos != NIL_RTVFSIOSTREAM) + RTVfsIoStrmRelease(m_hVfsIos); + if (m_pszPath) + RTStrFree(m_pszPath); +} + +RTVFSIOSTREAM BugReportFile::getStream(void) +{ + handleRtError(RTVfsIoStrmOpenNormal(m_pszPath, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &m_hVfsIos), + "Failed to open '%s'", m_pszPath); + return m_hVfsIos; +} + + +BugReportCommand::BugReportCommand(const char *pszTitle, const char *pszExec, ...) + : BugReportItem(pszTitle), m_hVfsIos(NIL_RTVFSIOSTREAM) +{ + unsigned cArgs = 0; + m_papszArgs[cArgs++] = RTStrDup(pszExec); + + const char *pszArg; + va_list va; + va_start(va, pszExec); + do + { + if (cArgs >= RT_ELEMENTS(m_papszArgs)) + { + va_end(va); + throw RTCError(com::Utf8StrFmt("Too many arguments (%u > %u)\n", cArgs+1, RT_ELEMENTS(m_papszArgs))); + } + pszArg = va_arg(va, const char *); + m_papszArgs[cArgs++] = pszArg ? RTStrDup(pszArg) : NULL; + } while (pszArg); + va_end(va); +} + +BugReportCommand::~BugReportCommand() +{ + if (m_hVfsIos != NIL_RTVFSIOSTREAM) + RTVfsIoStrmRelease(m_hVfsIos); + RTFileDelete(m_szFileName); + for (size_t i = 0; i < RT_ELEMENTS(m_papszArgs) && m_papszArgs[i]; ++i) + RTStrFree(m_papszArgs[i]); +} + +RTVFSIOSTREAM BugReportCommand::getStream(void) +{ + handleRtError(RTPathTemp(m_szFileName, RTPATH_MAX), + "Failed to obtain path to temporary folder"); + handleRtError(RTPathAppend(m_szFileName, RTPATH_MAX, "BugRepXXXXX.tmp"), + "Failed to append path"); + handleRtError(RTFileCreateTemp(m_szFileName, 0600), + "Failed to create temporary file '%s'", m_szFileName); + + RTHANDLE hStdOutErr; + hStdOutErr.enmType = RTHANDLETYPE_FILE; + handleRtError(RTFileOpen(&hStdOutErr.u.hFile, m_szFileName, + RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE), + "Failed to open temporary file '%s'", m_szFileName); + + RTPROCESS hProcess; + handleRtError(RTProcCreateEx(m_papszArgs[0], m_papszArgs, RTENV_DEFAULT, 0, + NULL, &hStdOutErr, &hStdOutErr, + NULL, NULL, NULL, &hProcess), + "Failed to create process '%s'", m_papszArgs[0]); + RTPROCSTATUS status; + handleRtError(RTProcWait(hProcess, RTPROCWAIT_FLAGS_BLOCK, &status), + "Process wait failed"); + //if (status.enmReason == RTPROCEXITREASON_NORMAL) {} + RTFileClose(hStdOutErr.u.hFile); + + handleRtError(RTVfsIoStrmOpenNormal(m_szFileName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &m_hVfsIos), + "Failed to open '%s'", m_szFileName); + return m_hVfsIos; +} + + +BugReportCommandTemp::BugReportCommandTemp(const char *pszTitle, const char *pszExec, ...) + : BugReportItem(pszTitle), m_hVfsIos(NIL_RTVFSIOSTREAM) +{ + handleRtError(RTPathTemp(m_szFileName, RTPATH_MAX), + "Failed to obtain path to temporary folder"); + handleRtError(RTPathAppend(m_szFileName, RTPATH_MAX, "BugRepXXXXX.tmp"), + "Failed to append path"); + handleRtError(RTFileCreateTemp(m_szFileName, 0600), + "Failed to create temporary file '%s'", m_szFileName); + + unsigned cArgs = 0; + m_papszArgs[cArgs++] = RTStrDup(pszExec); + + const char *pszArg; + va_list va; + va_start(va, pszExec); + do + { + if (cArgs >= RT_ELEMENTS(m_papszArgs) - 1) + { + va_end(va); + throw RTCError(com::Utf8StrFmt("Too many arguments (%u > %u)\n", cArgs+1, RT_ELEMENTS(m_papszArgs))); + } + pszArg = va_arg(va, const char *); + m_papszArgs[cArgs++] = RTStrDup(pszArg ? pszArg : m_szFileName); + } while (pszArg); + va_end(va); + + m_papszArgs[cArgs++] = NULL; +} + +BugReportCommandTemp::~BugReportCommandTemp() +{ + if (m_hVfsIos != NIL_RTVFSIOSTREAM) + RTVfsIoStrmRelease(m_hVfsIos); + RTFileDelete(m_szErrFileName); + RTFileDelete(m_szFileName); + for (size_t i = 0; i < RT_ELEMENTS(m_papszArgs) && m_papszArgs[i]; ++i) + RTStrFree(m_papszArgs[i]); +} + +RTVFSIOSTREAM BugReportCommandTemp::getStream(void) +{ + handleRtError(RTPathTemp(m_szErrFileName, RTPATH_MAX), + "Failed to obtain path to temporary folder"); + handleRtError(RTPathAppend(m_szErrFileName, RTPATH_MAX, "BugRepErrXXXXX.tmp"), + "Failed to append path"); + handleRtError(RTFileCreateTemp(m_szErrFileName, 0600), + "Failed to create temporary file '%s'", m_szErrFileName); + + RTHANDLE hStdOutErr; + hStdOutErr.enmType = RTHANDLETYPE_FILE; + handleRtError(RTFileOpen(&hStdOutErr.u.hFile, m_szErrFileName, + RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE), + "Failed to open temporary file '%s'", m_szErrFileName); + + /* Remove the output file to prevent errors or confirmation prompts */ + handleRtError(RTFileDelete(m_szFileName), + "Failed to delete temporary file '%s'", m_szFileName); + + RTPROCESS hProcess; + handleRtError(RTProcCreateEx(m_papszArgs[0], m_papszArgs, RTENV_DEFAULT, 0, + NULL, &hStdOutErr, &hStdOutErr, + NULL, NULL, NULL, &hProcess), + "Failed to create process '%s'", m_papszArgs[0]); + RTPROCSTATUS status; + handleRtError(RTProcWait(hProcess, RTPROCWAIT_FLAGS_BLOCK, &status), + "Process wait failed"); + RTFileClose(hStdOutErr.u.hFile); + + if (status.enmReason == RTPROCEXITREASON_NORMAL && status.iStatus == 0) + handleRtError(RTVfsIoStrmOpenNormal(m_szFileName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &m_hVfsIos), + "Failed to open '%s'", m_szFileName); + else + handleRtError(RTVfsIoStrmOpenNormal(m_szErrFileName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &m_hVfsIos), + "Failed to open '%s'", m_szErrFileName); + return m_hVfsIos; +} + + +BugReportText::BugReportText(const char *pszFileName) : BugReport(pszFileName) +{ + handleRtError(RTStrmOpen(pszFileName, "w", &m_StrmTxt), + "Failed to open '%s'", pszFileName); +} + +BugReportText::~BugReportText() +{ + if (m_StrmTxt) + RTStrmClose(m_StrmTxt); +} + +void BugReportText::processItem(BugReportItem* item) +{ + int cb = RTStrmPrintf(m_StrmTxt, "[ %s ] -------------------------------------------\n", item->getTitle()); + if (!cb) + throw RTCError(com::Utf8StrFmt("Write failure (cb=%d)\n", cb)); + + RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM; + try + { + hVfsIos = item->getStream(); + } + catch (RTCError &e) + { + hVfsIos = NIL_RTVFSIOSTREAM; + RTStrmPutStr(m_StrmTxt, e.what()); + } + + int rc = VINF_SUCCESS; + + if (hVfsIos != NIL_RTVFSIOSTREAM) + { + char buf[64*1024]; + size_t cbRead, cbWritten; + cbRead = cbWritten = 0; + while (RT_SUCCESS(rc = RTVfsIoStrmRead(hVfsIos, buf, sizeof(buf), true /*fBlocking*/, &cbRead)) && cbRead) + { + rc = RTStrmWriteEx(m_StrmTxt, applyFilters(item, buf, &cbRead), cbRead, &cbWritten); + if (RT_FAILURE(rc) || cbRead != cbWritten) + throw RTCError(com::Utf8StrFmt("Write failure (rc=%d, cbRead=%lu, cbWritten=%lu)\n", + rc, cbRead, cbWritten)); + } + } + + handleRtError(RTStrmPutCh(m_StrmTxt, '\n'), "Write failure"); +} + + +BugReportTarGzip::BugReportTarGzip(const char *pszFileName) + : BugReport(pszFileName), m_hTarFss(NIL_RTVFSFSSTREAM) +{ + VfsIoStreamHandle hVfsOut; + handleRtError(RTVfsIoStrmOpenNormal(pszFileName, RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE, + hVfsOut.getPtr()), + "Failed to create output file '%s'", pszFileName); + handleRtError(RTZipGzipCompressIoStream(hVfsOut.get(), 0, 6, m_hVfsGzip.getPtr()), + "Failed to create compressed stream for '%s'", pszFileName); + + int rc = RTZipTarFsStreamToIoStream(m_hVfsGzip.get(), RTZIPTARFORMAT_DEFAULT, RTZIPTAR_C_SPARSE, &m_hTarFss); + handleRtError(rc, "Failed to create TAR file '%s'", m_szTarName); +} + +BugReportTarGzip::~BugReportTarGzip() +{ + if (m_hTarFss != NIL_RTVFSFSSTREAM) + RTVfsFsStrmRelease(m_hTarFss); +} + +void BugReportTarGzip::dumpExceptionToArchive(RTCString &strTarFile, RTCError &e) +{ + RTVFSFILE hVfsFile; + int rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, _1K/*cbEstimate*/, &hVfsFile); + if (RT_SUCCESS(rc)) + { + rc = RTVfsFileWrite(hVfsFile, e.what(), RTStrNLen(e.what(), 1024), NULL /*pcbWritten*/); + if (RT_SUCCESS(rc)) + { + rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/); + if (RT_SUCCESS(rc)) + { + RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFile); + rc = RTVfsFsStrmAdd(m_hTarFss, strTarFile.c_str(), hVfsObj, 0 /*fFlags*/); + RTVfsObjRelease(hVfsObj); + } + } + RTVfsFileRelease(hVfsFile); + } + handleRtError(rc, "Failed to add exception text to TAR archive '%s'", m_szTarName); +} + +void BugReportTarGzip::processItem(BugReportItem* item) +{ + /* + * @todo Our TAR implementation does not support names larger than 100 characters. + * We truncate the title to make sure it will fit into 100-character field of TAR header. + */ + RTCString strTarFile = RTCString(item->getTitle()).substr(0, RTStrNLen(item->getTitle(), 99)); + RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM; + try + { + hVfsIos = item->getStream(); + } + catch (RTCError &e) + { + hVfsIos = NIL_RTVFSIOSTREAM; + dumpExceptionToArchive(strTarFile, e); + } + + if (hVfsIos != NIL_RTVFSIOSTREAM) + { + RTVFSOBJ hVfsObjIos = RTVfsObjFromIoStream(hVfsIos); + int rc = RTVfsFsStrmAdd(m_hTarFss, strTarFile.c_str(), hVfsObjIos, 0 /*fFlags*/); + RTVfsObjRelease(hVfsObjIos); + handleRtError(rc, "Failed to add file to TAR archive '%s'", m_szTarName); + } +} + +void BugReportTarGzip::complete(void) +{ + if (m_hTarFss != NIL_RTVFSFSSTREAM) + { + RTVfsFsStrmRelease(m_hTarFss); + m_hTarFss = NIL_RTVFSFSSTREAM; + } + handleRtError(RTVfsIoStrmFlush(m_hVfsGzip.get()), "Failed to flush output stream"); + m_hVfsGzip.release(); +} + + +/* Implementation - Main */ + +void createBugReport(BugReport* report, const char *pszHome, MachineInfoList& machines) +{ + /* Collect all log files from VBoxSVC */ + VBRDir HomeDir(PathJoin(pszHome, "VBoxSVC.log*")); + const char *pcszSvcLogFile = HomeDir.next(); + while (pcszSvcLogFile) + { + report->addItem(new BugReportFile(PathJoin(pszHome, pcszSvcLogFile), pcszSvcLogFile)); + pcszSvcLogFile = HomeDir.next(); + } + + report->addItem(new BugReportFile(PathJoin(pszHome, "VirtualBox.xml"), "VirtualBox.xml")); + report->addItem(new BugReportCommand("HostUsbDevices", g_pszVBoxManage, "list", "usbhost", NULL)); + report->addItem(new BugReportCommand("HostUsbFilters", g_pszVBoxManage, "list", "usbfilters", NULL)); + + for (MachineInfoList::iterator it = machines.begin(); it != machines.end(); ++it) + { + static const char * const s_apszLogFilePatterns[] = { "VBox.log*", "VBoxHardening.log" }; + for (size_t iPat = 0; iPat < RT_ELEMENTS(s_apszLogFilePatterns); iPat++) + { + VBRDir VmLogFiles(PathJoin((*it)->getLogPath(), s_apszLogFilePatterns[iPat])); + const char *pcszVmLogFile = VmLogFiles.next(); + while (pcszVmLogFile) + { + report->addItem(new BugReportFile(PathJoin((*it)->getLogPath(), pcszVmLogFile), + PathJoin((*it)->getName(), pcszVmLogFile))); + pcszVmLogFile = VmLogFiles.next(); + } + } + report->addItem(new BugReportFile((*it)->getSettingsFile(), + PathJoin((*it)->getName(), RTPathFilename((*it)->getSettingsFile())))); + report->addItem(new BugReportCommand(PathJoin((*it)->getName(), "GuestProperties"), + g_pszVBoxManage, "guestproperty", "enumerate", + (*it)->getName(), NULL)); + } + + createBugReportOsSpecific(report, pszHome); +} + +void addMachine(MachineInfoList& list, ComPtr<IMachine> machine) +{ + BOOL fAccessible = FALSE; + HRESULT hrc = machine->COMGETTER(Accessible)(&fAccessible); + if (SUCCEEDED(hrc) && !fAccessible) + return + handleComError(hrc, "Failed to get accessible status of VM"); + + com::Bstr name, logFolder, settingsFile; + handleComError(machine->COMGETTER(Name)(name.asOutParam()), + "Failed to get VM name"); + handleComError(machine->COMGETTER(LogFolder)(logFolder.asOutParam()), + "Failed to get VM log folder"); + handleComError(machine->COMGETTER(SettingsFilePath)(settingsFile.asOutParam()), + "Failed to get VM settings file path"); + list.push_back(new MachineInfo(com::Utf8Str(name).c_str(), + com::Utf8Str(logFolder).c_str(), + com::Utf8Str(settingsFile).c_str())); +} + + +static void printHeader(void) +{ + RTStrmPrintf(g_pStdErr, VBOX_PRODUCT " Bug Report Tool " VBOX_VERSION_STRING "\n" + "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n"); +} + +int main(int argc, char *argv[]) +{ + /* + * Initialize the VBox runtime without loading + * the support driver. + */ + RTR3InitExe(argc, &argv, 0); + + bool fAllMachines = false; + bool fTextOutput = false; + const char *pszOutputFile = NULL; + std::list<const char *> nameList; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + int ret = RTGetOptInit(&GetState, argc, argv, + g_aOptions, RT_ELEMENTS(g_aOptions), + 1 /* First */, 0 /*fFlags*/); + if (RT_FAILURE(ret)) + return ret; + int ch; + while ((ch = RTGetOpt(&GetState, &ValueUnion))) + { + switch(ch) + { + case 'h': + printHeader(); + RTStrmPrintf(g_pStdErr, g_szUsage, argv[0]); + return 0; + case 'A': + fAllMachines = true; + break; + case 'o': + pszOutputFile = ValueUnion.psz; + break; + case 't': + fTextOutput = true; + break; + case 'V': + RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr()); + return 0; + case VINF_GETOPT_NOT_OPTION: + nameList.push_back(ValueUnion.psz); + break; + default: + return RTGetOptPrintError(ch, &ValueUnion); + } + } + + printHeader(); + + HRESULT hr = S_OK; + char homeDir[RTPATH_MAX]; + com::GetVBoxUserHomeDirectory(homeDir, sizeof(homeDir)); + + try + { + /* Figure out the full path to VBoxManage */ + char szVBoxBin[RTPATH_MAX]; + if (!RTProcGetExecutablePath(szVBoxBin, sizeof(szVBoxBin))) + throw RTCError("RTProcGetExecutablePath failed\n"); + RTPathStripFilename(szVBoxBin); + g_pszVBoxManage = RTPathJoinA(szVBoxBin, VBOXMANAGE); + if (!g_pszVBoxManage) + throw RTCError("Out of memory\n"); + + handleComError(com::Initialize(VBOX_COM_INIT_F_DEFAULT | VBOX_COM_INIT_F_NO_COM_PATCHING), "Failed to initialize COM"); + + MachineInfoList list; + + do + { + ComPtr<IVirtualBoxClient> virtualBoxClient; + ComPtr<IVirtualBox> virtualBox; + ComPtr<ISession> session; + + hr = virtualBoxClient.createLocalObject(CLSID_VirtualBoxClient); + if (SUCCEEDED(hr)) + hr = virtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam()); + else + hr = virtualBox.createLocalObject(CLSID_VirtualBox); + if (FAILED(hr)) + RTStrmPrintf(g_pStdErr, "WARNING: Failed to create the VirtualBox object (hr=0x%x)\n", hr); + else + { + hr = session.createInprocObject(CLSID_Session); + if (FAILED(hr)) + RTStrmPrintf(g_pStdErr, "WARNING: Failed to create a session object (hr=0x%x)\n", hr); + } + + if (SUCCEEDED(hr)) + { + if (fAllMachines) + { + com::SafeIfaceArray<IMachine> machines; + hr = virtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines)); + if (SUCCEEDED(hr)) + { + for (size_t i = 0; i < machines.size(); ++i) + { + if (machines[i]) + addMachine(list, machines[i]); + } + } + } + else + { + for ( std::list<const char *>::iterator it = nameList.begin(); it != nameList.end(); ++it) + { + ComPtr<IMachine> machine; + handleComError(virtualBox->FindMachine(com::Bstr(*it).raw(), machine.asOutParam()), + "No such machine '%s'", *it); + addMachine(list, machine); + } + } + } + + } + while(0); + + RTTIMESPEC TimeSpec; + RTTIME Time; + RTTimeExplode(&Time, RTTimeNow(&TimeSpec)); + RTCStringFmt strOutFile("%04d-%02d-%02d-%02d-%02d-%02d-bugreport.%s", + Time.i32Year, Time.u8Month, Time.u8MonthDay, + Time.u8Hour, Time.u8Minute, Time.u8Second, + fTextOutput ? "txt" : "tgz"); + RTCString strFallbackOutFile; + if (!pszOutputFile) + { + RTFILE tmp; + pszOutputFile = strOutFile.c_str(); + int rc = RTFileOpen(&tmp, pszOutputFile, RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE); + if (rc == VERR_ACCESS_DENIED) + { + char szUserHome[RTPATH_MAX]; + handleRtError(RTPathUserHome(szUserHome, sizeof(szUserHome)), "Failed to obtain home directory"); + strFallbackOutFile.printf("%s/%s", szUserHome, strOutFile.c_str()); + pszOutputFile = strFallbackOutFile.c_str(); + } + else if (RT_SUCCESS(rc)) + { + RTFileClose(tmp); + RTFileDelete(pszOutputFile); + } + } + BugReport *pReport; + if (fTextOutput) + pReport = new BugReportText(pszOutputFile); + else + pReport = new BugReportTarGzip(pszOutputFile); + createBugReport(pReport, homeDir, list); + pReport->process(); + pReport->complete(); + RTPrintf("Report was written to '%s'\n", pszOutputFile); + delete pReport; + } + catch (RTCError &e) + { + RTStrmPrintf(g_pStdErr, "ERROR: %s\n", e.what()); + } + + com::Shutdown(); + + if (g_pszVBoxManage) + RTStrFree(g_pszVBoxManage); + + return SUCCEEDED(hr) ? 0 : 1; +} diff --git a/src/VBox/Frontends/VBoxBugReport/VBoxBugReport.h b/src/VBox/Frontends/VBoxBugReport/VBoxBugReport.h new file mode 100644 index 00000000..c81aaf34 --- /dev/null +++ b/src/VBox/Frontends/VBoxBugReport/VBoxBugReport.h @@ -0,0 +1,287 @@ +/* $Id: VBoxBugReport.h $ */ +/** @file + * VBoxBugReport - VirtualBox command-line diagnostics tool, internal header file. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxBugReport_VBoxBugReport_h +#define VBOX_INCLUDED_SRC_VBoxBugReport_VBoxBugReport_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +/* + * Introduction. + * + * In the most general sense a bug report is a collection of data obtained from + * the user's host system. It may include files common for all VMs, like the + * VBoxSVC.log file, as well as files related to particular machines. It may + * also contain the output of commands executed on the host, as well as data + * collected via OS APIs. + */ + +/** @todo not sure if using a separate namespace would be beneficial */ + +#include <iprt/path.h> +#include <iprt/stream.h> +#include <iprt/vfs.h> +#include <iprt/zip.h> +#include <iprt/cpp/list.h> + +#ifdef RT_OS_WINDOWS +#define VBOXMANAGE "VBoxManage.exe" +#else /* !RT_OS_WINDOWS */ +#define VBOXMANAGE "VBoxManage" +#endif /* !RT_OS_WINDOWS */ + +/* Base */ + +DECL_INLINE_THROW(void) handleRtError(int rc, const char *pszMsgFmt, ...) +{ + if (RT_FAILURE(rc)) + { + va_list va; + va_start(va, pszMsgFmt); + RTCString strMsg(pszMsgFmt, va); + va_end(va); + strMsg.appendPrintfNoThrow(". %Rrf\n", rc); + throw RTCError(strMsg); + } +} + +DECL_INLINE_THROW(void) handleComError(HRESULT hr, const char *pszMsgFmt, ...) +{ + if (FAILED(hr)) + { + va_list va; + va_start(va, pszMsgFmt); + RTCString strMsg(pszMsgFmt, va); + va_end(va); + strMsg.appendPrintfNoThrow(". (hr=0x%x %Rhrc)\n", hr, hr); + throw RTCError(strMsg); + } +} + +/* + * An auxiliary class to facilitate in-place path joins. + */ +class PathJoin +{ +public: + PathJoin(const char *folder, const char *file) { m_path = RTPathJoinA(folder, file); } + ~PathJoin() { RTStrFree(m_path); }; + operator char*() const { return m_path; }; +private: + char *m_path; +}; + + +/* + * An abstract class serving as the root of the bug report filter tree. + * A child provides an implementation of the 'apply' method. A child + * should modify the input buffer (provided via pvSource) in place, or + * allocate a new buffer via 'allocateBuffer'. Allocated buffers are + * released automatically when another buffer is allocated, which means + * that NEXT CALL TO 'APPLY' INVALIDATES BUFFERS RETURNED IN PREVIOUS + * CALLS! + */ +class BugReportFilter +{ +public: + BugReportFilter(); + virtual ~BugReportFilter(); + virtual void *apply(void *pvSource, size_t *pcbInOut) = 0; +protected: + void *allocateBuffer(size_t cbNeeded); +private: + void *m_pvBuffer; + size_t m_cbBuffer; +}; + + +/* + * An abstract class serving as the root of the bug report item tree. + */ +class BugReportItem +{ +public: + BugReportItem(const char *pszTitle); + virtual ~BugReportItem(); + virtual const char *getTitle(void); + virtual RTVFSIOSTREAM getStream(void) = 0; + void addFilter(BugReportFilter *filter); + void *applyFilter(void *pvSource, size_t *pcbInOut); +private: + char *m_pszTitle; + BugReportFilter *m_filter; +}; + +/* + * An abstract class to serve as a base class for all report types. + */ +class BugReport +{ +public: + BugReport(const char *pszFileName); + virtual ~BugReport(); + + void addItem(BugReportItem* item, BugReportFilter *filter = 0); + int getItemCount(void); + void process(); + void *applyFilters(BugReportItem* item, void *pvSource, size_t *pcbInOut); + + virtual void processItem(BugReportItem* item) = 0; + virtual void complete(void) = 0; + +protected: + char *m_pszFileName; + RTCList<BugReportItem*> m_Items; +}; + +/* + * An auxiliary class providing formatted output into a temporary file for item + * classes that obtain data via host OS APIs. + */ +class BugReportStream : public BugReportItem +{ +public: + BugReportStream(const char *pszTitle); + virtual ~BugReportStream(); + virtual RTVFSIOSTREAM getStream(void); +protected: + int printf(const char *pszFmt, ...); + int putStr(const char *pszString); +private: + RTVFSIOSTREAM m_hVfsIos; + char m_szFileName[RTPATH_MAX]; +}; + + +/* Generic */ + +/* + * This class reports everything into a single text file. + */ +class BugReportText : public BugReport +{ +public: + BugReportText(const char *pszFileName); + virtual ~BugReportText(); + virtual void processItem(BugReportItem* item); + virtual void complete(void) {}; +private: + PRTSTREAM m_StrmTxt; +}; + +/* + * This class reports items as individual files archived into a single compressed TAR file. + */ +class BugReportTarGzip : public BugReport +{ +public: + BugReportTarGzip(const char *pszFileName); + virtual ~BugReportTarGzip(); + virtual void processItem(BugReportItem* item); + virtual void complete(void); +private: + void dumpExceptionToArchive(RTCString &strTarFile, RTCError &e); + + /* + * Helper class to release handles going out of scope. + */ + class VfsIoStreamHandle + { + public: + VfsIoStreamHandle() : m_hVfsStream(NIL_RTVFSIOSTREAM) {}; + ~VfsIoStreamHandle() { release(); } + PRTVFSIOSTREAM getPtr(void) { return &m_hVfsStream; }; + RTVFSIOSTREAM get(void) { return m_hVfsStream; }; + void release(void) + { + if (m_hVfsStream != NIL_RTVFSIOSTREAM) + RTVfsIoStrmRelease(m_hVfsStream); + m_hVfsStream = NIL_RTVFSIOSTREAM; + }; + private: + RTVFSIOSTREAM m_hVfsStream; + }; + + VfsIoStreamHandle m_hVfsGzip; + + RTVFSFSSTREAM m_hTarFss; + char m_szTarName[RTPATH_MAX]; +}; + + +/* + * BugReportFile adds a file as an item to a report. + */ +class BugReportFile : public BugReportItem +{ +public: + BugReportFile(const char *pszPath, const char *pcszName); + virtual ~BugReportFile(); + virtual RTVFSIOSTREAM getStream(void); + +private: + char *m_pszPath; + RTVFSIOSTREAM m_hVfsIos; +}; + +/* + * A base class for item classes that collect CLI output. + */ +class BugReportCommand : public BugReportItem +{ +public: + BugReportCommand(const char *pszTitle, const char *pszExec, ...); + virtual ~BugReportCommand(); + virtual RTVFSIOSTREAM getStream(void); +private: + RTVFSIOSTREAM m_hVfsIos; + char m_szFileName[RTPATH_MAX]; + char *m_papszArgs[32]; +}; + +/* + * A base class for item classes that provide temp output file to a command. + */ +class BugReportCommandTemp : public BugReportItem +{ +public: + BugReportCommandTemp(const char *pszTitle, const char *pszExec, ...); + virtual ~BugReportCommandTemp(); + virtual RTVFSIOSTREAM getStream(void); +private: + RTVFSIOSTREAM m_hVfsIos; + char m_szFileName[RTPATH_MAX]; + char m_szErrFileName[RTPATH_MAX]; + char *m_papszArgs[32]; +}; + +/* Platform-specific */ + +void createBugReportOsSpecific(BugReport* report, const char *pszHome); + +#endif /* !VBOX_INCLUDED_SRC_VBoxBugReport_VBoxBugReport_h */ diff --git a/src/VBox/Frontends/VBoxBugReport/VBoxBugReportWin.cpp b/src/VBox/Frontends/VBoxBugReport/VBoxBugReportWin.cpp new file mode 100644 index 00000000..28a26794 --- /dev/null +++ b/src/VBox/Frontends/VBoxBugReport/VBoxBugReportWin.cpp @@ -0,0 +1,786 @@ +/* $Id: VBoxBugReportWin.cpp $ */ +/** @file + * VBoxBugReportWin - VirtualBox command-line diagnostics tool, Windows-specific part. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include <VBox/com/com.h> +#include <VBox/com/string.h> + +#include <iprt/cpp/exception.h> + +#include "VBoxBugReport.h" + +#include <netcfgx.h> +#include <iprt/win/setupapi.h> +#include <initguid.h> +#include <devguid.h> +#include <usbiodef.h> +#include <usbioctl.h> +#include <psapi.h> + +#define ReleaseAndReset(obj) \ + if (obj) \ + obj->Release(); \ + obj = NULL; + + +class BugReportNetworkAdaptersWin : public BugReportStream +{ +public: + BugReportNetworkAdaptersWin() : BugReportStream("NetworkAdapters") {}; + virtual ~BugReportNetworkAdaptersWin() {}; + virtual RTVFSIOSTREAM getStream(void) { collect(); return BugReportStream::getStream(); }; +private: + struct CharacteristicsName + { + DWORD dwChar; + const char *szName; + }; + void printCharteristics(DWORD dwChars); + void collect(); + void collectNetCfgComponentInfo(int ident, bool fEnabled, INetCfgComponent *pComponent); +}; + + + +void BugReportNetworkAdaptersWin::printCharteristics(DWORD dwChars) +{ + static CharacteristicsName cMap[] = + { + { NCF_VIRTUAL, "virtual" }, + { NCF_SOFTWARE_ENUMERATED, "software_enumerated" }, + { NCF_PHYSICAL, "physical" }, + { NCF_HIDDEN, "hidden" }, + { NCF_NO_SERVICE, "no_service" }, + { NCF_NOT_USER_REMOVABLE, "not_user_removable" }, + { NCF_MULTIPORT_INSTANCED_ADAPTER, "multiport_instanced_adapter" }, + { NCF_HAS_UI, "has_ui" }, + { NCF_SINGLE_INSTANCE, "single_instance" }, + { NCF_FILTER, "filter" }, + { NCF_DONTEXPOSELOWER, "dontexposelower" }, + { NCF_HIDE_BINDING, "hide_binding" }, + { NCF_NDIS_PROTOCOL, "ndis_protocol" }, + { NCF_FIXED_BINDING, "fixed_binding" }, + { NCF_LW_FILTER, "lw_filter" } + }; + bool fPrintDelim = false; + + for (int i = 0; i < RT_ELEMENTS(cMap); ++i) + { + if (dwChars & cMap[i].dwChar) + { + if (fPrintDelim) + { + putStr(", "); + fPrintDelim = false; + } + putStr(cMap[i].szName); + fPrintDelim = true; + } + } +} + +void BugReportNetworkAdaptersWin::collectNetCfgComponentInfo(int ident, bool fEnabled, INetCfgComponent *pComponent) +{ + LPWSTR pwszName = NULL; + HRESULT hr = pComponent->GetDisplayName(&pwszName); + if (FAILED(hr)) + throw RTCError(com::Utf8StrFmt("Failed to get component display name, hr=0x%x.\n", hr)); + printf("%s%c %ls [", RTCString(ident, ' ').c_str(), fEnabled ? '+' : '-', pwszName); + if (pwszName) + CoTaskMemFree(pwszName); + + DWORD dwChars = 0; + hr = pComponent->GetCharacteristics(&dwChars); + if (FAILED(hr)) + throw RTCError(com::Utf8StrFmt("Failed to get component characteristics, hr=0x%x.\n", hr)); + printCharteristics(dwChars); + putStr("]\n"); +} + +void BugReportNetworkAdaptersWin::collect(void) +{ + INetCfg *pNetCfg = NULL; + IEnumNetCfgComponent *pEnumAdapters = NULL; + INetCfgComponent *pNetCfgAdapter = NULL; + INetCfgComponentBindings *pAdapterBindings = NULL; + IEnumNetCfgBindingPath *pEnumBp = NULL; + INetCfgBindingPath *pBp = NULL; + IEnumNetCfgBindingInterface *pEnumBi = NULL; + INetCfgBindingInterface *pBi = NULL; + INetCfgComponent *pUpperComponent = NULL; + + try + { + HRESULT hr = CoCreateInstance(CLSID_CNetCfg, NULL, CLSCTX_INPROC_SERVER, IID_INetCfg, (PVOID*)&pNetCfg); + if (FAILED(hr)) + throw RTCError(com::Utf8StrFmt("Failed to create instance of INetCfg, hr=0x%x.\n", hr)); + hr = pNetCfg->Initialize(NULL); + if (FAILED(hr)) + throw RTCError(com::Utf8StrFmt("Failed to initialize instance of INetCfg, hr=0x%x.\n", hr)); + + hr = pNetCfg->EnumComponents(&GUID_DEVCLASS_NET, &pEnumAdapters); + if (FAILED(hr)) + throw RTCError(com::Utf8StrFmt("Failed enumerate network adapters, hr=0x%x.\n", hr)); + + hr = pEnumAdapters->Reset(); + Assert(SUCCEEDED(hr)); + do + { + hr = pEnumAdapters->Next(1, &pNetCfgAdapter, NULL); + if (hr == S_FALSE) + break; + if (hr != S_OK) + throw RTCError(com::Utf8StrFmt("Failed to get next network adapter, hr=0x%x.\n", hr)); + hr = pNetCfgAdapter->QueryInterface(IID_INetCfgComponentBindings, (PVOID*)&pAdapterBindings); + if (FAILED(hr)) + throw RTCError(com::Utf8StrFmt("Failed to query INetCfgComponentBindings, hr=0x%x.\n", hr)); + hr = pAdapterBindings->EnumBindingPaths(EBP_ABOVE, &pEnumBp); + if (FAILED(hr)) + throw RTCError(com::Utf8StrFmt("Failed to enumerate binding paths, hr=0x%x.\n", hr)); + hr = pEnumBp->Reset(); + if (FAILED(hr)) + throw RTCError(com::Utf8StrFmt("Failed to reset enumeration of binding paths (0x%x)\n", hr)); + do + { + hr = pEnumBp->Next(1, &pBp, NULL); + if (hr == S_FALSE) + break; + if (hr != S_OK) + throw RTCError(com::Utf8StrFmt("Failed to get next network adapter, hr=0x%x.\n", hr)); + bool fBpEnabled; + hr = pBp->IsEnabled(); + if (hr == S_FALSE) + fBpEnabled = false; + else if (hr != S_OK) + throw RTCError(com::Utf8StrFmt("Failed to check if bind path is enabled, hr=0x%x.\n", hr)); + else + fBpEnabled = true; + hr = pBp->EnumBindingInterfaces(&pEnumBi); + if (FAILED(hr)) + throw RTCError(com::Utf8StrFmt("Failed to enumerate binding interfaces (0x%x)\n", hr)); + hr = pEnumBi->Reset(); + if (FAILED(hr)) + throw RTCError(com::Utf8StrFmt("Failed to reset enumeration of binding interfaces (0x%x)\n", hr)); + int ident; + for (ident = 0;; ++ident) + { + hr = pEnumBi->Next(1, &pBi, NULL); + if (hr == S_FALSE) + break; + if (hr != S_OK) + throw RTCError(com::Utf8StrFmt("Failed to get next binding interface, hr=0x%x.\n", hr)); + hr = pBi->GetUpperComponent(&pUpperComponent); + if (FAILED(hr)) + throw RTCError(com::Utf8StrFmt("Failed to get upper component, hr=0x%x.\n", hr)); + collectNetCfgComponentInfo(ident, fBpEnabled, pUpperComponent); + ReleaseAndReset(pUpperComponent); + ReleaseAndReset(pBi); + } + collectNetCfgComponentInfo(ident, fBpEnabled, pNetCfgAdapter); + ReleaseAndReset(pEnumBi); + ReleaseAndReset(pBp); + } while (true); + + ReleaseAndReset(pEnumBp); + ReleaseAndReset(pAdapterBindings); + ReleaseAndReset(pNetCfgAdapter); + } while (true); + ReleaseAndReset(pEnumAdapters); + ReleaseAndReset(pNetCfg); + } + + catch (RTCError &e) + { + ReleaseAndReset(pUpperComponent); + ReleaseAndReset(pBi); + ReleaseAndReset(pEnumBi); + ReleaseAndReset(pBp); + ReleaseAndReset(pEnumBp); + ReleaseAndReset(pAdapterBindings); + ReleaseAndReset(pNetCfgAdapter); + ReleaseAndReset(pEnumAdapters); + ReleaseAndReset(pNetCfg); + RTPrintf("ERROR in osCollect: %s\n", e.what()); + throw; + } + +} + + +class ErrorHandler +{ +public: + ErrorHandler(const char *pszFunction, int iLine) + : m_function(pszFunction), m_line(iLine) + { } + + void handleWinError(DWORD uError, const char *pszMsgFmt, ...) + { + if (uError != ERROR_SUCCESS) + { + va_list va; + va_start(va, pszMsgFmt); + RTCString msgArgs(pszMsgFmt, va); + va_end(va); + + LPSTR pBuf = NULL; + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, uError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&pBuf, 0, NULL); + RTCStringFmt msg("%s at %s(%d): err=%u %s", msgArgs.c_str(), m_function, m_line, uError, pBuf); + LocalFree(pBuf); + throw RTCError(msg.c_str()); + } + } + +private: + const char *m_function; + int m_line; +}; +#define handleWinError ErrorHandler(__FUNCTION__, __LINE__).handleWinError + + +class BugReportUsbTreeWin : public BugReportStream +{ +public: + BugReportUsbTreeWin(); + virtual ~BugReportUsbTreeWin(); + virtual RTVFSIOSTREAM getStream(void) { enumerate(); return BugReportStream::getStream(); } +private: + class AutoHandle { + public: + AutoHandle(HANDLE h) { m_h = h; } + ~AutoHandle() { close(); } + bool isValid() { return m_h != INVALID_HANDLE_VALUE; } + operator HANDLE() { return m_h; } + void close(void) { if (isValid()) { CloseHandle(m_h); m_h = INVALID_HANDLE_VALUE; } } + private: + HANDLE m_h; + }; + void enumerate(); + + void enumerateController(PSP_DEVINFO_DATA pInfoData, PSP_DEVICE_INTERFACE_DATA pInterfaceData); + void enumerateHub(RTCString strFullName, RTCString strPrefix); + void enumeratePorts(HANDLE hHub, unsigned cPorts, RTCString strPrefix); + PBYTE getDeviceRegistryProperty(HDEVINFO hDev, PSP_DEVINFO_DATA pInfoData, DWORD uProperty, + DWORD uExpectedType, PDWORD puSize); + RTCString getDeviceRegistryPropertyString(HDEVINFO hDev, PSP_DEVINFO_DATA pInfoData, DWORD uProperty); + + RTCString getDeviceDescByDriverName(RTCString strDrvName); + RTCString getDriverKeyName(HANDLE hHub, int iPort); + RTCString getExternalHubName(HANDLE hHub, int iPort); + + HDEVINFO m_hDevInfo; + PSP_DEVICE_INTERFACE_DETAIL_DATA m_pDetailData; + HANDLE m_hHostCtrlDev; +}; + +BugReportUsbTreeWin::BugReportUsbTreeWin() : BugReportStream("HostUsbTree") +{ + m_hDevInfo = INVALID_HANDLE_VALUE; + m_pDetailData = NULL; + m_hHostCtrlDev = INVALID_HANDLE_VALUE; +} + +BugReportUsbTreeWin::~BugReportUsbTreeWin() +{ + if (m_hHostCtrlDev != INVALID_HANDLE_VALUE) + CloseHandle(m_hHostCtrlDev); + if (m_pDetailData) + RTMemFree(m_pDetailData); + if (m_hDevInfo != INVALID_HANDLE_VALUE) + SetupDiDestroyDeviceInfoList(m_hDevInfo); +} + + +PBYTE BugReportUsbTreeWin::getDeviceRegistryProperty(HDEVINFO hDev, + PSP_DEVINFO_DATA pInfoData, + DWORD uProperty, + DWORD uExpectedType, + PDWORD puSize) +{ + DWORD uActualType, cbNeeded = 0; + if (!SetupDiGetDeviceRegistryProperty(hDev, pInfoData, uProperty, &uActualType, + NULL, 0, &cbNeeded) + && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + if (GetLastError() == ERROR_INVALID_DATA) + return NULL; + handleWinError(GetLastError(), "SetupDiGetDeviceRegistryProperty(0x%x) failed", uProperty); + } + if (uExpectedType != REG_NONE && uActualType != uExpectedType) + throw RTCError(RTCStringFmt("SetupDiGetDeviceRegistryProperty(0x%x) returned type %d instead of %d", + uActualType, uExpectedType).c_str()); + PBYTE pBuffer = (PBYTE)RTMemAlloc(cbNeeded); + if (!pBuffer) + throw RTCError(RTCStringFmt("Failed to allocate %u bytes", cbNeeded).c_str()); + if (!SetupDiGetDeviceRegistryProperty(hDev, pInfoData, uProperty, NULL, + pBuffer, cbNeeded, &cbNeeded)) + { + DWORD dwErr = GetLastError(); + RTMemFree(pBuffer); + pBuffer = NULL; + handleWinError(dwErr, "SetupDiGetDeviceRegistryProperty(0x%x) failed", uProperty); + } + if (puSize) + *puSize = cbNeeded; + + return pBuffer; +} + +RTCString BugReportUsbTreeWin::getDeviceRegistryPropertyString(HDEVINFO hDev, PSP_DEVINFO_DATA pInfoData, DWORD uProperty) +{ + PWSTR pUnicodeString = (PWSTR)getDeviceRegistryProperty(hDev, pInfoData, uProperty, REG_SZ, NULL); + + if (!pUnicodeString) + return RTCString(); + + RTCStringFmt utf8string("%ls", pUnicodeString); + RTMemFree(pUnicodeString); + return utf8string; +} + + +RTCString BugReportUsbTreeWin::getDeviceDescByDriverName(RTCString strDrvName) +{ + DWORD dwErr; + SP_DEVINFO_DATA devInfoData; + HDEVINFO hDevInfo = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT); + + if (hDevInfo == INVALID_HANDLE_VALUE) + handleWinError(GetLastError(), "SetupDiGetClassDevs failed"); + + bool fFound = false; + devInfoData.cbSize = sizeof(devInfoData); + for (int i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); ++i) + { + if (getDeviceRegistryPropertyString(hDevInfo, &devInfoData, SPDRP_DRIVER).equals(strDrvName)) + { + fFound = true; + break; + } + } + if (!fFound) + { + dwErr = GetLastError(); + SetupDiDestroyDeviceInfoList(hDevInfo); + handleWinError(dwErr, "SetupDiEnumDeviceInfo failed"); + } + + RTCString strDesc = getDeviceRegistryPropertyString(hDevInfo, &devInfoData, SPDRP_DEVICEDESC); + SetupDiDestroyDeviceInfoList(hDevInfo); + return strDesc; +} + + +RTCString BugReportUsbTreeWin::getDriverKeyName(HANDLE hHub, int iPort) +{ + USB_NODE_CONNECTION_DRIVERKEY_NAME name; + ULONG cbNeeded = 0; + + name.ConnectionIndex = iPort; + if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, + &name, sizeof(name), &name, sizeof(name), &cbNeeded, NULL)) + handleWinError(GetLastError(), "DeviceIoControl(IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME) failed"); + cbNeeded = name.ActualLength; + PUSB_NODE_CONNECTION_DRIVERKEY_NAME pName = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)RTMemAlloc(cbNeeded); + if (!pName) + throw RTCError(RTCStringFmt("Failed to allocate %u bytes", cbNeeded).c_str()); + pName->ConnectionIndex = iPort; + if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, + pName, cbNeeded, pName, cbNeeded, &cbNeeded, NULL)) + { + DWORD dwErr = GetLastError(); + RTMemFree(pName); + handleWinError(dwErr, "DeviceIoControl(IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME) failed"); + } + RTCStringFmt strName("%ls", pName->DriverKeyName); + RTMemFree(pName); + return strName; +} + + +RTCString BugReportUsbTreeWin::getExternalHubName(HANDLE hHub, int iPort) +{ + USB_NODE_CONNECTION_NAME name; + ULONG cbNeeded = 0; + + name.ConnectionIndex = iPort; + if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_NAME, + &name, sizeof(name), &name, sizeof(name), &cbNeeded, NULL)) + handleWinError(GetLastError(), "DeviceIoControl(IOCTL_USB_GET_NODE_CONNECTION_NAME) failed"); + cbNeeded = name.ActualLength; + PUSB_NODE_CONNECTION_NAME pName = (PUSB_NODE_CONNECTION_NAME)RTMemAlloc(cbNeeded); + if (!pName) + throw RTCError(RTCStringFmt("Failed to allocate %u bytes", cbNeeded).c_str()); + pName->ConnectionIndex = iPort; + if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_NAME, + pName, cbNeeded, pName, cbNeeded, &cbNeeded, NULL)) + { + DWORD dwErr = GetLastError(); + RTMemFree(pName); + handleWinError(dwErr, "DeviceIoControl(IOCTL_USB_GET_NODE_CONNECTION_NAME) failed"); + } + RTCStringFmt strName("%ls", pName->NodeName); + RTMemFree(pName); + return strName; +} + + +void BugReportUsbTreeWin::enumeratePorts(HANDLE hHub, unsigned cPorts, RTCString strPrefix) +{ + DWORD cbInfo = sizeof(USB_NODE_CONNECTION_INFORMATION_EX) + 30 * sizeof(USB_PIPE_INFO); + PUSB_NODE_CONNECTION_INFORMATION_EX pInfo = (PUSB_NODE_CONNECTION_INFORMATION_EX)RTMemAlloc(cbInfo); + if (!pInfo) + throw RTCError(RTCStringFmt("Failed to allocate %u bytes", cbInfo).c_str()); + for (unsigned i = 1; i <= cPorts; ++i) + { + pInfo->ConnectionIndex = i; + if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, + pInfo, cbInfo, pInfo, cbInfo, &cbInfo, NULL)) + { + DWORD dwErr = GetLastError(); + RTMemFree(pInfo); + handleWinError(dwErr, "DeviceIoControl(IOCTL_USB_GET_NODE_CONNECTION_INFORMATION) failed"); + } + if (pInfo->ConnectionStatus == NoDeviceConnected) + printf("%s[Port %d]\n", strPrefix.c_str(), i); + else + { + RTCString strName = getDeviceDescByDriverName(getDriverKeyName(hHub, i)); + printf("%s[Port %d] %s\n", strPrefix.c_str(), i, strName.c_str()); + if (pInfo->DeviceIsHub) + enumerateHub(getExternalHubName(hHub, i), strPrefix + " "); + } + } + RTMemFree(pInfo); +} + +void BugReportUsbTreeWin::enumerateHub(RTCString strFullName, RTCString strPrefix) +{ + AutoHandle hHubDev(CreateFileA(RTCString("\\\\.\\").append(strFullName).c_str(), + GENERIC_WRITE, FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL)); + if (!hHubDev.isValid()) + handleWinError(GetLastError(), "CreateFile(%s) failed", strFullName.c_str()); + ULONG cb; + USB_NODE_INFORMATION hubInfo; + if (!DeviceIoControl(hHubDev, + IOCTL_USB_GET_NODE_INFORMATION, + &hubInfo, + sizeof(USB_NODE_INFORMATION), + &hubInfo, + sizeof(USB_NODE_INFORMATION), + &cb, + NULL)) + handleWinError(GetLastError(), "DeviceIoControl(IOCTL_USB_GET_NODE_INFORMATION) failed"); + enumeratePorts(hHubDev, hubInfo.u.HubInformation.HubDescriptor.bNumberOfPorts, strPrefix); +} + +void BugReportUsbTreeWin::enumerateController(PSP_DEVINFO_DATA pInfoData, PSP_DEVICE_INTERFACE_DATA pInterfaceData) +{ + RT_NOREF(pInterfaceData); + RTCString strCtrlDesc = getDeviceRegistryPropertyString(m_hDevInfo, pInfoData, SPDRP_DEVICEDESC); + printf("%s\n", strCtrlDesc.c_str()); + + ULONG cbNeeded; + USB_ROOT_HUB_NAME rootHub; + /* Find out the name length first */ + if (!DeviceIoControl(m_hHostCtrlDev, IOCTL_USB_GET_ROOT_HUB_NAME, NULL, 0, + &rootHub, sizeof(rootHub), + &cbNeeded, NULL)) + handleWinError(GetLastError(), "DeviceIoControl(IOCTL_USB_GET_ROOT_HUB_NAME) failed"); + cbNeeded = rootHub.ActualLength; + PUSB_ROOT_HUB_NAME pUnicodeName = (PUSB_ROOT_HUB_NAME)RTMemAlloc(cbNeeded); + if (!pUnicodeName) + throw RTCError(RTCStringFmt("Failed to allocate %u bytes", cbNeeded).c_str()); + + if (!DeviceIoControl(m_hHostCtrlDev, IOCTL_USB_GET_ROOT_HUB_NAME, NULL, 0, + pUnicodeName, cbNeeded, + &cbNeeded, NULL)) + { + DWORD dwErr = GetLastError(); + RTMemFree(pUnicodeName); + handleWinError(dwErr, "DeviceIoControl(IOCTL_USB_GET_ROOT_HUB_NAME) failed"); + } + + RTCStringFmt strRootHubName("%ls", pUnicodeName->RootHubName); + RTMemFree(pUnicodeName); + printf(" Root Hub\n"); + enumerateHub(strRootHubName, " "); +} + +void BugReportUsbTreeWin::enumerate() +{ + m_hDevInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVINTERFACE_USB_HOST_CONTROLLER, NULL, NULL, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (m_hDevInfo == INVALID_HANDLE_VALUE) + handleWinError(GetLastError(), "SetupDiGetClassDevs(GUID_DEVINTERFACE_USB_HOST_CONTROLLER) failed"); + + SP_DEVINFO_DATA deviceInfoData; + deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + for (int i = 0; SetupDiEnumDeviceInfo(m_hDevInfo, i, &deviceInfoData); ++i) + { + if (m_hHostCtrlDev != INVALID_HANDLE_VALUE) + CloseHandle(m_hHostCtrlDev); + if (m_pDetailData) + RTMemFree(m_pDetailData); + + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if (!SetupDiEnumDeviceInterfaces(m_hDevInfo, 0, (LPGUID)&GUID_DEVINTERFACE_USB_HOST_CONTROLLER, + i, &deviceInterfaceData)) + handleWinError(GetLastError(), "SetupDiEnumDeviceInterfaces(GUID_DEVINTERFACE_USB_HOST_CONTROLLER) failed"); + + ULONG cbNeeded = 0; + if (!SetupDiGetDeviceInterfaceDetail(m_hDevInfo, &deviceInterfaceData, NULL, 0, &cbNeeded, NULL) + && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + handleWinError(GetLastError(), "SetupDiGetDeviceInterfaceDetail failed"); + + m_pDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)RTMemAlloc(cbNeeded); + if (!m_pDetailData) + throw RTCError(RTCStringFmt("Failed to allocate %u bytes", cbNeeded).c_str()); + + m_pDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + if (!SetupDiGetDeviceInterfaceDetail(m_hDevInfo, &deviceInterfaceData, m_pDetailData, cbNeeded, &cbNeeded, NULL)) + handleWinError(GetLastError(), "SetupDiGetDeviceInterfaceDetail failed"); + + m_hHostCtrlDev = CreateFile(m_pDetailData->DevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (m_hHostCtrlDev == INVALID_HANDLE_VALUE) + handleWinError(GetLastError(), "CreateFile(%ls) failed", m_pDetailData); + + enumerateController(&deviceInfoData, &deviceInterfaceData); + } +} + +class BugReportDriversWin : public BugReportStream +{ +public: + BugReportDriversWin(); + virtual ~BugReportDriversWin(); + virtual RTVFSIOSTREAM getStream(void) { enumerateDrivers(); return BugReportStream::getStream(); } +private: + void enumerateDrivers(void); + + WCHAR *m_pwszSystemRoot; + UINT m_cSystemRoot; + LPVOID *m_pDrivers; + DWORD m_cDrivers; + LPVOID m_pVerInfo; + DWORD m_cbVerInfo; +}; + +BugReportDriversWin::BugReportDriversWin() : BugReportStream("DriverVersions") +{ + m_cSystemRoot = MAX_PATH; + m_pwszSystemRoot = new WCHAR[MAX_PATH]; + m_cDrivers = 1024; + m_pDrivers = new LPVOID[m_cDrivers]; + m_pVerInfo = NULL; + m_cbVerInfo = 0; +} + +BugReportDriversWin::~BugReportDriversWin() +{ + if (m_pVerInfo) + RTMemTmpFree(m_pVerInfo); + delete[] m_pDrivers; + delete[] m_pwszSystemRoot; +} + +void BugReportDriversWin::enumerateDrivers() +{ + UINT cNeeded = GetWindowsDirectory(m_pwszSystemRoot, m_cSystemRoot); + if (cNeeded > m_cSystemRoot) + { + /* Re-allocate and try again */ + m_cSystemRoot = cNeeded; + delete[] m_pwszSystemRoot; + m_pwszSystemRoot = new WCHAR[m_cSystemRoot]; + cNeeded = GetWindowsDirectory(m_pwszSystemRoot, m_cSystemRoot); + } + if (cNeeded == 0) + handleWinError(GetLastError(), "GetWindowsDirectory failed"); + + DWORD cbNeeded = 0; + if ( !EnumDeviceDrivers(m_pDrivers, m_cDrivers * sizeof(m_pDrivers[0]), &cbNeeded) + || cbNeeded > m_cDrivers * sizeof(m_pDrivers[0])) + { + /* Re-allocate and try again */ + m_cDrivers = cbNeeded / sizeof(m_pDrivers[0]); + delete[] m_pDrivers; + m_pDrivers = new LPVOID[m_cDrivers]; + if (!EnumDeviceDrivers(m_pDrivers, cbNeeded, &cbNeeded)) + handleWinError(GetLastError(), "EnumDeviceDrivers failed (%p, %u)", m_pDrivers, cbNeeded); + } + + WCHAR wszDriver[1024]; + for (unsigned i = 0; i < m_cDrivers; i++) + { + if (GetDeviceDriverBaseName(m_pDrivers[i], wszDriver, RT_ELEMENTS(wszDriver))) + { + if (_wcsnicmp(L"vbox", wszDriver, 4)) + continue; + } + else + continue; + if (GetDeviceDriverFileName(m_pDrivers[i], wszDriver, RT_ELEMENTS(wszDriver))) + { + WCHAR wszTmpDrv[1024]; + WCHAR *pwszDrv = wszDriver; + if (!wcsncmp(L"\\SystemRoot", wszDriver, 11)) + { + wcsncpy_s(wszTmpDrv, m_pwszSystemRoot, m_cSystemRoot); + wcsncat_s(wszTmpDrv, wszDriver + 11, RT_ELEMENTS(wszTmpDrv) - m_cSystemRoot); + pwszDrv = wszTmpDrv; + } + else if (!wcsncmp(L"\\??\\", wszDriver, 4)) + pwszDrv = wszDriver + 4; + + + /* Allocate a buffer for version info. Reuse if large enough. */ + DWORD cbNewVerInfo = GetFileVersionInfoSize(pwszDrv, NULL); + if (cbNewVerInfo > m_cbVerInfo) + { + if (m_pVerInfo) + RTMemTmpFree(m_pVerInfo); + m_cbVerInfo = cbNewVerInfo; + m_pVerInfo = RTMemTmpAlloc(m_cbVerInfo); + if (!m_pVerInfo) + throw RTCError(RTCStringFmt("Failed to allocate %u bytes", m_cbVerInfo).c_str()); + } + + if (GetFileVersionInfo(pwszDrv, NULL, m_cbVerInfo, m_pVerInfo)) + { + UINT cbSize = 0; + LPBYTE lpBuffer = NULL; + if (VerQueryValue(m_pVerInfo, L"\\", (VOID FAR* FAR*)&lpBuffer, &cbSize)) + { + if (cbSize) + { + VS_FIXEDFILEINFO *pFileInfo = (VS_FIXEDFILEINFO *)lpBuffer; + if (pFileInfo->dwSignature == 0xfeef04bd) + { + printf("%ls (Version: %d.%d.%d.%d)\n", pwszDrv, + (pFileInfo->dwFileVersionMS >> 16) & 0xffff, + (pFileInfo->dwFileVersionMS >> 0) & 0xffff, + (pFileInfo->dwFileVersionLS >> 16) & 0xffff, + (pFileInfo->dwFileVersionLS >> 0) & 0xffff); + } + else + printf("%ls - invalid signature\n", pwszDrv); + } + else + printf("%ls - version info size is 0\n", pwszDrv); + } + else + printf("%ls - failed to query version info size\n", pwszDrv); + } + else + printf("%ls - failed to get version info with 0x%x\n", pwszDrv, GetLastError()); + } + else + printf("%ls - GetDeviceDriverFileName failed with 0x%x\n", wszDriver, GetLastError()); + } +} + + +class BugReportFilterRegistryWin : public BugReportFilter +{ +public: + BugReportFilterRegistryWin() {}; + virtual ~BugReportFilterRegistryWin() {}; + virtual void *apply(void *pvSource, size_t *pcbInOut); +}; + +void *BugReportFilterRegistryWin::apply(void *pvSource, size_t *pcbInOut) +{ + /* + * The following implementation is not optimal by any means. It serves to + * illustrate and test the case when filter's output is longer than its + * input. + */ + RT_NOREF(pcbInOut); + /* Registry export files are encoded in UTF-16 (little endian on Intel x86). */ + void *pvDest = pvSource; + uint16_t *pwsSource = (uint16_t *)pvSource; + if (*pwsSource++ == 0xFEFF && *pcbInOut > 48) + { + if (!memcmp(pwsSource, L"Windows Registry Editor", 46)) + { + *pcbInOut += 2; + pvDest = allocateBuffer(*pcbInOut); + uint16_t *pwsDest = (uint16_t *)pvDest; + *pwsDest++ = 0xFEFF; + *pwsDest++ = '#'; + /* Leave space for 0xFEFF and '#' */ + memcpy(pwsDest, pwsSource, *pcbInOut - 4); + } + } + return pvDest; +} + + +void createBugReportOsSpecific(BugReport* report, const char *pszHome) +{ + RT_NOREF(pszHome); + WCHAR szWinDir[MAX_PATH]; + + int cbNeeded = GetWindowsDirectory(szWinDir, RT_ELEMENTS(szWinDir)); + if (cbNeeded == 0) + throw RTCError(RTCStringFmt("Failed to get Windows directory (err=%d)\n", GetLastError())); + if (cbNeeded > MAX_PATH) + throw RTCError(RTCStringFmt("Failed to get Windows directory (needed %d-byte buffer)\n", cbNeeded)); + RTCStringFmt WinInfDir("%ls/inf", szWinDir); + report->addItem(new BugReportFile(PathJoin(WinInfDir.c_str(), "setupapi.app.log"), "setupapi.app.log")); + report->addItem(new BugReportFile(PathJoin(WinInfDir.c_str(), "setupapi.dev.log"), "setupapi.dev.log")); + report->addItem(new BugReportNetworkAdaptersWin); + RTCStringFmt WinSysDir("%ls/System32", szWinDir); + report->addItem(new BugReportCommand("IpConfig", PathJoin(WinSysDir.c_str(), "ipconfig.exe"), "/all", NULL)); + report->addItem(new BugReportCommand("RouteTable", PathJoin(WinSysDir.c_str(), "netstat.exe"), "-rn", NULL)); + report->addItem(new BugReportCommand("SystemEvents", PathJoin(WinSysDir.c_str(), "wevtutil.exe"), + "qe", "System", + "/q:*[System[Provider[@Name='VBoxUSBMon' or @Name='VBoxNetLwf']]]", NULL)); + report->addItem(new BugReportCommand("UpdateHistory", PathJoin(WinSysDir.c_str(), "wbem/wmic.exe"), + "qfe", "list", "brief", NULL)); + report->addItem(new BugReportCommand("DriverServices", PathJoin(WinSysDir.c_str(), "sc.exe"), + "query", "type=", "driver", "state=", "all", NULL)); + report->addItem(new BugReportCommand("DriverStore", PathJoin(WinSysDir.c_str(), "pnputil.exe"), "-e", NULL)); + report->addItem(new BugReportCommandTemp("RegDevKeys", PathJoin(WinSysDir.c_str(), "reg.exe"), "export", + "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\Root\\NET", NULL), + new BugReportFilterRegistryWin()); + report->addItem(new BugReportCommandTemp("RegDrvKeys", PathJoin(WinSysDir.c_str(), "reg.exe"), "export", + "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", NULL), + new BugReportFilterRegistryWin()); + report->addItem(new BugReportCommandTemp("RegNetwork", PathJoin(WinSysDir.c_str(), "reg.exe"), "export", + "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Network", NULL), + new BugReportFilterRegistryWin()); + report->addItem(new BugReportCommandTemp("RegNetFltNobj", PathJoin(WinSysDir.c_str(), "reg.exe"), "export", + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\CLSID\\{f374d1a0-bf08-4bdc-9cb2-c15ddaeef955}", NULL), + new BugReportFilterRegistryWin()); + report->addItem(new BugReportUsbTreeWin); + report->addItem(new BugReportDriversWin); +} |