blob: 3e8faa475de5fcad8608fb6bacb12ed89a0e903f [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2007-2008 VMware, Inc. 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.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* 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 VMWARE, INC. 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.
*/
/* Copyright (c) 2003-2007 Determina Corp. */
/* Copyright (c) 2002-2003 Massachusetts Institute of Technology */
/* Copyright (c) 2002 Hewlett-Packard Company */
// DynamoRIODoc.cpp : implementation of the CDynamoRIODoc class
//
#include "stdafx.h"
#include <assert.h>
#include <commdlg.h>
#include <imagehlp.h>
#include <direct.h>
#include "DynamoRIO.h"
#include "DynamoRIODoc.h"
#include "ShellInterface.h"
#include "CmdlineDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define INJECTOR_SUBPATH _T("\\bin\\drinject.exe")
/////////////////////////////////////////////////////////////////////////////
// CDynamoRIODoc
IMPLEMENT_DYNCREATE(CDynamoRIODoc, CDocument)
BEGIN_MESSAGE_MAP(CDynamoRIODoc, CDocument)
//{{AFX_MSG_MAP(CDynamoRIODoc)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CDynamoRIODoc construction/destruction
CDynamoRIODoc::CDynamoRIODoc()
{
#ifndef DRSTATS_DEMO
InitPaths();
CShellInterface::Initialize();
#endif
// make sure framework prompts to "save unsaved work"
SetModifiedFlag(TRUE);
}
#ifndef DRSTATS_DEMO
void CDynamoRIODoc::InitPaths()
{
// limit to 200 to give room for rest of injector path
int len = GetEnvironmentVariable(_T("DYNAMORIO_HOME"), m_dynamorio_home, _MAX_DIR);
assert(len <= _MAX_DIR && len + _tcslen(INJECTOR_SUBPATH) < MAX_PATH);
if (len == 0 || len > _MAX_DIR)
m_dynamorio_home[0] = _T('\0');
else
NULL_TERMINATE_BUFFER(m_dynamorio_home);
_stprintf(m_injector_path, _T("%s%s"), m_dynamorio_home, INJECTOR_SUBPATH);
#if 0
// there are issues with privileges to write to Program Files, so we no longer do this:
_stprintf(m_logs_dir, _T("%s\\logs"), m_dynamorio_home);
#else
// instead we do $USERPROFILE\\Application Data\\DynamoRIO
TCHAR userprof[MAX_PATH];
CString profile_dir;
len = GetEnvironmentVariable(_T("USERPROFILE"), userprof, MAX_PATH);
assert(len <= MAX_PATH);
if (len == 0 || len > MAX_PATH) {
// on NT, use $SYSTEMROOT\\Profiles
len = GetEnvironmentVariable(_T("SYSTEMROOT"), userprof, MAX_PATH);
assert(len > 0 && len < MAX_PATH);
profile_dir.Format(_T("%s\\Profiles"), userprof);
} else {
profile_dir.Format(_T("%s"), userprof);
}
_stprintf(m_logs_dir, _T("%s\\Application Data\\DynamoRIO"), profile_dir);
if (!SetCurrentDirectory(m_logs_dir)) {
if (!CreateDirectory(m_logs_dir, NULL)) {
_stprintf(userprof, _T("Cannot create default working directory %s"), m_logs_dir);
MessageBox(NULL, userprof, _T("Error"), MB_OK | MYMBFLAGS);
_stprintf(m_logs_dir, _T("c:\\")); // any better ideas?
}
}
#endif
}
#endif /* !DRSTATS_DEMO */
CDynamoRIODoc::~CDynamoRIODoc()
{
#ifndef DRSTATS_DEMO
CShellInterface::Uninitialize();
#endif
}
BOOL CDynamoRIODoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
// blank title initially
SetTitle(_T(""));
return TRUE;
}
BOOL CDynamoRIODoc::OnOpenDocument(LPCTSTR lpszPathName)
{
if (!CDocument::OnOpenDocument(lpszPathName))
return FALSE;
// initialization that doesn't happen in Serialize
// for us, that's all of it
return TRUE;
}
#ifndef DRSTATS_DEMO
BOOL CDynamoRIODoc::RunApplication(LPCTSTR lpszPathName)
{
// application name (may be different than lpszPathName, for shortcuts)
TCHAR app_name[MAX_PATH];
// working directory
TCHAR rundir[_MAX_DIR];
// entire command line = app_name + arguments
TCHAR app_cmdline[MAX_PATH*2];
// buffer for error messages
TCHAR msg[MAX_PATH*3];
// default working directory
TCHAR default_rundir[_MAX_DIR];
_stprintf(default_rundir, _T("%s"), m_logs_dir);
CString app_args; // will be used to build app_cmdline
// if app is a shortcut, resolve it now
int len = _tcslen(lpszPathName);
TCHAR * last4 = (TCHAR *)lpszPathName + len - 4;
if (_tcscmp(last4, _T(".lnk")) == 0) {
TCHAR params[MAX_PATH];
if (CShellInterface::ResolveLinkFile((TCHAR *)lpszPathName, app_name,
params, rundir, CDynamoRIOApp::GetActiveView()->m_hWnd)) {
_stprintf(msg, _T("Resolved link file to %s %s\nin directory %s\n"),
app_name, params, rundir);
MessageBox(NULL, msg, _T("Link File"), MB_OK | MYMBFLAGS);
app_args.Format(_T("%s"), params);
} else {
_stprintf(msg, _T("Failed to resolve link file"));
MessageBox(NULL, msg, _T("Error"), MB_OK | MYMBFLAGS);
return FALSE;
}
} else {
_stprintf(app_name, _T("%s"), lpszPathName);
// ask for arguments and working dir
// pass in default working dir
CString defwdir(default_rundir);
CCmdlineDlg dlg(defwdir);
int res = dlg.DoModal();
if (res == IDCANCEL)
return FALSE;
CString wdir = dlg.GetWorkingDir();
_tcscpy(rundir, wdir.GetBuffer(0));
app_args = dlg.GetArguments();
}
// construct app_cmdline
// if passing app name to injector as argument, must quote it
// if running natively, do not quote it
if (CDynamoRIOApp::SystemwideSet()) {
_stprintf(app_cmdline, _T("%s %s"), app_name, app_args);
} else {
_stprintf(app_cmdline, _T("\"%s\" %s"), app_name, app_args);
}
// if rundir has environment vars, resolve them
CString dir(rundir);
CString realdir(_T(""));
int start = dir.Find(_T('%'));
int old_end = 0;
while (start != -1) {
realdir += dir.Mid(old_end, start - old_end);
int end = dir.Find(_T('%'), start+1);
if (end == -1) {
realdir += dir.Right(dir.GetLength() - start);
break;
}
#if 0 // debugging stuff
_stprintf(msg, _T("from %d to %d"), start, end);
MessageBox(NULL, msg, _T("env var value"), MB_OK);
#endif
CString name = dir.Mid(start + 1, end - start - 1);
#if 0 // debugging stuff
MessageBox(NULL, name.GetBuffer(0), _T("env var name"), MB_OK);
#endif
int len = GetEnvironmentVariable(name.GetBuffer(0), msg, MAX_PATH);
assert(len <= MAX_PATH);
if (len == 0 || len > MAX_PATH)
msg[0] = _T('\0');
#if 0 // debugging stuff
MessageBox(NULL, msg, _T("env var value"), MB_OK);
#endif
realdir += msg;
old_end = end + 1;
start = dir.Find(_T('%'), end+1);
}
if (!realdir.IsEmpty())
_stprintf(rundir, _T("%s"), realdir.GetBuffer(0));
// if rundir is empty, use default
// FIXME: have option to set default?
if (rundir[0] == _T('\0'))
_tcscpy(rundir, default_rundir);
// be robust -- make sure app file exists
CFile check;
if (!check.Open(app_name, CFile::modeRead|CFile::shareDenyNone)) {
_stprintf(msg, _T("Application %s does not exist"), app_name);
MessageBox(NULL, msg, _T("Error"), MB_OK | MYMBFLAGS);
return FALSE;
}
check.Close();
// now go to run directory
if (!SetCurrentDirectory(rundir)) {
_stprintf(msg, _T("Error changing to working directory %s"), rundir);
MessageBox(NULL, msg, _T("Error"), MB_OK | MYMBFLAGS);
return FALSE;
}
// now launch application
TCHAR *launch_name;
TCHAR launch_cmdline[MAX_PATH*2];
if (CDynamoRIOApp::SystemwideSet()) {
// just launch app natively, systemwide injector will catch it
launch_name = app_name;
// note that we want target app name as part of cmd line
_tcscpy(launch_cmdline, app_cmdline);
} else {
// explicitly launch app under injector
launch_name = m_injector_path;
// note that we want target app name as part of cmd line
TCHAR * dll_path = CDynamoRIOApp::GetDllPath();
_stprintf(launch_cmdline, _T("\"%s\" \"%s\" %s"),
m_injector_path, dll_path, app_cmdline);
// be robust
if (!check.Open(m_injector_path, CFile::modeRead|CFile::shareDenyNone)) {
_stprintf(msg, _T("DynamoRIO injector %s does not exist"), m_injector_path);
MessageBox(NULL, msg, _T("DynamoRIO Configuration Error"), MB_OK | MYMBFLAGS);
return FALSE;
}
check.Close();
if (!check.Open(dll_path, CFile::modeRead|CFile::shareDenyNone)) {
_stprintf(msg, _T("DynamoRIO library %s does not exist"), dll_path);
MessageBox(NULL, msg, _T("DynamoRIO Configuration Error"), MB_OK | MYMBFLAGS);
return FALSE;
}
check.Close();
}
assert(_tcslen(rundir) + _tcslen(launch_cmdline) + _tcslen(_T("\nin directory\n")) < MAX_PATH*2);
#if 0 // I'm disabling this dialog, usually just annoying
_stprintf(msg, _T("%s\nin directory\n%s"), launch_cmdline, rundir);
MessageBox(NULL, msg, _T("About to run"), MB_OK | MB_TOPMOST);
#endif // 0
#if 0 // I'm taking this out, it requires an extra library and doesn't add much
// FIXME: do this up above, instead of checking app_name exists?
PLOADED_IMAGE li;
#ifdef UNICODE
// ImageLoad does not take unicode strings
char ansi_launch_name[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, launch_name, _tcslen(launch_name),
ansi_launch_name, MAX_PATH, NULL, NULL);
if (li = ImageLoad(ansi_launch_name, NULL))
#else
if (li = ImageLoad(launch_name, NULL))
#endif
{
ImageUnload(li);
} else {
_stprintf(msg, _T("Failed to load %s"), launch_name);
MessageBox(NULL, msg, _T("Error"), MB_OK | MYMBFLAGS);
return FALSE;
}
#endif // 0
// Launch the application process
STARTUPINFO si;
PROCESS_INFORMATION pi;
STARTUPINFO mysi;
GetStartupInfo(&mysi);
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = mysi.hStdInput;
si.hStdOutput = mysi.hStdOutput;
si.hStdError = mysi.hStdError;
/* Must specify TRUE for bInheritHandles so child inherits stdin! */
if (!CreateProcess(launch_name, launch_cmdline, NULL, NULL, TRUE,
DETACHED_PROCESS, // to avoid console creation!
NULL, NULL, &si, &pi)) {
_stprintf(msg, _T("Failed to load %s"), app_name);
MessageBox(NULL, msg, _T("Error"), MB_OK | MYMBFLAGS);
return FALSE;
}
#if 0
// FIXME: We have the injector's pid, not the child's pid!
if (!CDynamoRIOApp::GetActiveView()->SelectProcess(pi.dwProcessId))
MessageBox(NULL, _T("Failed to select"), _T("Error"), MB_OK | MYMBFLAGS);
#endif
// it takes some time for the new process to start up and its shared memory
// to become visible, so wait for it, but don't wait forever -- there could be
// a usage error or the new process may crash
int i = 0;
// wait 10ms*200 = 2 seconds
while (i < 200) {
// change the <no instances found> entry
if (CDynamoRIOApp::GetActiveView()->UpdateProcessList())
break;
Sleep(10);
i++;
}
// don't call SetTitle -- View sets it to what's being viewed
// SetTitle(lpszPathName);
return TRUE;
}
#endif /* !DRSTATS_DEMO */
/////////////////////////////////////////////////////////////////////////////
// CDynamoRIODoc serialization
void CDynamoRIODoc::Serialize(CArchive& ar)
{
if (ar.IsStoring()) {
// TODO: add storing code here
} else {
// TODO: add loading code here
}
}
/////////////////////////////////////////////////////////////////////////////
// CDynamoRIODoc diagnostics
#ifdef _DEBUG
void CDynamoRIODoc::AssertValid() const
{
CDocument::AssertValid();
}
void CDynamoRIODoc::Dump(CDumpContext& dc) const
{
CDocument::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CDynamoRIODoc commands
BOOL CDynamoRIODoc::SaveModified()
{
#ifndef DRSTATS_DEMO
CDynamoRIOApp::AboutToExit();
#endif
return CDocument::SaveModified();
}