From 69756617335d266880199d23c72cfbe06a76cf67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 17 Nov 2009 03:19:13 +0000 Subject: [PATCH] massive refactors of process, processmanager is now processenumerator, better split between windows and linux code, finalized suspend/resume --- library/CMakeLists.txt | 36 +- library/DFCommonInternal.h | 19 +- library/DFHackAPI.cpp | 4 +- library/DFMemAccess.h | 4 +- library/DFProcess-linux.cpp | 362 +++++++++++++++++ library/DFProcess-linux.h | 32 ++ library/DFProcess-windows.cpp | 276 +++++++++++++ library/DFProcess-windows.h | 67 ++++ library/DFProcess.cpp | 377 ------------------ library/{DFProcessManager.h => DFProcess.h} | 72 +--- ...essManager.cpp => DFProcessEnumerator.cpp} | 230 +++-------- library/DFProcessEnumerator.h | 53 +++ ...MemAccess-memfiles.h => MemAccess-linux.h} | 0 ...WindowsMemAccess.h => MemAccess-windows.h} | 0 library/md5/md5wrapper.cpp | 56 +-- .../LinuxMemAccess-ptrace.h | 0 tools/CMakeLists.txt | 14 +- tools/incrementalsearch.cpp | 3 +- tools/suspendtest.cpp | 45 +++ 19 files changed, 988 insertions(+), 662 deletions(-) create mode 100644 library/DFProcess-linux.cpp create mode 100644 library/DFProcess-linux.h create mode 100644 library/DFProcess-windows.cpp create mode 100644 library/DFProcess-windows.h delete mode 100644 library/DFProcess.cpp rename library/{DFProcessManager.h => DFProcess.h} (52%) rename library/{DFProcessManager.cpp => DFProcessEnumerator.cpp} (60%) create mode 100644 library/DFProcessEnumerator.h rename library/{LinuxMemAccess-memfiles.h => MemAccess-linux.h} (100%) rename library/{WindowsMemAccess.h => MemAccess-windows.h} (100%) rename library/{ => unmaintained}/LinuxMemAccess-ptrace.h (100%) create mode 100644 tools/suspendtest.cpp diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 9a6e53f2c..7c4bc5a0f 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -6,15 +6,12 @@ DFDataModel.h DFHackAPI.h DFMemAccess.h DFMemInfo.h -DFProcessManager.h +DFProcessEnumerator.h +DFProcess.h DFTileTypes.h DFTypes.h DFVector.h integers.h -stdint_win.h -LinuxMemAccess-memfiles.h -LinuxMemAccess-ptrace.h -WindowsMemAccess.h md5/md5.h md5/md5wrapper.h @@ -26,8 +23,7 @@ tinyxml/tinyxml.h SET(PROJECT_SRCS DFDataModel.cpp DFMemInfo.cpp -DFProcess.cpp -DFProcessManager.cpp +DFProcessEnumerator.cpp DFHackAPI.cpp DFTileTypes.cpp md5/md5.cpp @@ -38,6 +34,32 @@ tinyxml/tinyxmlerror.cpp tinyxml/tinyxmlparser.cpp ) +SET(PROJECT_HDRS_LINUX +DFProcess-linux.h +MemAccess-linux.h +) + +SET(PROJECT_HDRS_WINDOWS +MemAccess-windows.h +stdint_win.h +) + +SET(PROJECT_SRCS_LINUX +DFProcess-linux.cpp +) + +SET(PROJECT_SRCS_WINDOWS +DFProcess-windows.cpp +) + +IF(UNIX) + LIST(APPEND PROJECT_HDRS ${PROJECT_HDRS_LINUX}) + LIST(APPEND PROJECT_SRCS ${PROJECT_SRCS_LINUX}) +ELSE(UNIX) + LIST(APPEND PROJECT_HDRS ${PROJECT_HDRS_WINDOWS}) + LIST(APPEND PROJECT_SRCS ${PROJECT_SRCS_WINDOWS}) +ENDIF(UNIX) + SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE ) LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS}) diff --git a/library/DFCommonInternal.h b/library/DFCommonInternal.h index da1654e09..e62ea52e3 100644 --- a/library/DFCommonInternal.h +++ b/library/DFCommonInternal.h @@ -72,13 +72,30 @@ using namespace std; #include #endif +#ifdef LINUX_BUILD +typedef pid_t ProcessHandle; +#else +typedef HANDLE ProcessHandle; +#endif + +namespace DFHack +{ + class Process; + /* + * Currently attached process and its handle + */ + extern Process * g_pProcess; ///< current process. non-NULL when picked + extern ProcessHandle g_ProcessHandle; ///< cache of handle to current process. used for speed reasons + extern int g_ProcessMemFile; ///< opened /proc/PID/mem, valid when attached +} #ifndef BUILD_DFHACK_LIB # define BUILD_DFHACK_LIB #endif #include "DFTypes.h" #include "DFDataModel.h" -#include "DFProcessManager.h" +#include "DFProcess.h" +#include "DFProcessEnumerator.h" #include "DFMemAccess.h" #include "DFVector.h" #include "DFMemInfo.h" diff --git a/library/DFHackAPI.cpp b/library/DFHackAPI.cpp index b2f0b5e71..503e44d42 100644 --- a/library/DFHackAPI.cpp +++ b/library/DFHackAPI.cpp @@ -74,7 +74,7 @@ class API::Private uint32_t dwarf_lang_table_offset; - ProcessManager* pm; + ProcessEnumerator* pm; Process* p; DataModel* dm; memory_info* offset_descriptor; @@ -1078,7 +1078,7 @@ bool API::Attach() // detach all processes, destroy manager if(d->pm == NULL) { - d->pm = new ProcessManager(d->xml); // FIXME: handle bad XML better + d->pm = new ProcessEnumerator(d->xml); // FIXME: handle bad XML better } // find a process (ProcessManager can find multiple when used properly) diff --git a/library/DFMemAccess.h b/library/DFMemAccess.h index 1d98c8f80..181025c1f 100644 --- a/library/DFMemAccess.h +++ b/library/DFMemAccess.h @@ -26,9 +26,9 @@ distribution. #define PROCESSUTIL_H_INCLUDED #ifdef LINUX_BUILD - #include "LinuxMemAccess-memfiles.h" + #include "MemAccess-linux.h" #else - #include "WindowsMemAccess.h" + #include "MemAccess-windows.h" #endif #endif // PROCESSUTIL_H_INCLUDED diff --git a/library/DFProcess-linux.cpp b/library/DFProcess-linux.cpp new file mode 100644 index 000000000..2ae843cda --- /dev/null +++ b/library/DFProcess-linux.cpp @@ -0,0 +1,362 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include "DFCommonInternal.h" +using namespace DFHack; + +class Process::Private +{ + public: + Private() + { + my_datamodel = NULL; + my_descriptor = NULL; + my_handle = NULL; + my_pid = 0; + attached = false; + suspended = false; + }; + ~Private(){}; + DataModel* my_datamodel; + memory_info * my_descriptor; + ProcessHandle my_handle; + uint32_t my_pid; + string memFile; + bool attached; + bool suspended; + bool identified; + bool validate(char * exe_file, uint32_t pid, char * mem_file, vector & known_versions); +}; + +Process::Process(uint32_t pid, vector & known_versions) +: d(new Private()) +{ + char dir_name [256]; + char exe_link_name [256]; + char mem_name [256]; + char cwd_name [256]; + char cmdline_name [256]; + char target_name[1024]; + int target_result; + + d->identified = false; + + sprintf(dir_name,"/proc/%d/", pid); + sprintf(exe_link_name,"/proc/%d/exe", pid); + sprintf(mem_name,"/proc/%d/mem", pid); + sprintf(cwd_name,"/proc/%d/cwd", pid); + sprintf(cmdline_name,"/proc/%d/cmdline", pid); + + // resolve /proc/PID/exe link + target_result = readlink(exe_link_name, target_name, sizeof(target_name)-1); + if (target_result == -1) + { + return; + } + // make sure we have a null terminated string... + target_name[target_result] = 0; + + // is this the regular linux DF? + if (strstr(target_name, "dwarfort.exe") != NULL) + { + // create linux process, add it to the vector + d->identified = d->validate(target_name,pid,mem_name,known_versions ); + return; + } + + // FIXME: this fails when the wine process isn't started from the 'current working directory'. strip path data from cmdline + // is this windows version of Df running in wine? + if(strstr(target_name, "wine-preloader")!= NULL) + { + // get working directory + target_result = readlink(cwd_name, target_name, sizeof(target_name)-1); + target_name[target_result] = 0; + + // got path to executable, do the same for its name + ifstream ifs ( cmdline_name , ifstream::in ); + string cmdline; + getline(ifs,cmdline); + if (cmdline.find("dwarfort-w.exe") != string::npos || cmdline.find("dwarfort.exe") != string::npos || cmdline.find("Dwarf Fortress.exe") != string::npos) + { + char exe_link[1024]; + // put executable name and path together + sprintf("%s/%s",target_name,cmdline.c_str()); + + // create wine process, add it to the vector + d->identified = d->validate(exe_link,pid,mem_name,known_versions); + } + } +} + +bool Process::isSuspended() +{ + return d->suspended; +} +bool Process::isAttached() +{ + return d->attached; +} + +bool Process::isIdentified() +{ + return d->identified; +} + +bool Process::Private::validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions) +{ + md5wrapper md5; + // get hash of the running DF process + string hash = md5.getHashFromFile(exe_file); + vector::iterator it; + + // iterate over the list of memory locations + for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) + { + if(hash == (*it).getString("md5")) // are the md5 hashes the same? + { + memory_info * m = &*it; + Process * ret; + //cout <<"Found process " << PH << ". It's DF version " << m->getVersion() << "." << endl; + + // df can run under wine on Linux + if(memory_info::OS_WINDOWS == (*it).getOS()) + { + my_datamodel =new DMWindows40d(); + my_descriptor = m; + my_handle = my_pid = pid; + } + else if (memory_info::OS_LINUX == (*it).getOS()) + { + my_datamodel =new DMLinux40d(); + my_descriptor = m; + my_handle = my_pid = pid; + } + else + { + // some error happened, continue with next process + continue; + } + // tell Process about the /proc/PID/mem file + this->memFile = memFile; + identified = true; + return true; + } + } + return false; +} + +Process::~Process() +{ + if(d->attached) + { + detach(); + } + // destroy data model. this is assigned by processmanager + delete d->my_datamodel; +} + + +DataModel *Process::getDataModel() +{ + return d->my_datamodel; +} + + +memory_info * Process::getDescriptor() +{ + return d->my_descriptor; +} + + +/*void Process::setMemFile(const string & memf) +{ + assert(!attached); + memFile = memf; +}*/ + +bool Process::getThreadIDs(vector & threads ) +{ + return false; +} + +void Process::getMemRanges( vector & ranges ) +{ + char buffer[1024]; + char name[1024]; + char permissions[5]; // r/-, w/-, x/-, p/s, 0 + + sprintf(buffer, "/proc/%lu/maps", d->my_pid); + FILE *mapFile = ::fopen(buffer, "r"); + uint64_t begin, end, offset, device1, device2, node; + + while (fgets(buffer, 1024, mapFile)) + { + t_memrange temp; + temp.name[0] = 0; + sscanf(buffer, "%llx-%llx %s %llx %2llu:%2llu %llu %s", + &temp.start, + &temp.end, + (char*)&permissions, + &offset, &device1, &device2, &node, + (char*)&temp.name); + temp.read = permissions[0] == 'r'; + temp.write = permissions[1] == 'w'; + temp.execute = permissions[2] == 'x'; + ranges.push_back(temp); + } +} + +bool Process::suspend() +{ + int status; + if(!d->attached) + return false; + if(d->suspended) + return true; + if (kill(d->my_handle, SIGSTOP) == -1) + { + // no, we got an error + perror("kill SIGSTOP error"); + return false; + } + while(true) + { + // we wait on the pid + pid_t w = waitpid(d->my_handle, &status, 0); + if (w == -1) + { + // child died + perror("DF exited during suspend call"); + return false; + } + // stopped -> let's continue + if (WIFSTOPPED(status)) + { + break; + } + } + d->suspended = true; +} + +bool Process::resume() +{ + int status; + if(!d->attached) + return false; + if(!d->suspended) + return true; + if (ptrace(PTRACE_CONT, d->my_handle, NULL, NULL) == -1) + { + // no, we got an error + perror("ptrace resume error"); + return false; + } + d->suspended = false; +} + + +bool Process::attach() +{ + int status; + if(g_pProcess != NULL) + { + return false; + } + // can we attach? + if (ptrace(PTRACE_ATTACH , d->my_handle, NULL, NULL) == -1) + { + // no, we got an error + perror("ptrace attach error"); + cerr << "attach failed on pid " << d->my_handle << endl; + return false; + } + while(true) + { + // we wait on the pid + pid_t w = waitpid(d->my_handle, &status, 0); + if (w == -1) + { + // child died + perror("wait inside attach()"); + return false; + } + // stopped -> let's continue + if (WIFSTOPPED(status)) + { + break; + } + } + d->suspended = true; + + int proc_pid_mem = open(d->memFile.c_str(),O_RDONLY); + if(proc_pid_mem == -1) + { + ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL); + cerr << "couldn't open /proc/" << d->my_handle << "/mem" << endl; + perror("open(memFile.c_str(),O_RDONLY)"); + return false; + } + else + { + d->attached = true; + g_pProcess = this; + g_ProcessHandle = d->my_handle; + + g_ProcessMemFile = proc_pid_mem; + return true; // we are attached + } +} + +bool Process::detach() +{ + if(!d->attached) return false; + if(!d->suspended) suspend(); + int result = 0; + // close /proc/PID/mem + result = close(g_ProcessMemFile); + if(result == -1) + { + cerr << "couldn't close /proc/"<< d->my_handle <<"/mem" << endl; + perror("mem file close"); + return false; + } + else + { + // detach + g_ProcessMemFile = -1; + result = ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL); + if(result == -1) + { + cerr << "couldn't detach from process pid" << d->my_handle << endl; + perror("ptrace detach"); + return false; + } + else + { + d->attached = false; + g_pProcess = NULL; + g_ProcessHandle = 0; + return true; + } + } +} \ No newline at end of file diff --git a/library/DFProcess-linux.h b/library/DFProcess-linux.h new file mode 100644 index 000000000..85b0146f8 --- /dev/null +++ b/library/DFProcess-linux.h @@ -0,0 +1,32 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* +DO NOT USE DIRECTLY, use DFProcess.h instead. +*/ + +#ifndef PROCESS_LIN_H_INCLUDED +#define PROCESS_LIN_H_INCLUDED + +#endif \ No newline at end of file diff --git a/library/DFProcess-windows.cpp b/library/DFProcess-windows.cpp new file mode 100644 index 000000000..e4dc0f866 --- /dev/null +++ b/library/DFProcess-windows.cpp @@ -0,0 +1,276 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include "DFCommonInternal.h" +using namespace DFHack; + +class Process::Private +{ + public: + Private() + { + my_datamodel = NULL; + my_descriptor = NULL; + my_handle = NULL; + my_main_thread = NULL; + my_pid = 0; + attached = false; + suspended = false; + }; + ~Private(){}; + DataModel* my_datamodel; + memory_info * my_descriptor; + ProcessHandle my_handle; + HANDLE my_main_thread; + uint32_t my_pid; + string memFile; + bool attached; + bool suspended; + bool identified; +}; + +Process::Process(uint32_t pid, vector & known_versions) +: d(new Private()) +{ + HMODULE hmod = NULL; + DWORD junk; + HANDLE hProcess; + bool found = false; + + IMAGE_NT_HEADERS32 pe_header; + IMAGE_SECTION_HEADER sections[16]; + d->identified = false; + // open process + hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid ); + if (NULL == hProcess) + return; + + // try getting the first module of the process + if(EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk) == 0) + { + CloseHandle(hProcess); + } + + // got base ;) + uint32_t base = (uint32_t)hmod; + + // read from this process + g_ProcessHandle = hProcess; + uint32_t pe_offset = MreadDWord(base+0x3C); + Mread(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header); + Mread(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)§ions ); + + // see if there's a version entry that matches this process + vector::iterator it; + for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) + { + // filter by OS + if(memory_info::OS_WINDOWS != (*it).getOS()) + continue; + + // filter by timestamp + uint32_t pe_timestamp = (*it).getHexValue("pe_timestamp"); + if (pe_timestamp != pe_header.FileHeader.TimeDateStamp) + continue; + + // all went well + { + printf("Match found! Using version %s.\n", (*it).getVersion().c_str()); + d->identified = true; + // give the process a data model and memory layout fixed for the base of first module + memory_info *m = new memory_info(*it); + m->RebaseAll(base); + // keep track of created memory_info object so we can destroy it later + d->my_descriptor = m; + // process is responsible for destroying its data model + d->my_datamodel = new DMWindows40d(); + d->my_pid = pid; + d->my_handle = hProcess; + d->identified = true; + + // TODO: detect errors in thread enumeration + vector threads; + getThreadIDs( threads ); + d->my_main_thread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads[0]); + + found = true; + break; // break the iterator loop + } + } + // close handle of processes that aren't DF + if(!found) + { + CloseHandle(hProcess); + } +} +/* +*/ + +Process::~Process() +{ + if(d->attached) + { + detach(); + } + // destroy data model. this is assigned by processmanager + delete d->my_datamodel; + delete d->my_descriptor; + if(d->my_handle != NULL) + { + CloseHandle(d->my_handle); + } + if(d->my_main_thread != NULL) + { + CloseHandle(d->my_main_thread); + } + +} + + +DataModel *Process::getDataModel() +{ + return d->my_datamodel; +} + + +memory_info * Process::getDescriptor() +{ + return d->my_descriptor; +} + +bool Process::isSuspended() +{ + return d->suspended; +} +bool Process::isAttached() +{ + return d->attached; +} + +bool Process::isIdentified() +{ + return d->identified; +} + +bool Process::suspend() +{ + if(!d->attached) + return false; + if(d->suspended) + { + return true; + } + SuspendThread(d->my_main_thread); + d->suspended = true; + return true; +} + +bool Process::resume() +{ + if(!d->attached) + return false; + if(!d->suspended) + { + return true; + } + ResumeThread(d->my_main_thread); + d->suspended = false; + return true; +} + +bool Process::attach() +{ + if(d->attached) + { + return false; + } + d->attached = true; + g_pProcess = this; + g_ProcessHandle = d->my_handle; + suspend(); + + return true; +} + + +bool Process::detach() +{ + if(!d->attached) + { + return false; + } + resume(); + d->attached = false; + g_pProcess = NULL; + g_ProcessHandle = 0; + return true; +} + +bool Process::getThreadIDs(vector & threads ) +{ + HANDLE AllThreads = INVALID_HANDLE_VALUE; + THREADENTRY32 te32; + + AllThreads = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); + if( AllThreads == INVALID_HANDLE_VALUE ) + { + return false; + } + te32.dwSize = sizeof(THREADENTRY32 ); + + if( !Thread32First( AllThreads, &te32 ) ) + { + CloseHandle( AllThreads ); + return false; + } + + do + { + if( te32.th32OwnerProcessID == d->my_pid ) + { + threads.push_back(te32.th32ThreadID); + } + } while( Thread32Next(AllThreads, &te32 ) ); + + CloseHandle( AllThreads ); + return true; +} + + +void Process::getMemRanges( vector & ranges ) +{ + // code here is taken from hexsearch by Silas Dunmore. + // As this IMHO isn't a 'sunstantial portion' of anything, I'm not including the MIT license here + + // I'm faking this, because there's no way I'm using VirtualQuery + + t_memrange temp; + uint32_t base = d->my_descriptor->getBase(); + temp.start = base + 0x1000; // more fakery. + temp.end = base + MreadDWord(base+MreadDWord(base+0x3C)+0x50)-1; // yay for magic. + temp.read = 1; + temp.write = 1; + temp.execute = 0; // fake + strcpy(temp.name,"pants");// that's right. I'm calling it pants. Windows can go to HELL + ranges.push_back(temp); +} diff --git a/library/DFProcess-windows.h b/library/DFProcess-windows.h new file mode 100644 index 000000000..a16e3c289 --- /dev/null +++ b/library/DFProcess-windows.h @@ -0,0 +1,67 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* +DO NOT USE DIRECTLY, use DFProcess.h instead. +*/ + +#ifndef PROCESS_WIN_H_INCLUDED +#define PROCESS_WIN_H_INCLUDED + +namespace DFHack +{ + class DFHACK_EXPORT Process + { + friend class ProcessEnumerator; + private: + Process(DataModel * dm, memory_info* mi, ProcessHandle ph, uint32_t pid); + ~Process(); + DataModel* my_datamodel; + memory_info * my_descriptor; + ProcessHandle my_handle; + uint32_t my_pid; + string memFile; + bool attached; + bool suspended; + void freeResources(); + void setMemFile(const string & memf); + public: + // Set up stuff so we can read memory + bool attach(); + bool detach(); + + bool suspend(); + bool resume(); + + bool isSuspended() {return suspended;}; + bool isAttached() {return attached;}; + + bool getThreadIDs(vector & threads ); + void getMemRanges( vector & ranges ); + // is the process still there? + memory_info *getDescriptor(); + DataModel *getDataModel(); + }; +} +#endif \ No newline at end of file diff --git a/library/DFProcess.cpp b/library/DFProcess.cpp deleted file mode 100644 index 479e5a04d..000000000 --- a/library/DFProcess.cpp +++ /dev/null @@ -1,377 +0,0 @@ -/* -www.sourceforge.net/projects/dfhack -Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ -#include "DFCommonInternal.h" -using namespace DFHack; - -Process::Process(DataModel * dm, memory_info* mi, ProcessHandle ph, uint32_t pid) -{ - my_datamodel = dm; - my_descriptor = mi; - my_handle = ph; - my_pid = pid; - attached = false; - suspended = false; -} - - -Process::~Process() -{ - if(attached) - { - detach(); - } - // destroy data model. this is assigned by processmanager - delete my_datamodel; - freeResources(); -} - - -DataModel *Process::getDataModel() -{ - return my_datamodel; -} - - -memory_info * Process::getDescriptor() -{ - return my_descriptor; -} - - -void Process::setMemFile(const string & memf) -{ - assert(!attached); - memFile = memf; -} - -#ifdef LINUX_BUILD -/* - * LINUX PART - */ - - -bool Process::getThreadIDs(vector & threads ) -{ - return false; -} - -void Process::getMemRanges( vector & ranges ) -{ - char buffer[1024]; - char name[1024]; - char permissions[5]; // r/-, w/-, x/-, p/s, 0 - - sprintf(buffer, "/proc/%lu/maps", my_pid); - FILE *mapFile = ::fopen(buffer, "r"); - uint64_t begin, end, offset, device1, device2, node; - - while (fgets(buffer, 1024, mapFile)) - { - t_memrange temp; - temp.name[0] = 0; - sscanf(buffer, "%llx-%llx %s %llx %2llu:%2llu %llu %s", - &temp.start, - &temp.end, - (char*)&permissions, - &offset, &device1, &device2, &node, - (char*)&temp.name); - temp.read = permissions[0] == 'r'; - temp.write = permissions[1] == 'w'; - temp.execute = permissions[2] == 'x'; - ranges.push_back(temp); - } -} - -bool Process::suspend() -{ - int status; - if(!attached) - return false; - if(suspended) - return true; - if (kill(my_handle, SIGSTOP) == -1) - { - // no, we got an error - perror("kill SIGSTOP error"); - return false; - } - while(true) - { - // we wait on the pid - pid_t w = waitpid(my_handle, &status, 0); - if (w == -1) - { - // child died - perror("DF exited during suspend call"); - return false; - } - // stopped -> let's continue - if (WIFSTOPPED(status)) - { - break; - } - } - suspended = true; -} - -bool Process::resume() -{ - int status; - if(!attached) - return false; - if(!suspended) - return true; - if (ptrace(PTRACE_CONT, my_handle, NULL, NULL) == -1) - { - // no, we got an error - perror("ptrace resume error"); - return false; - } - suspended = false; -} - - -bool Process::attach() -{ - int status; - if(g_pProcess != NULL) - { - return false; - } - // can we attach? - if (ptrace(PTRACE_ATTACH , my_handle, NULL, NULL) == -1) - { - // no, we got an error - perror("ptrace attach error"); - cerr << "attach failed on pid " << my_handle << endl; - return false; - } - while(true) - { - // we wait on the pid - pid_t w = waitpid(my_handle, &status, 0); - if (w == -1) - { - // child died - perror("wait inside attach()"); - return false; - } - // stopped -> let's continue - if (WIFSTOPPED(status)) - { - break; - } - } - suspended = true; - - int proc_pid_mem = open(memFile.c_str(),O_RDONLY); - if(proc_pid_mem == -1) - { - ptrace(PTRACE_DETACH, my_handle, NULL, NULL); - cerr << "couldn't open /proc/" << my_handle << "/mem" << endl; - perror("open(memFile.c_str(),O_RDONLY)"); - return false; - } - else - { - attached = true; - g_pProcess = this; - g_ProcessHandle = my_handle; - - g_ProcessMemFile = proc_pid_mem; - return true; // we are attached - } -} - -bool Process::detach() -{ - if(!attached) return false; - if(!suspended) suspend(); - int result = 0; - // close /proc/PID/mem - result = close(g_ProcessMemFile); - if(result == -1) - { - cerr << "couldn't close /proc/"<< my_handle <<"/mem" << endl; - perror("mem file close"); - return false; - } - else - { - // detach - g_ProcessMemFile = -1; - result = ptrace(PTRACE_DETACH, my_handle, NULL, NULL); - if(result == -1) - { - cerr << "couldn't detach from process pid" << my_handle << endl; - perror("ptrace detach"); - return false; - } - else - { - attached = false; - g_pProcess = NULL; - g_ProcessHandle = 0; - return true; - } - } -} - - - -void Process::freeResources() -{ - // nil -}; - - -#else -/* - * WINDOWS PART - */ - -bool Process::suspend() -{ - if(!attached) - return false; - if(suspended) - { - return true; - } - vector threads; - getThreadIDs( threads ); - for(int i = 0; i < /*threads.size()*/ 1; i++) - { - HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads[i]); - SuspendThread(thread_handle); - CloseHandle(thread_handle); - } - suspended = true; - return true; -} - -bool Process::resume() -{ - if(!attached) - return false; - if(!suspended) - { - return true; - } - vector threads; - getThreadIDs( threads ); - for(int i = 0; i < /* threads.size() */ 1; i++) - { - HANDLE thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads[i]); - ResumeThread(thread_handle); - CloseHandle(thread_handle); - } - suspended = false; - return true; -} - -bool Process::attach() -{ - if(attached) - { - return false; - } - attached = true; - g_pProcess = this; - g_ProcessHandle = my_handle; - suspend(); - - return true; -} - - -bool Process::detach() -{ - if(!attached) - { - return false; - } - resume(); - attached = false; - g_pProcess = NULL; - g_ProcessHandle = 0; - return true; -} - -bool Process::getThreadIDs(vector & threads ) -{ - HANDLE AllThreads = INVALID_HANDLE_VALUE; - THREADENTRY32 te32; - - AllThreads = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); - if( AllThreads == INVALID_HANDLE_VALUE ) - { - return false; - } - te32.dwSize = sizeof(THREADENTRY32 ); - - if( !Thread32First( AllThreads, &te32 ) ) - { - CloseHandle( AllThreads ); - return false; - } - - do - { - if( te32.th32OwnerProcessID == my_pid ) - { - threads.push_back(te32.th32ThreadID); - } - } while( Thread32Next(AllThreads, &te32 ) ); - - CloseHandle( AllThreads ); - return true; -} - - -void Process::getMemRanges( vector & ranges ) -{ - // code here is taken from hexsearch by Silas Dunmore. - // As this IMHO isn't a 'sunstantial portion' of anything, I'm not including the MIT license here - - // I'm faking this, because there's no way I'm using VirtualQuery - - t_memrange temp; - uint32_t base = this->my_descriptor->getBase(); - temp.start = base + 0x1000; // more fakery. - temp.end = base + MreadDWord(base+MreadDWord(base+0x3C)+0x50)-1; // yay for magic. - temp.read = 1; - temp.write = 1; - temp.execute = 0; // fake - strcpy(temp.name,"pants");// that's right. I'm calling it pants. Windows can go to HELL - ranges.push_back(temp); -} - -void Process::freeResources() -{ - // opened by process manager - CloseHandle(my_handle); -}; -#endif - diff --git a/library/DFProcessManager.h b/library/DFProcess.h similarity index 52% rename from library/DFProcessManager.h rename to library/DFProcess.h index 3ff527579..21f6a2960 100644 --- a/library/DFProcessManager.h +++ b/library/DFProcess.h @@ -22,33 +22,17 @@ must not be misrepresented as being the original software. distribution. */ -#ifndef PROCESSMANAGER_H_INCLUDED -#define PROCESSMANAGER_H_INCLUDED +#ifndef PROCESS_H_INCLUDED +#define PROCESS_H_INCLUDED #include "Export.h" -class TiXmlElement; - namespace DFHack { - class memory_info; class DataModel; class Process; - #ifdef LINUX_BUILD - typedef pid_t ProcessHandle; - #else - typedef HANDLE ProcessHandle; - #endif - - /* - * Currently attached process and its handle - */ - extern Process * g_pProcess; ///< current process. non-NULL when picked - extern ProcessHandle g_ProcessHandle; ///< cache of handle to current process. used for speed reasons - extern int g_ProcessMemFile; ///< opened /proc/PID/mem, valid when attached - // structure describing a memory range struct DFHACK_EXPORT t_memrange { @@ -75,19 +59,12 @@ namespace DFHack class DFHACK_EXPORT Process { - friend class ProcessManager; - protected: - Process(DataModel * dm, memory_info* mi, ProcessHandle ph, uint32_t pid); + friend class ProcessEnumerator; + class Private; + private: + Private * const d; + Process(uint32_t pid, vector & known_versions); ~Process(); - DataModel* my_datamodel; - memory_info * my_descriptor; - ProcessHandle my_handle; - uint32_t my_pid; - string memFile; - bool attached; - bool suspended; - void freeResources(); - void setMemFile(const string & memf); public: // Set up stuff so we can read memory bool attach(); @@ -96,8 +73,9 @@ namespace DFHack bool suspend(); bool resume(); - bool isSuspended() {return suspended;}; - bool isAttached() {return attached;}; + bool isSuspended(); + bool isAttached(); + bool isIdentified(); bool getThreadIDs(vector & threads ); void getMemRanges( vector & ranges ); @@ -105,33 +83,5 @@ namespace DFHack memory_info *getDescriptor(); DataModel *getDataModel(); }; - - /* - * Process manager - */ - class DFHACK_EXPORT ProcessManager - { - public: - ProcessManager( string path_to_xml); - ~ProcessManager(); - bool findProcessess(); - uint32_t size(); - Process * operator[](uint32_t index); - - private: - // memory info entries loaded from a file - std::vector meminfo; - // vector to keep track of dynamically created memory_info entries - std::vector destroy_meminfo; - Process * currentProcess; - ProcessHandle currentProcessHandle; - std::vector processes; - bool loadDescriptors( string path_to_xml); - void ParseVTable(TiXmlElement* vtable, memory_info& mem); - void ParseEntry (TiXmlElement* entry, memory_info& mem, map & knownEntries); - #ifdef LINUX_BUILD - Process* addProcess(const string & exe,ProcessHandle PH,const string & memFile); - #endif - }; } -#endif // PROCESSMANAGER_H_INCLUDED +#endif \ No newline at end of file diff --git a/library/DFProcessManager.cpp b/library/DFProcessEnumerator.cpp similarity index 60% rename from library/DFProcessManager.cpp rename to library/DFProcessEnumerator.cpp index 73ca19058..136071e71 100644 --- a/library/DFProcessManager.cpp +++ b/library/DFProcessEnumerator.cpp @@ -22,7 +22,6 @@ must not be misrepresented as being the original software. distribution. */ -//#define LINUX_BUILD #include "DFCommonInternal.h" using namespace DFHack; @@ -31,51 +30,29 @@ Process * DFHack::g_pProcess; ///< current process. non-NULL when picked ProcessHandle DFHack::g_ProcessHandle; ///< cache of handle to current process. used for speed reasons int DFHack::g_ProcessMemFile; ///< opened /proc/PID/mem, valid when attached +class DFHack::ProcessEnumerator::Private +{ + public: + Private(){}; + // memory info entries loaded from a file + std::vector meminfo; + Process * currentProcess; + ProcessHandle currentProcessHandle; + std::vector processes; + bool loadDescriptors( string path_to_xml); + void ParseVTable(TiXmlElement* vtable, memory_info& mem); + void ParseEntry (TiXmlElement* entry, memory_info& mem, map & knownEntries); + #ifdef LINUX_BUILD + Process* addProcess(const string & exe,ProcessHandle PH,const string & memFile); + #endif +}; + #ifdef LINUX_BUILD /* * LINUX version of the process finder. */ -Process* ProcessManager::addProcess(const string & exe,ProcessHandle PH, const string & memFile) -{ - md5wrapper md5; - // get hash of the running DF process - string hash = md5.getHashFromFile(exe); - vector::iterator it; - - // iterate over the list of memory locations - for ( it=meminfo.begin() ; it < meminfo.end(); it++ ) - { - if(hash == (*it).getString("md5")) // are the md5 hashes the same? - { - memory_info * m = &*it; - Process * ret; - //cout <<"Found process " << PH << ". It's DF version " << m->getVersion() << "." << endl; - - // df can run under wine on Linux - if(memory_info::OS_WINDOWS == (*it).getOS()) - { - ret= new Process(new DMWindows40d(),m,PH, PH); - } - else if (memory_info::OS_LINUX == (*it).getOS()) - { - ret= new Process(new DMLinux40d(),m,PH, PH); - } - else - { - // some error happened, continue with next process - continue; - } - // tell Process about the /proc/PID/mem file - ret->setMemFile(memFile); - processes.push_back(ret); - return ret; - } - } - return NULL; -} - -bool ProcessManager::findProcessess() +bool ProcessEnumerator::findProcessess() { DIR *dir_p; struct dirent *dir_entry_p; @@ -86,8 +63,7 @@ bool ProcessManager::findProcessess() string cmdline; // ALERT: buffer overrun potential - char target_name[1024]; - int target_result; + int errorcount; int result; @@ -103,67 +79,19 @@ bool ProcessManager::findProcessess() { continue; } - - // string manipulation - get /proc/PID/exe link and /proc/PID/mem names - dir_name = "/proc/"; - dir_name += dir_entry_p->d_name; - dir_name += "/"; - exe_link = dir_name + "exe"; - string mem_name = dir_name + "mem"; - - // resolve /proc/PID/exe link - target_result = readlink(exe_link.c_str(), target_name, sizeof(target_name)-1); - if (target_result == -1) - { - // bad result from link resolution, continue with another processed - continue; - } - // make sure we have a null terminated string... - target_name[target_result] = 0; - - // is this the regular linux DF? - if (strstr(target_name, "dwarfort.exe") != NULL) + Process *p = new Process(atoi(dir_entry_p->d_name),d->meminfo); + if(p->isIdentified()) { - exe_link = target_name; - // get PID - result = atoi(dir_entry_p->d_name); - // create linux process, add it to the vector - addProcess(exe_link,result,mem_name); - // continue with next process - continue; + d->processes.push_back(p); } - - // FIXME: this fails when the wine process isn't started from the 'current working directory'. strip path data from cmdline - // is this windows version of Df running in wine? - if(strstr(target_name, "wine-preloader")!= NULL) + else { - // get working directory - cwd_link = dir_name + "cwd"; - target_result = readlink(cwd_link.c_str(), target_name, sizeof(target_name)-1); - target_name[target_result] = 0; - - // got path to executable, do the same for its name - cmdline_path = dir_name + "cmdline"; - ifstream ifs ( cmdline_path.c_str() , ifstream::in ); - getline(ifs,cmdline); - if (cmdline.find("dwarfort-w.exe") != string::npos || cmdline.find("dwarfort.exe") != string::npos || cmdline.find("Dwarf Fortress.exe") != string::npos) - { - // put executable name and path together - exe_link = target_name; - exe_link += "/"; - exe_link += cmdline; - - // get PID - result = atoi(dir_entry_p->d_name); - - // create wine process, add it to the vector - addProcess(exe_link,result,mem_name); - } + delete p; } } closedir(dir_p); // return value depends on if we found some DF processes - if(processes.size()) + if(d->processes.size()) { return true; } @@ -199,18 +127,10 @@ bool EnableDebugPriv() } // WINDOWS version of the process finder -bool ProcessManager::findProcessess() +bool ProcessEnumerator::findProcessess() { // Get the list of process identifiers. - //TODO: make this dynamic. (call first to get the array size and second to really get process handles) - DWORD ProcArray[512], memoryNeeded, numProccesses; - HMODULE hmod = NULL; - DWORD junk; - HANDLE hProcess; - bool found = false; - - IMAGE_NT_HEADERS32 pe_header; - IMAGE_SECTION_HEADER sections[16]; + DWORD ProcArray[2048], memoryNeeded, numProccesses; EnableDebugPriv(); if ( !EnumProcesses( ProcArray, sizeof(ProcArray), &memoryNeeded ) ) @@ -224,71 +144,24 @@ bool ProcessManager::findProcessess() // iterate through processes for ( int i = 0; i < (int)numProccesses; i++ ) { - found = false; - - // open process - hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, ProcArray[i] ); - if (NULL == hProcess) - continue; - - // try getting the first module of the process - if(EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk) == 0) - { - CloseHandle(hProcess); - continue; - } - - // got base ;) - uint32_t base = (uint32_t)hmod; - - // read from this process - g_ProcessHandle = hProcess; - uint32_t pe_offset = MreadDWord(base+0x3C); - Mread(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header); - Mread(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)§ions ); - - // see if there's a version entry that matches this process - vector::iterator it; - for ( it=meminfo.begin() ; it < meminfo.end(); it++ ) + Process *p = new Process(ProcArray[i],d->meminfo); + if(p->isIdentified()) { - // filter by OS - if(memory_info::OS_WINDOWS != (*it).getOS()) - continue; - - // filter by timestamp - uint32_t pe_timestamp = (*it).getHexValue("pe_timestamp"); - if (pe_timestamp != pe_header.FileHeader.TimeDateStamp) - continue; - - // all went well - { - printf("Match found! Using version %s.\n", (*it).getVersion().c_str()); - // give the process a data model and memory layout fixed for the base of first module - memory_info *m = new memory_info(*it); - m->RebaseAll(base); - // keep track of created memory_info objects so we can destroy them later - destroy_meminfo.push_back(m); - // process is responsible for destroying its data model - Process *ret= new Process(new DMWindows40d(),m,hProcess, ProcArray[i]); - processes.push_back(ret); - found = true; - break; // break the iterator loop - } + d->processes.push_back(p); } - // close handle of processes that aren't DF - if(!found) + else { - CloseHandle(hProcess); + delete p; } } - if(processes.size()) + if(d->processes.size()) return true; return false; } #endif -void ProcessManager::ParseVTable(TiXmlElement* vtable, memory_info& mem) +void ProcessEnumerator::Private::ParseVTable(TiXmlElement* vtable, memory_info& mem) { TiXmlElement* pClassEntry; TiXmlElement* pClassSubEntry; @@ -336,7 +209,7 @@ void ProcessManager::ParseVTable(TiXmlElement* vtable, memory_info& mem) -void ProcessManager::ParseEntry (TiXmlElement* entry, memory_info& mem, map & knownEntries) +void ProcessEnumerator::Private::ParseEntry (TiXmlElement* entry, memory_info& mem, map & knownEntries) { TiXmlElement* pMemEntry; const char *cstr_version = entry->Attribute("version"); @@ -459,7 +332,7 @@ void ProcessManager::ParseEntry (TiXmlElement* entry, memory_info& mem, map processes.size(); }; -Process * ProcessManager::operator[](uint32_t index) +Process * ProcessEnumerator::operator[](uint32_t index) { - assert(index < processes.size()); - return processes[index]; + assert(index < d->processes.size()); + return d->processes[index]; }; -ProcessManager::ProcessManager( string path_to_xml ) +ProcessEnumerator::ProcessEnumerator( string path_to_xml ) +: d(new Private()) { - currentProcess = NULL; - currentProcessHandle = 0; - loadDescriptors( path_to_xml ); + d->currentProcess = NULL; + d->currentProcessHandle = 0; + d->loadDescriptors( path_to_xml ); } -ProcessManager::~ProcessManager() +ProcessEnumerator::~ProcessEnumerator() { // delete all processes - for(uint32_t i = 0;i < processes.size();i++) - { - delete processes[i]; - } - //delete all generated memory_info stuff - for(uint32_t i = 0;i < destroy_meminfo.size();i++) + for(uint32_t i = 0;i < d->processes.size();i++) { - delete destroy_meminfo[i]; + delete d->processes[i]; } + delete d; } diff --git a/library/DFProcessEnumerator.h b/library/DFProcessEnumerator.h new file mode 100644 index 000000000..f369330e7 --- /dev/null +++ b/library/DFProcessEnumerator.h @@ -0,0 +1,53 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef PROCESSMANAGER_H_INCLUDED +#define PROCESSMANAGER_H_INCLUDED + +#include "Export.h" + +class TiXmlElement; + +namespace DFHack +{ + class memory_info; + class DataModel; + class Process; + + /* + * Process manager + */ + class DFHACK_EXPORT ProcessEnumerator + { + class Private; + Private * const d; + public: + ProcessEnumerator( string path_to_xml); + ~ProcessEnumerator(); + bool findProcessess(); + uint32_t size(); + Process * operator[](uint32_t index); + }; +} +#endif // PROCESSMANAGER_H_INCLUDED diff --git a/library/LinuxMemAccess-memfiles.h b/library/MemAccess-linux.h similarity index 100% rename from library/LinuxMemAccess-memfiles.h rename to library/MemAccess-linux.h diff --git a/library/WindowsMemAccess.h b/library/MemAccess-windows.h similarity index 100% rename from library/WindowsMemAccess.h rename to library/MemAccess-windows.h diff --git a/library/md5/md5wrapper.cpp b/library/md5/md5wrapper.cpp index 3b7ae832d..241d2bb31 100644 --- a/library/md5/md5wrapper.cpp +++ b/library/md5/md5wrapper.cpp @@ -109,34 +109,34 @@ std::string md5wrapper::getHashFromString(std::string text) */ std::string md5wrapper::getHashFromFile(std::string filename) { - FILE *file; - MD5_CTX context; - - int len; - unsigned char buffer[1024], digest[16]; - - //open file - if ((file = fopen (filename.c_str(), "rb")) == NULL) - { - return "-1"; - } - - //init md5 - md5->MD5Init (&context); - - //read the filecontent - while ( (len = fread (buffer, 1, 1024, file)) ) - { - md5->MD5Update (&context, buffer, len); - } - - /* - generate hash, close the file and return the - hash as std::string - */ - md5->MD5Final (digest, &context); - fclose (file); - return convToString(digest); + FILE *file; + MD5_CTX context; + + int len; + unsigned char buffer[1024], digest[16]; + + //open file + if ((file = fopen (filename.c_str(), "rb")) == NULL) + { + return "-1"; + } + + //init md5 + md5->MD5Init (&context); + + //read the filecontent + while ( (len = fread (buffer, 1, 1024, file)) ) + { + md5->MD5Update (&context, buffer, len); + } + + /* + generate hash, close the file and return the + hash as std::string + */ + md5->MD5Final (digest, &context); + fclose (file); + return convToString(digest); } /* diff --git a/library/LinuxMemAccess-ptrace.h b/library/unmaintained/LinuxMemAccess-ptrace.h similarity index 100% rename from library/LinuxMemAccess-ptrace.h rename to library/unmaintained/LinuxMemAccess-ptrace.h diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index e4e63d980..371d6e736 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -42,9 +42,16 @@ TARGET_LINK_LIBRARIES(dfmaterialtest dfhack) ADD_EXECUTABLE(dfposition position.cpp) TARGET_LINK_LIBRARIES(dfposition dfhack) -# incremental - incremental memory search tool, a foreshadowing of the future direction of dfhack -ADD_EXECUTABLE(dfincremental incrementalsearch.cpp) -TARGET_LINK_LIBRARIES(dfincremental dfhack) +#currently only stable on linux +IF(UNIX) + # incremental - incremental memory search tool, a foreshadowing of the future direction of dfhack + ADD_EXECUTABLE(dfincremental incrementalsearch.cpp) + TARGET_LINK_LIBRARIES(dfincremental dfhack) +ENDIF(UNIX) + +# suspendtest - test if suspend works. df should stop responding when suspended by dfhack +ADD_EXECUTABLE(dfsuspend suspendtest.cpp) +TARGET_LINK_LIBRARIES(dfsuspend dfhack) IF(UNIX) install(TARGETS @@ -59,6 +66,7 @@ dfmaterialtest dfbuildingsdump dfposition dfincremental +dfsuspend RUNTIME DESTINATION bin ) ENDIF(UNIX) \ No newline at end of file diff --git a/tools/incrementalsearch.cpp b/tools/incrementalsearch.cpp index 7f30de343..39f148a9c 100644 --- a/tools/incrementalsearch.cpp +++ b/tools/incrementalsearch.cpp @@ -21,7 +21,8 @@ using namespace std; #include #include -#include +#include +#include #include //TODO: lots of optimization diff --git a/tools/suspendtest.cpp b/tools/suspendtest.cpp new file mode 100644 index 000000000..07f5c680f --- /dev/null +++ b/tools/suspendtest.cpp @@ -0,0 +1,45 @@ +// Test suspend/resume + +#include +#include +#include +#include +#include +#include +using namespace std; + +#include +#include + +int main (void) +{ + string blah; + DFHack::API DF("Memory.xml"); + if(!DF.Attach()) + { + cerr << "DF not found" << endl; + return 1; + } + cout << "Attached, DF should be suspended now" << endl; + getline(cin, blah); + + DF.Resume(); + + cout << "Resumed, DF should be running" << endl; + getline(cin, blah); + + DF.Suspend(); + + cout << "Suspended, DF should be suspended now" << endl; + getline(cin, blah); + + if(!DF.Detach()) + { + cerr << "Can't detach from DF" << endl; + return 1; + } + cout << "Detached, DF should be running again" << endl; + getline(cin, blah); + + return 0; +} \ No newline at end of file