Got rid of merge conflict

This commit is contained in:
Aaron 2015-08-26 09:28:48 -07:00
commit f6958d7212
6 changed files with 293 additions and 58 deletions

View file

@ -8,17 +8,16 @@ include_directories(../ext-src/gtest/fused-src impl)
link_directories(${monad_native_BINARY_DIR})
# source file definitions
set(LIB_SOURCE_FILES impl/getcurrentprocessorid.cpp impl/getusername.cpp impl/terminal.cpp impl/getcpinfo.cpp)
set(LIB_SOURCE_FILES impl/getcurrentprocessorid.cpp impl/getusername.cpp impl/terminal.cpp impl/getcomputername.cpp impl/getcpinfo.cpp)
set(HOST_COMMON_SOURCE_FILES host/common/coreclrutil.cpp)
set(HOST_COMMON_TEST_SOURCE_FILES tests/host/test-hostutil.cpp)
set(TEST_SOURCE_FILES tests/test-getcurrentprocessid.cpp tests/test-getcpinfo.cpp tests/test-getusername.cpp ${HOST_COMMON_SOURCE_FILES} ${HOST_COMMON_TEST_SOURCE_FILES})
set(TEST_SOURCE_FILES tests/test-getcurrentprocessid.cpp tests/test-getusername.cpp tests/test-getcomputername.cpp tests/test-getcpinfo.cpp ${HOST_COMMON_SOURCE_FILES} ${HOST_COMMON_TEST_SOURCE_FILES})
set(SOURCE_FILES main.cpp ../ext-src/gtest/fused-src/gtest/gtest-all.cc)
set(HOST_CMDLINE_SOURCE_FILES host/cmdline/main.cpp ${HOST_COMMON_SOURCE_FILES})
# target definitions
add_library(ps SHARED ${LIB_SOURCE_FILES})
add_executable(monad_native ${SOURCE_FILES} ${TEST_SOURCE_FILES})
add_executable(host_cmdline ${HOST_CMDLINE_SOURCE_FILES})

View file

