diff options
| author | pepper <peppersclothescult@gmail.com> | 2015-01-10 21:32:32 -0800 |
|---|---|---|
| committer | pepper <peppersclothescult@gmail.com> | 2015-01-10 21:32:32 -0800 |
| commit | d53fa8a169832563c62262078b8d2ffe5cab8473 (patch) | |
| tree | b911d06d357d009c976709780f10e92ce915228a /source/logging/ErrorReporter.c | |
first
Diffstat (limited to 'source/logging/ErrorReporter.c')
| -rw-r--r-- | source/logging/ErrorReporter.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/source/logging/ErrorReporter.c b/source/logging/ErrorReporter.c new file mode 100644 index 0000000..29f6ac7 --- /dev/null +++ b/source/logging/ErrorReporter.c @@ -0,0 +1,286 @@ +// +// ErrorReport.c - MrsWatson +// Created by Nik Reiman on 9/22/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "app/BuildInfo.h" +#include "base/File.h" +#include "base/PlatformInfo.h" +#include "logging/ErrorReporter.h" +#include "logging/EventLogger.h" + +#if WINDOWS +#include <Shlobj.h> +#endif + +static const char *kErrorReportInfoText = "MrsWatson is now running \ +in error report mode, which will generate a report on your desktop with \ +any input/output sources and error logs. This also enables some extra \ +arguments and will disable console logging.\n"; +static const char *kErrorReportCopyPluginsPromptText = "Would you like to copy the \ +plugin to the report? All data sent to the official support address is kept \ +strictly confidential. If the plugin in question has copy protection (or is \ +cracked), or depends on external resources, this probably won't work. But if \ +the plugin can be copied, it greatly helps in fixing bugs.\n\ +Copy the plugin? (y/n) "; + + +ErrorReporter newErrorReporter(void) +{ + ErrorReporter errorReporter = (ErrorReporter)malloc(sizeof(ErrorReporterMembers)); + + errorReporter->started = false; + errorReporter->completed = false; + errorReporter->reportName = newCharString(); + + errorReporter->desktopPath = newCharString(); + errorReporter->reportDirPath = newCharString(); + + return errorReporter; +} + +void errorReporterInitialize(ErrorReporter self) +{ + CharString infoText = newCharStringWithCString(kErrorReportInfoText); + CharString wrappedInfoText; + time_t now; + size_t length; + size_t i; + + printf("=== Starting error report ===\n"); + wrappedInfoText = charStringWrap(infoText, 0); + // The second newline here is intentional + printf("%s\n", wrappedInfoText->data); + + time(&now); + self->started = true; + + snprintf(self->reportName->data, self->reportName->capacity, + "MrsWatson Report %s", ctime(&now)); + // Trim the final newline character from this string if it exists + length = strlen(self->reportName->data); + + if (self->reportName->data[length - 1] == '\n') { + self->reportName->data[length - 1] = '\0'; + length--; + } + + for (i = 0; i < length; i++) { + if (!(charStringIsLetter(self->reportName, i) || + charStringIsNumber(self->reportName, i))) { + self->reportName->data[i] = '-'; + } + } + +#if UNIX + snprintf(self->desktopPath->data, self->desktopPath->capacity, + "%s/Desktop", getenv("HOME")); +#elif WINDOWS + SHGetFolderPathA(NULL, CSIDL_DESKTOPDIRECTORY, NULL, 0, self->desktopPath->data); +#endif + + // Try to place the report on the user's desktop. However, if we cannot find + // the desktop (which may occur with localized Linux installations, for instance), + // then just dump it in the current directory instead. + File desktopPath = newFileWithPath(self->desktopPath); + File reportPath; + + if (!fileExists(desktopPath)) { + logWarn("Could not find desktop location, placing error report in current directory instead"); + CharString currentDirString = fileGetCurrentDirectory(); + File currentDir = newFileWithPath(currentDirString); + reportPath = newFileWithParent(currentDir, self->reportName); + freeFile(currentDir); + freeCharString(currentDirString); + } else { + reportPath = newFileWithParent(desktopPath, self->reportName); + freeFile(desktopPath); + } + + if (fileExists(reportPath)) { + logCritical("The path '%s' already contains a previous error report. Please remove the report data and try again."); + } else { + fileCreate(reportPath, kFileTypeDirectory); + } + + // Now we should have a real error report path + charStringCopy(self->reportDirPath, reportPath->absolutePath); + + freeFile(reportPath); + freeCharString(wrappedInfoText); + freeCharString(infoText); +} + +void errorReporterCreateLauncher(ErrorReporter self, int argc, char *argv[]) +{ + CharString outScriptName = newCharString(); + FILE *scriptFilePointer; + int i; + + charStringCopyCString(outScriptName, "run.sh"); + errorReporterRemapPath(self, outScriptName); + scriptFilePointer = fopen(outScriptName->data, "w"); + fprintf(scriptFilePointer, "#!/bin/sh\n"); + fprintf(scriptFilePointer, "mrswatson"); + + for (i = 1; i < argc; i++) { + // Don't run with the error report option again + if (strcmp(argv[i], "--error-report")) { + fprintf(scriptFilePointer, " %s", argv[i]); + } + } + + fprintf(scriptFilePointer, "\n"); + fclose(scriptFilePointer); +} + +void errorReporterRemapPath(ErrorReporter self, CharString path) +{ + File pathAsFile = newFileWithPath(path); + CharString basename = fileGetBasename(pathAsFile); + File parent = newFileWithPath(self->reportDirPath); + File remappedPath = newFileWithParent(parent, basename); + + charStringCopy(path, remappedPath->absolutePath); + + freeCharString(basename); + freeFile(parent); + freeFile(pathAsFile); + freeFile(remappedPath); +} + +boolByte errorReportCopyFileToReport(ErrorReporter self, CharString path) +{ + boolByte success; + + // Copy the destination path so that the original is not modified + CharString destination = newCharString(); + charStringCopy(destination, path); + errorReporterRemapPath(self, destination); + + File reportDirPath = newFileWithPath(self->reportDirPath); + File distinationPath = newFileWithPath(path); + File result = fileCopyTo(distinationPath, reportDirPath); + success = fileExists(result); + + freeCharString(destination); + freeFile(reportDirPath); + freeFile(distinationPath); + freeFile(result); + return success; +} + +// This could live in File, however this is currently the only place it is being used +// and also it's a rather cheap hack, so I would prefer to keep it as a static function +// here until another use-case presents itself. If that should happen, then we should +// refactor this code properly and move it to File. +static boolByte _copyDirectoryToErrorReportDir(ErrorReporter self, CharString path) +{ + boolByte success; + +#if UNIX + int result; + CharString copyCommand = newCharString(); + // TODO: This is the lazy way of doing this... + snprintf(copyCommand->data, copyCommand->capacity, "/bin/cp -r \"%s\" \"%s\"", + path->data, self->reportDirPath->data); + result = system(copyCommand->data); + success = (boolByte)(WEXITSTATUS(result) == 0); + + if (!success) { + logError("Could not copy '%s' to '%s'\n", path->data, self->reportDirPath->data); + } + +#else + logUnsupportedFeature("Copy directory recursively"); + success = false; +#endif + + return success; +} + +boolByte errorReporterShouldCopyPlugins(void) +{ + CharString promptText = newCharStringWithCString(kErrorReportCopyPluginsPromptText); + CharString wrappedPromptText; + char response; + + wrappedPromptText = charStringWrap(promptText, 0); + printf("%s", wrappedPromptText->data); + freeCharString(wrappedPromptText); + freeCharString(promptText); + + response = (char)getchar(); + return (boolByte)(response == 'y' || response == 'Y'); +} + +boolByte errorReporterCopyPlugins(ErrorReporter self, PluginChain pluginChain) +{ + CharString pluginAbsolutePath = NULL; + Plugin currentPlugin = NULL; + boolByte failed = false; + unsigned int i; + PlatformInfo platform = newPlatformInfo(); + + for (i = 0; i < pluginChain->numPlugins; i++) { + currentPlugin = pluginChain->plugins[i]; + pluginAbsolutePath = currentPlugin->pluginAbsolutePath; + + if (charStringIsEmpty(pluginAbsolutePath)) { + logInfo("Plugin '%s' does not have an absolute path and could not be copied", currentPlugin->pluginName->data); + } else if (platform->type == PLATFORM_MACOSX) { + failed |= !_copyDirectoryToErrorReportDir(self, pluginAbsolutePath); + } else { + failed |= !errorReportCopyFileToReport(self, pluginAbsolutePath); + } + } + + freePlatformInfo(platform); + return (boolByte)!failed; +} + +void errorReporterClose(ErrorReporter self) +{ + // Always do this, just in case + flushErrorLog(); + + printf("\n=== Error report complete ===\n"); + printf("Created error report at %s\n", self->reportDirPath->data); + printf("Please compress and email the report to: %s\n", SUPPORT_EMAIL); + printf("Thanks!\n"); +} + +void freeErrorReporter(ErrorReporter errorReporter) +{ + freeCharString(errorReporter->reportName); + freeCharString(errorReporter->reportDirPath); + freeCharString(errorReporter->desktopPath); + free(errorReporter); +} |
