sm-ext-accelerator-2023/extension/extension.cpp

376 lines
10 KiB
C++
Raw Normal View History

2013-05-16 08:18:31 +02:00
/*
* =============================================================================
* Accelerator Extension
* Copyright (C) 2011 Asher Baker (asherkin). All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "extension.h"
#include <IWebternet.h>
#include "MemoryDownloader.h"
2013-05-18 01:46:46 +02:00
#if defined _LINUX
2013-05-16 08:18:31 +02:00
#include "client/linux/handler/exception_handler.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
2013-05-16 08:18:31 +02:00
#include <signal.h>
#include <dirent.h>
#include <unistd.h>
2013-05-18 01:46:46 +02:00
#elif defined _WINDOWS
#define _STDINT // ~.~
#include "client/windows/handler/exception_handler.h"
2014-11-08 02:45:19 +01:00
#else
#error Bad platform.
2013-05-18 01:46:46 +02:00
#endif
2013-05-16 08:18:31 +02:00
Accelerator g_accelerator;
SMEXT_LINK(&g_accelerator);
IWebternet *webternet;
static IThreadHandle *uploadThread;
char buffer[512];
2013-05-16 08:18:31 +02:00
google_breakpad::ExceptionHandler *handler = NULL;
struct PluginInfo {
unsigned int serial;
PluginStatus status;
char filename[256];
char name[256];
char author[256];
char description[256];
char version[256];
char url[256];
};
unsigned int plugin_count;
PluginInfo plugins[256];
2013-05-18 01:46:46 +02:00
#if defined _LINUX
2013-05-16 08:18:31 +02:00
void (*SignalHandler)(int, siginfo_t *, void *);
const int kExceptionSignals[] = {
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS
};
const int kNumHandledSignals = sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded)
{
//printf("Wrote minidump to: %s\n", descriptor.path());
sys_write(STDOUT_FILENO, "Wrote minidump to: ", 19);
sys_write(STDOUT_FILENO, descriptor.path(), my_strlen(descriptor.path()));
sys_write(STDOUT_FILENO, "\n", 1);
my_strlcpy(buffer, descriptor.path(), sizeof(buffer));
my_strlcat(buffer, ".txt", sizeof(buffer));
int extra = sys_open(buffer, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (extra == -1) {
sys_write(STDOUT_FILENO, "Failed to open metadata file!\n", 30);
return succeeded;
}
char pis[64];
char pds[32];
for (unsigned i = 0; i < plugin_count; ++i) {
PluginInfo *p = &plugins[i];
if (p->serial == 0) continue;
my_uitos(pds, i, my_uint_len(i));
pds[my_uint_len(i)] = '\0';
my_strlcpy(pis, "plugin[", sizeof(pis));
my_strlcat(pis, pds, sizeof(pis));
my_strlcat(pis, "].", sizeof(pis));
sys_write(extra, pis, my_strlen(pis));
sys_write(extra, "filename=", 9);
sys_write(extra, p->filename, my_strlen(p->filename));
sys_write(extra, "\n", 1);
sys_write(extra, pis, my_strlen(pis));
sys_write(extra, "name=", 5);
sys_write(extra, p->name, my_strlen(p->name));
sys_write(extra, "\n", 1);
sys_write(extra, pis, my_strlen(pis));
sys_write(extra, "author=", 7);
sys_write(extra, p->author, my_strlen(p->author));
sys_write(extra, "\n", 1);
sys_write(extra, pis, my_strlen(pis));
sys_write(extra, "description=", 12);
sys_write(extra, p->description, my_strlen(p->description));
sys_write(extra, "\n", 1);
sys_write(extra, pis, my_strlen(pis));
sys_write(extra, "version=", 8);
sys_write(extra, p->version, my_strlen(p->version));
sys_write(extra, "\n", 1);
sys_write(extra, pis, my_strlen(pis));
sys_write(extra, "url=", 4);
sys_write(extra, p->url, my_strlen(p->url));
sys_write(extra, "\n", 1);
}
sys_close(extra);
2013-05-16 08:18:31 +02:00
return succeeded;
}
void OnGameFrame(bool simulating)
{
bool weHaveBeenFuckedOver = false;
struct sigaction oact;
for (int i = 0; i < kNumHandledSignals; ++i) {
sigaction(kExceptionSignals[i], NULL, &oact);
if (oact.sa_sigaction != SignalHandler) {
weHaveBeenFuckedOver = true;
break;
}
}
if (!weHaveBeenFuckedOver) {
return;
}
struct sigaction act;
memset(&act, 0, sizeof(act));
sigemptyset(&act.sa_mask);
for (int i = 0; i < kNumHandledSignals; ++i) {
sigaddset(&act.sa_mask, kExceptionSignals[i]);
}
act.sa_sigaction = SignalHandler;
act.sa_flags = SA_ONSTACK | SA_SIGINFO;
for (int i = 0; i < kNumHandledSignals; ++i) {
sigaction(kExceptionSignals[i], &act, NULL);
}
}
2013-05-18 01:46:46 +02:00
#elif defined _WINDOWS
2014-11-08 03:00:40 +01:00
void *vectoredHandler = NULL;
2013-05-18 01:46:46 +02:00
LONG CALLBACK BreakpadVectoredHandler(_In_ PEXCEPTION_POINTERS ExceptionInfo)
{
2013-05-19 04:28:09 +02:00
switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
2013-05-18 01:46:46 +02:00
{
2013-05-19 04:28:09 +02:00
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_INVALID_HANDLE:
2013-05-19 04:28:09 +02:00
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
case EXCEPTION_DATATYPE_MISALIGNMENT:
case EXCEPTION_ILLEGAL_INSTRUCTION:
2014-10-31 23:42:09 +01:00
case EXCEPTION_INT_DIVIDE_BY_ZERO:
2013-05-19 04:28:09 +02:00
case EXCEPTION_STACK_OVERFLOW:
2015-02-14 16:58:07 +01:00
case 0xC0000409: // STATUS_STACK_BUFFER_OVERRUN
case 0xC0000374: // STATUS_HEAP_CORRUPTION
2013-05-19 04:28:09 +02:00
break;
2014-10-31 23:42:09 +01:00
case 0: // Valve use this for Sys_Error.
if ((ExceptionInfo->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) == 0)
return EXCEPTION_CONTINUE_SEARCH;
break;
2013-05-19 04:28:09 +02:00
default:
return EXCEPTION_CONTINUE_SEARCH;
2013-05-18 01:46:46 +02:00
}
if (handler->WriteMinidumpForException(ExceptionInfo))
{
// Stop the handler thread from deadlocking us.
delete handler;
// Stop Valve's handler being called.
ExceptionInfo->ExceptionRecord->ExceptionCode = EXCEPTION_BREAKPOINT;
return EXCEPTION_EXECUTE_HANDLER;
} else {
return EXCEPTION_CONTINUE_SEARCH;
}
}
static bool dumpCallback(const wchar_t* dump_path,
const wchar_t* minidump_id,
void* context,
EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion,
bool succeeded)
{
printf("Wrote minidump to: %ls\\%ls.dmp\n", dump_path, minidump_id);
return succeeded;
}
2014-11-08 02:45:19 +01:00
#else
#error Bad platform.
2013-05-18 01:46:46 +02:00
#endif
2013-05-16 08:18:31 +02:00
void UploadCrashDump(const char *path)
{
IWebForm *form = webternet->CreateForm();
form->AddString("UserID", g_pSM->GetCoreConfigValue("MinidumpAccount"));
2014-11-08 03:04:17 +01:00
form->AddString("GameDirectory", g_pSM->GetGameFolderName());
2014-11-08 02:50:04 +01:00
form->AddString("ExtensionVersion", SMEXT_CONF_VERSION);
2013-05-16 08:18:31 +02:00
form->AddFile("upload_file_minidump", path);
char metapath[512];
strcpy(metapath, path);
strcat(metapath, ".txt");
if (libsys->PathExists(metapath)) {
form->AddFile("upload_file_metadata", metapath);
unlink(metapath);
}
2013-05-16 08:18:31 +02:00
MemoryDownloader data;
IWebTransfer *xfer = webternet->CreateSession();
xfer->SetFailOnHTTPError(true);
2013-05-18 01:46:46 +02:00
printf(">>> UPLOADING %s\n", path);
2013-05-16 08:18:31 +02:00
if (!xfer->PostAndDownload("http://crash.limetech.org/submit", form, &data, NULL))
{
2013-05-17 06:24:31 +02:00
printf(">>> UPLOAD FAILED\n");
2013-05-16 08:18:31 +02:00
} else {
2013-05-17 06:24:31 +02:00
printf(">>> UPLOADED CRASH DUMP");
printf("%s", data.GetBuffer());
2013-05-16 08:18:31 +02:00
}
}
void Accelerator::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
{
2013-05-18 01:46:46 +02:00
IDirectory *dumps = libsys->OpenDirectory(buffer);
2013-05-16 08:18:31 +02:00
char path[512];
2014-11-08 02:45:19 +01:00
int count = 0;
2013-05-16 08:18:31 +02:00
2013-05-18 01:46:46 +02:00
while (dumps->MoreFiles())
2013-05-16 08:18:31 +02:00
{
2013-05-18 01:46:46 +02:00
if (!dumps->IsEntryFile())
{
dumps->NextEntry();
2013-05-16 08:18:31 +02:00
continue;
2013-05-18 01:46:46 +02:00
}
const char *name = dumps->GetEntryName();
int namelen = strlen(name);
if (namelen < 4 || strcmp(&name[namelen-4], ".dmp") != 0) {
dumps->NextEntry();
continue;
}
g_pSM->Format(path, sizeof(path), "%s/%s", buffer, name);
2013-05-16 08:18:31 +02:00
UploadCrashDump(path);
2013-05-18 01:46:46 +02:00
2014-11-08 02:45:19 +01:00
int err = 0;
2013-05-18 01:46:46 +02:00
#if defined _LINUX
2014-11-08 02:45:19 +01:00
err = unlink(path);
2013-05-18 01:46:46 +02:00
#elif defined _WINDOWS
2014-11-08 02:45:19 +01:00
err = _unlink(path);
#else
#error Bad platform.
2013-05-18 01:46:46 +02:00
#endif
2014-11-08 02:45:19 +01:00
if (err != 0) {
printf(">>> FAILED TO DELETE CRASH DUMP!!!\n");
}
2013-05-18 01:46:46 +02:00
2014-11-08 02:45:19 +01:00
count++;
2013-05-18 01:46:46 +02:00
dumps->NextEntry();
2013-05-16 08:18:31 +02:00
}
2013-05-18 01:46:46 +02:00
libsys->CloseDirectory(dumps);
2014-11-08 02:45:19 +01:00
if (count > 0) {
printf(">>> UPLOADED %d CRASH DUMPS\n", count);
}
2013-05-16 08:18:31 +02:00
}
bool Accelerator::SDK_OnLoad(char *error, size_t maxlength, bool late)
{
sharesys->AddDependency(myself, "webternet.ext", true, true);
SM_GET_IFACE(WEBTERNET, webternet);
2013-05-18 01:46:46 +02:00
g_pSM->BuildPath(Path_SM, buffer, sizeof(buffer), "data/dumps");
2013-05-16 08:18:31 +02:00
if (!libsys->IsPathDirectory(buffer))
{
if (!libsys->CreateFolder(buffer))
{
if (error)
g_pSM->Format(error, maxlength, "%s didn't exist and we couldn't create it :(", buffer);
return false;
}
}
2013-05-18 01:46:46 +02:00
#if defined _LINUX
2013-05-16 08:18:31 +02:00
google_breakpad::MinidumpDescriptor descriptor(buffer);
handler = new google_breakpad::ExceptionHandler(descriptor, NULL, dumpCallback, NULL, true, -1);
struct sigaction oact;
sigaction(SIGSEGV, NULL, &oact);
SignalHandler = oact.sa_sigaction;
2013-05-18 01:46:46 +02:00
2013-05-16 08:18:31 +02:00
g_pSM->AddGameFrameHook(OnGameFrame);
2013-05-18 01:46:46 +02:00
#elif defined _WINDOWS
wchar_t *buf = new wchar_t[sizeof(buffer)];
size_t num_chars = mbstowcs(buf, buffer, sizeof(buffer));
handler = new google_breakpad::ExceptionHandler(std::wstring(buf, num_chars), NULL, dumpCallback, NULL, google_breakpad::ExceptionHandler::HANDLER_ALL);
2014-11-08 03:00:40 +01:00
vectoredHandler = AddVectoredExceptionHandler(0, BreakpadVectoredHandler);
2013-05-18 01:46:46 +02:00
delete buf;
2014-11-08 02:45:19 +01:00
#else
#error Bad platform.
2013-05-18 01:46:46 +02:00
#endif
IPluginIterator *i = plsys->GetPluginIterator();
while (i->MorePlugins()) {
IPlugin *p = i->GetPlugin();
const sm_plugininfo_t *pmi = p->GetPublicInfo();
PluginInfo *pi = &plugins[plugin_count++];
pi->serial = p->GetSerial();
pi->status = p->GetStatus();
strncpy(pi->filename, p->GetFilename(), sizeof(pi->filename) - 1);
strncpy(pi->name, pmi->name, sizeof(pi->name) - 1);
strncpy(pi->author, pmi->author, sizeof(pi->author) - 1);
strncpy(pi->description, pmi->description, sizeof(pi->description) - 1);
strncpy(pi->version, pmi->version, sizeof(pi->version) - 1);
strncpy(pi->url, pmi->url, sizeof(pi->url) - 1);
i->NextPlugin();
}
delete i;
2013-05-16 08:18:31 +02:00
return true;
}
void Accelerator::SDK_OnUnload()
{
2013-05-18 01:46:46 +02:00
#if defined _LINUX
2013-05-16 08:18:31 +02:00
g_pSM->RemoveGameFrameHook(OnGameFrame);
2013-05-18 01:46:46 +02:00
#elif defined _WINDOWS
2014-11-08 03:00:40 +01:00
if (vectoredHandler) {
RemoveVectoredExceptionHandler(vectoredHandler);
}
2014-11-08 02:45:19 +01:00
#else
#error Bad platform.
2013-05-18 01:46:46 +02:00
#endif
delete handler;
2013-05-16 08:18:31 +02:00
}