@ -16,7 +16,7 @@ void printHelp()
{
std::cerr << "PS CoreCLR host" << std::endl;
std::cerr << "Usage: host_cmdline [-c coreclr_path] [-alc load_context_assembly] [-s search_paths]" << std::endl;
std::cerr << " [-b base_path] assembly_name type_name function_name [...]" << std::endl;
std::cerr << " [-b base_path] assembly [...]" << std::endl;
std::cerr << std::endl;
std::cerr << "What it does:" << std::endl;
std::cerr << "- by default the host assumes that CoreCLR is located in the same folder" << std::endl;
@ -28,8 +28,8 @@ void printHelp()
std::cerr << " + a custom assembly containing the PowerShellAssemblyLoadContext can" << std::endl;
std::cerr << " be provided with the -alc command line argument" << std::endl;
std::cerr << "- all additional parameters at the end of the command line are forwarded" << std::endl;
std::cerr << " to the specified entry function in the assembly" << std::endl;
std::cerr << "- the host will execute the specified entry function in the specified assembly" << std::endl;
std::cerr << " to the Main function in the assembly" << std::endl;
std::cerr << "- the host will execute the Main function in the specified assembly" << std::endl;
std::cerr << " + this assembly has to be located in the search path" << std::endl;
std::cerr << "- by default the host will add the current working directory to the assembly search path" << std::endl;
std::cerr << " + this can be overridden with the -s command line argument" << std::endl;
@ -37,8 +37,8 @@ void printHelp()
std::cerr << "- by default the host assumes the PS base path for the assembly load context is the current" << std::endl;
std::cerr << " working directory" << std::endl;
std::cerr << " + this can be overridden with the -b command line argument" << std::endl;
std::cerr << "- the function signature of the function that gets executed must be:" << std::endl;
std::cerr << " public static int UnmanagedMain(int argc, [MarshalAs(UnmanagedType.LPArray,ArraySubType=UnmanagedType.LPStr,SizeParamIndex=0)] String[] argv)" << std::endl;
std::cerr << "- the function signature of the Main function that gets executed must be:" << std::endl;
std::cerr << " static void Main(string[] args)" << std::endl;
std::cerr << std::endl;
std::cerr << "Options:" << std::endl;
std::cerr << "-c, --clr-path path to libcoreclr.so and the managed CLR assemblies" << std::endl;
@ -50,13 +50,10 @@ void printHelp()
std::cerr << " separated by :" << std::endl;
std::cerr << " unless part of the same folder as CoreCLR, the main assembly referenced with the assembly_name" << std::endl;
std::cerr << " argument, must always be added to the TPA list with this parameter" << std::endl;
std::cerr << "assembly_name the assembly name of the assembly to execute" << std::endl;
std::cerr << " must be available in the search path" << std::endl;
std::cerr << "type_name the type name where the function can be found" << std::endl;
std::cerr << "function_name the function to execute (must have the function signature described above!)" << std::endl;
std::cerr << "assembly the path of the assembly to execute relative to current directory" << std::endl;
std::cerr << std::endl;
std::cerr << "Example:" << std::endl;
std::cerr << "./host_cmdline -c /test/coreclr -alc /test/ps/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll -s /test/ps -b /test/ps -tpa /test/ps/powershell-simple.exe 'powershell-simple, version=1.0.0.0, culture=neutral, PublicKeyToken=null' 'ps_hello_world.Program' 'UnmanagedMain' 'get-process'" << std::endl;
std::cerr << "./host_cmdline -c /test/coreclr -alc /test/ps/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll -s /test/ps -b /test/ps -tpa /test/ps/powershell-simple.exe 'powershell-simple, version=1.0.0.0, culture=neutral, PublicKeyToken=null' 'get-process'" << std::endl;
}
struct Args
@ -73,9 +70,7 @@ struct Args
std::string searchPaths;
std::string basePath;
std::string tpaList;
std::string entryAssemblyName;
std::string entryTypeName;
std::string entryFunctionName;
std::string entryAssemblyPath;
int argc;
char** argv;
bool verbose;
@ -88,9 +83,7 @@ struct Args
std::cerr << "- searchPaths " << searchPaths << std::endl;
std::cerr << "- basePath " << basePath << std::endl;
std::cerr << "- tpaList " << tpaList << std::endl;
std::cerr << "- entryAssemblyName " << entryAssemblyName << std::endl;
std::cerr << "- entryTypeName " << entryTypeName << std::endl;
std::cerr << "- entryFunctionName " << entryFunctionName << std::endl;
std::cerr << "- entryAssemblyPath " << entryAssemblyPath << std::endl;
std::cerr << "- argc " << argc << std::endl;
std::cerr << "- verbose " << (verbose ? "true" : "false") << std::endl;
}
@ -141,17 +134,9 @@ bool parseCmdline(const int argc, char** argv, Args& args)
{
args.verbose = true;
}
else if (args.entryAssemblyName == "")
else if (args.entryAssemblyPath == "")
{
args.entryAssemblyName = arg;
}
else if (args.entryTypeName == "")
{
args.entryTypeName = arg;
}
else if (args.entryFunctionName == "")
{
args.entryFunctionName = arg;
args.entryAssemblyPath = arg;
}
else
{
@ -165,18 +150,10 @@ bool parseCmdline(const int argc, char** argv, Args& args)
}
// check for mandatory parameters
if (args.entryAssemblyName == "")
if (args.entryAssemblyPath == "")
{
std::cerr << "error: assembly_name argument missing" << std::endl;
}
if (args.entryTypeName == "")
{
std::cerr << "error: type_name argument missing" << std::endl;
}
if (args.entryFunctionName == "")
{
std::cerr << "error: function_name argument missing" << std::endl;
}
return true;
}
@ -410,23 +387,12 @@ int main(int argc, char** argv)
}
loaderDelegate(psBasePath16.c_str());
// call the unmanaged entry point for PowerShell
typedef int (*UnmanagedMain)(int argc, char const* const* argv);
UnmanagedMain unmanagedMain = nullptr;
status = createDelegate(
hostHandle,
domainId,
args.entryAssemblyName.c_str(),
args.entryTypeName.c_str(),
args.entryFunctionName.c_str(),
(void**)&unmanagedMain);
if (0 > status)
{
std::cerr << "could not create delegate for UnmanagedMain - status: " << std::hex << status << std::endl;
return 4;
}
int exitCode = unmanagedMain(args.argc,args.argv);
// call into Main of powershell-simple.exe
unsigned int exitCode;
executeAssembly(hostHandle, domainId, args.argc,
(const char**)args.argv,
(currentDirAbsolutePath+"/"+args.entryAssemblyPath).c_str(),
&exitCode);
// shutdown CoreCLR
status = shutdownCoreCLR(hostHandle,domainId);

View file

@ -0,0 +1,127 @@
//! @file getcomputername.cpp
//! @author Aaron Katz <v-aakatz@microsoft.com>
//! @brief Implements GetComputerName Win32 API
#include <errno.h>
#include <langinfo.h>
#include <locale.h>
#include <unistd.h>
#include <string>
#include <unicode/unistr.h>
#include "getcomputername.h"
//! @brief GetComputerName retrieves the name of the host associated with
//! the current thread.
//!
//! GetComputerNameW is the Unicode variation. See [MSDN documentation].
//!
//! @param[out] lpBuffer
//! @parblock
//! A pointer to the buffer to receive the hosts's
//! name. If this buffer is not large enough to contain the
//! entire user hosts name, the function fails.
//!
//! WCHAR_T* is a Unicode [LPTSTR].
//! @endparblock
//!
//! @param[in, out] lpnSize
//! @parblock
//! On input, this variable specifies the size of the lpBuffer buffer,
//! in TCHARs. On output, the variable receives the number of TCHARs
//! copied to the buffer, including the terminating null character.
//!
//! TCHAR is a Unicode 16-bit [WCHAR].
//!
//! If lpBuffer is too small, the function fails and GetLastError
//! returns ERROR_INSUFFICIENT_BUFFER. This parameter receives the
//! required buffer size, including the terminating null character.
//! @endparblock
//!
//! @exception errno Passes these errors via errno to GetLastError:
//! - ERROR_INVALID_PARAMETER: parameter is not valid
//! - ERROR_BAD_ENVIRONMENT: locale is not UTF-8
//! - ERROR_TOO_MANY_OPEN_FILES: already have the maximum allowed number of open files
//! - ERROR_NO_ASSOCIATION: calling process has no controlling terminal
//! - ERROR_INSUFFICIENT_BUFFER: buffer not large enough to hold username string
//! - ERROR_NO_SUCH_USER: there was no corresponding entry in the utmp-file
//! - ERROR_OUTOFMEMORY: insufficient memory to allocate passwd structure
//! - ERROR_NO_ASSOCIATION: standard input didn't refer to a terminal
//! - ERROR_INVALID_FUNCTION: getlogin_r() returned an unrecognized error code
//!
//! @retval TRUE If the function succeeds, the return value is a nonzero
//! value, and the variable pointed to by lpnSize contains the number
//! of TCHARs copied to the buffer specified by lpBuffer, including
//! the terminating null character.
//!
//! @retval FALSE If the function fails, the return value is zero. To get
//! extended error information, call GetLastError.
//!
//! [MSDN documentation]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724295%28v=vs.85%29.aspx
//! [WCHAR]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx#WCHAR
//! [LPTSTR]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx#LPTSTR
BOOL GetComputerNameW(WCHAR_T* lpBuffer, LPDWORD lpnSize)
{
errno = 0;
// Check parameters
if (!lpBuffer || !lpnSize)
{
errno = ERROR_INVALID_PARAMETER;
return FALSE;
}
// Select locale from environment
setlocale(LC_ALL, "");
// Check that locale is UTF-8
if (nl_langinfo(CODESET) != std::string("UTF-8"))
{
errno = ERROR_BAD_ENVIRONMENT;
return FALSE;
}
// Get computername from system in a thread-safe manner
std::string computername(HOST_NAME_MAX, 0);
int err = gethostname(&computername[0], computername.length());
// Map errno to Win32 Error Codes
if (err != 0)
{
switch (errno)
{
case EFAULT:
errno = ERROR_INVALID_ADDRESS;
break;
case ENAMETOOLONG:
errno = ERROR_GEN_FAILURE;
break;
default:
errno = ERROR_INVALID_FUNCTION;
}
return FALSE;
}
// Convert to UnicodeString
auto computername16 = icu::UnicodeString::fromUTF8(computername.c_str());
// Terminate string with null
computername16.append('\0');
// Size in WCHARs including null
const DWORD size = computername16.length();
//Check if parameters passed enough buffer space
if (size > *lpnSize)
{
errno = ERROR_INSUFFICIENT_BUFFER;
// Set lpnSize if buffer is too small to inform user
// of necessary size
*lpnSize= size;
return FALSE;
}
// Extract string as UTF-16LE to buffer
computername16.extract(0, size, reinterpret_cast<char*>(lpBuffer), "UTF-16LE");
*lpnSize = size;
return TRUE;
}

View file

@ -0,0 +1,9 @@
#pragma once
#include "pal.h"
PAL_BEGIN_EXTERNC
BOOL GetComputerNameW(WCHAR_T* lpBuffer, LPDWORD lpnSize);
PAL_END_EXTERNC

View file

@ -69,7 +69,6 @@
#define INFINITE 0xFFFFFFFF
#define WINAPI
#define S_OK 0
#define ERROR_GEN_FAILURE 31
#define TRUE 1
#define FALSE 0
#define ERROR_INVALID_PARAMETER 87
@ -81,7 +80,8 @@
#define ERROR_NO_SUCH_USER 0x00000525
#define ERROR_INVALID_FUNCTION 0x00000001
#define MAX_PATH 0x00000104
#define ERROR_BAD_ENVIRONMENT 0x0000000A
#define ERROR_INVALID_ADDRESS 0x000001e7
#define ERROR_GEN_FAILURE 0x0000001F
typedef unsigned long long uint64;
#endif

View file

@ -0,0 +1,134 @@
//! @file test-getcomputername.cpp
//! @author Aaron Katz <v-aakatz@microsoft.com>
//! @brief Unit tests for GetComputerNameW
#include <string>
#include <cstring>
#include <vector>
#include <unistd.h>
#include <gtest/gtest.h>
#include <unicode/unistr.h>
#include "getcomputername.h"
//! Test fixture for GetComputerNameTest
class GetComputerNameTest : public ::testing::Test
{
protected:
DWORD lpnSize;
std::vector<WCHAR_T> lpBuffer;
BOOL result;
std::string expectedComputerName;
DWORD expectedSize;
//Get expected result from using linux call
GetComputerNameTest()
{
expectedComputerName.resize(HOST_NAME_MAX);
BOOL ret = gethostname(&expectedComputerName[0], expectedComputerName.length());
EXPECT_EQ(0, ret);
expectedSize = std::strlen(expectedComputerName.c_str()) + 1;
expectedComputerName.resize(expectedSize - 1);
}
//! Invokes GetComputerNameW with lpnSize and lpBuffer, saves result.
//!
//! @param size Assigns to lpnSize and allocates lpBuffer with
//! size number of null characters.
void TestWithSize(DWORD size)
{
lpnSize = size;
lpBuffer.assign(lpnSize, 0);
result = GetComputerNameW(&lpBuffer[0], &lpnSize);
}
void TestSuccess()
{
SCOPED_TRACE("");
//! Returns TRUE on success.
EXPECT_EQ(TRUE, result);
//! Sets lpnSize to number of WCHARs including null.
ASSERT_EQ(expectedSize, lpnSize);
// Read lpBuffer into UnicodeString (without null)
const char* begin = reinterpret_cast<char*>(&lpBuffer[0]);
icu::UnicodeString computername16(begin, (lpnSize-1)*sizeof(UChar), "UTF-16LE");
// Convert to UTF-8 for comparison
std::string computername;
computername16.toUTF8String(computername);
ASSERT_EQ(expectedSize, computername.length() + 1);
//! Returned computername(after conversion) is what was expected.
EXPECT_EQ(expectedComputerName, computername);
}
void TestInvalidParameter()
{
SCOPED_TRACE("");
// returns FALSE on failure
EXPECT_EQ(FALSE, result);
// sets errno to ERROR_INVALID_PARAMETER when lpBuffer is null
EXPECT_EQ(errno, ERROR_INVALID_PARAMETER);
}
void TestInsufficientBuffer()
{
SCOPED_TRACE("");
// returns FALSE on failure
EXPECT_EQ(FALSE, result);
// sets errno to ERROR_INSUFFICIENT_BUFFER
EXPECT_EQ(errno, ERROR_INSUFFICIENT_BUFFER);
// sets lpnSize to length of username + null
ASSERT_EQ(expectedSize, lpnSize);
}
};
TEST_F(GetComputerNameTest, BufferAsNullButNotBufferSize)
{
lpnSize = 1;
result = GetComputerNameW(NULL, &lpnSize);
TestInvalidParameter();
// does not reset lpnSize
EXPECT_EQ(1, lpnSize);
}
TEST_F(GetComputerNameTest, BufferSizeAsNullButNotBuffer)
{
lpBuffer.push_back('\0');
result = GetComputerNameW(&lpBuffer[0], NULL);
TestInvalidParameter();
}
TEST_F(GetComputerNameTest, BufferSizeAsZero)
{
TestWithSize(0);
EXPECT_EQ(errno, ERROR_INVALID_PARAMETER);
}
TEST_F(GetComputerNameTest, BufferSizeAsUserNameMinusOne)
{
// the buffer is also too small
TestWithSize(expectedSize-1);
TestInsufficientBuffer();
}
TEST_F(GetComputerNameTest, BufferSizeAsUserNamePlusOne)
{
// the buffer is exactly big enough
TestWithSize(expectedSize+1);
TestSuccess();
}
TEST_F(GetComputerNameTest, BufferSizeAsLoginNameMax)
{
// the buffer larger than needed
TestWithSize(HOST_NAME_MAX);
TestSuccess();
}