<commit_before>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// Compiler engine class
//==============================================================================

#include "compilerengine.h"
#include "compilermath.h"
#include "corecliutils.h"

//==============================================================================

#include <QApplication>
#include <QFile>
#include <QIODevice>
#include <QTemporaryFile>
#include <QTextStream>

//==============================================================================

#include "llvmdisablewarnings.h"
    #include "llvm/IR/LLVMContext.h"
    #include "llvm/IR/Module.h"
    #include "llvm/Support/TargetSelect.h"
    #include "llvm/Support/Host.h"

    #include "clang/Basic/DiagnosticOptions.h"
    #include "clang/CodeGen/CodeGenAction.h"
    #include "clang/Driver/Compilation.h"
    #include "clang/Driver/Driver.h"
    #include "clang/Driver/Tool.h"
    #include "clang/Frontend/CompilerInstance.h"
    #include "clang/Frontend/TextDiagnosticPrinter.h"
#include "llvmenablewarnings.h"

//==============================================================================

#include <string>

//==============================================================================

namespace OpenCOR {
namespace Compiler {

//==============================================================================

CompilerEngine::CompilerEngine() :
    mExecutionEngine(std::unique_ptr<llvm::ExecutionEngine>()),
    mError(QString())
{
}

//==============================================================================

CompilerEngine::~CompilerEngine()
{
    // Reset ourselves

    reset();
}

//==============================================================================

void CompilerEngine::reset(const bool &pResetError)
{
    // Reset some internal objects

    delete mExecutionEngine.release();

    mExecutionEngine = std::unique_ptr<llvm::ExecutionEngine>();

    if (pResetError)
        mError = QString();
}

//==============================================================================

bool CompilerEngine::hasError() const
{
    // Return whether an error occurred

    return mError.size();
}

//==============================================================================

QString CompilerEngine::error() const
{
    // Return the compiler engine's error

    return mError;
}

//==============================================================================

bool CompilerEngine::compileCode(const QString &pCode)
{
    // Reset our compiler engine

    reset();

    // Determine our target triple
    // Note: normally, we would call llvm::sys::getProcessTriple(), but this
    //       returns the information about the system on which LLVM was built.
    //       In most cases it is fine, but on OS X it may be a problem. Indeed,
    //       with OS X 10.9, Apple decided to extend the C standard by adding
    //       some functions (e.g. __exp10()). So, if the given code needs one of
    //       those functions, then OpenCOR will crash if run on an 'old' version
    //       of OS X. So, to avoid this issue, we set the target triple
    //       ourselves, based on the system on which OpenCOR is being used...

    std::string targetTriple;

#if defined(Q_OS_WIN)
    targetTriple = (sizeof(void *) == 4)?"i686-pc-windows-msvc-elf":"x86_64-pc-windows-msvc-elf";
    // Note: MCJIT currently works only through the ELF object format, hence we
    //       are appending "-elf"...
    //---GRY--- WE WERE ORIGINALLY TRYING TO USE i686-pc-win32-elf AND
    //          x86_64-pc-win32-elf FOR WIN32 AND WIN64, RESPECTIVELY, BUT
    //          THOUGH IT WORKS FINE ON WIN64, IT DOESN'T ON WIN32. INDEED, ON
    //          THAT PLATFORM, WE GET THE SAME PROBLEM AS THE ONE REPORTED IN
    //          ISSUE #583 (SEE https://github.com/opencor/opencor/issues/583).
    //          NOW, THINGS MIGHT BE DIFFERENT ONCE LLVM 3.6.1 IS OUT, SO WE
    //          MIGHT WANT TO REVISIT THE SITUATION THEN. UNTIL THEN, WE USE
    //          i686-pc-windows-msvc-elf AND x86_64-pc-windows-msvc-elf SINCE
    //          WE KNOW THEM TO WORK FINE (THANKS TO THE LLVM PATCH USED TO FIX
    //          ISSUE #583)...
#elif defined(Q_OS_LINUX)
    targetTriple = (sizeof(void *) == 4)?"i686-pc-linux-gnu":"x86_64-pc-linux-gnu";
#elif defined(Q_OS_MAC)
    targetTriple = "x86_64-apple-darwin"+std::to_string(QSysInfo::MacintoshVersion+2);
#else
    #error Unsupported platform
#endif

    // Get a driver to compile our code

#ifdef QT_DEBUG
    llvm::raw_ostream &outputStream = llvm::outs();
#else
    llvm::raw_ostream &outputStream = llvm::nulls();
#endif
    llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagnosticOptions = new clang::DiagnosticOptions();

    clang::DiagnosticsEngine diagnosticsEngine(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()),
                                               &*diagnosticOptions,
                                               new clang::TextDiagnosticPrinter(outputStream, &*diagnosticOptions));

    clang::driver::Driver driver("clang", targetTriple, diagnosticsEngine);

    driver.setCheckInputsExist(false);

    // Get a compilation object to which we pass some arguments

    llvm::StringRef dummyFileName("dummyFile.c");

    llvm::SmallVector<const char *, 16> compilationArguments;

    compilationArguments.push_back("clang");
    compilationArguments.push_back("-fsyntax-only");
    compilationArguments.push_back("-O3");
    compilationArguments.push_back("-ffast-math");
    compilationArguments.push_back("-Werror");
    compilationArguments.push_back(dummyFileName.data());

    std::unique_ptr<clang::driver::Compilation> compilation(driver.BuildCompilation(compilationArguments));

    if (!compilation) {
        mError = tr("the compilation object could not be created");

        return false;
    }

    // The compilation object should have only one command, so if it doesn't
    // then something went wrong

    const clang::driver::JobList &jobList = compilation->getJobs();

    if (    (jobList.size() != 1)
        || !llvm::isa<clang::driver::Command>(*jobList.begin())) {
        mError = tr("the compilation object must contain only one command");

        return false;
    }

    // Retrieve the command job

    const clang::driver::Command &command = llvm::cast<clang::driver::Command>(*jobList.begin());
    QString commandName = command.getCreator().getName();

    if (commandName.compare("clang")) {
        mError = tr("a clang command was expected, but a %1 command was found instead").arg(commandName);

        return false;
    }

    // Create a compiler invocation using our command's arguments

    const clang::driver::ArgStringList &commandArguments = command.getArguments();
    std::unique_ptr<clang::CompilerInvocation> compilerInvocation(new clang::CompilerInvocation());

    clang::CompilerInvocation::CreateFromArgs(*compilerInvocation,
                                              const_cast<const char **>(commandArguments.data()),
                                              const_cast<const char **>(commandArguments.data())+commandArguments.size(),
                                              diagnosticsEngine);

    // Map our dummy file to a memory buffer

    QByteArray codeByteArray = pCode.toUtf8();

    compilerInvocation->getPreprocessorOpts().addRemappedFile(dummyFileName, llvm::MemoryBuffer::getMemBuffer(codeByteArray.constData()).release());

    // Create a compiler instance to handle the actual work

    clang::CompilerInstance compilerInstance;

    compilerInstance.setInvocation(compilerInvocation.release());

    // Create the compiler instance's diagnostics engine

    compilerInstance.createDiagnostics();

    if (!compilerInstance.hasDiagnostics()) {
        mError = tr("the diagnostics engine could not be created");

        return false;
    }

    // Create and execute the frontend to generate an LLVM bitcode module
    // Note: the LLVM team has been meaning to modify
    //       CompilerInstance::ExecuteAction() so that we could specify the
    //       output stream we want to use (rather than always use llvm::errs()),
    //       but they have yet to actually do it, so we modified it ourselves...

    std::unique_ptr<clang::CodeGenAction> codeGenerationAction(new clang::EmitLLVMOnlyAction(&llvm::getGlobalContext()));

    if (!compilerInstance.ExecuteAction(*codeGenerationAction, outputStream)) {
        mError = tr("the code could not be compiled");

        reset(false);

        return false;
    }

    // Retrieve the LLVM bitcode module

    std::unique_ptr<llvm::Module> module = codeGenerationAction->takeModule();

    // Initialise the native target (and its ASM printer), so not only can we
    // then create an execution engine, but more importantly its data layout
    // will match that of our target platform

    llvm::InitializeNativeTarget();
    llvm::InitializeNativeTargetAsmPrinter();

    // Create and keep track of an execution engine

    mExecutionEngine = std::unique_ptr<llvm::ExecutionEngine>(llvm::EngineBuilder(std::move(module)).setEngineKind(llvm::EngineKind::JIT).create());

    if (!mExecutionEngine) {
        mError = tr("the execution engine could not be created");

        delete module.release();

        return false;
    }

    return true;
}

//==============================================================================

void * CompilerEngine::getFunction(const QString &pFunctionName)
{
    // Return the address of the requested function

    if (mExecutionEngine)
        return (void *) mExecutionEngine->getFunctionAddress(qPrintable(pFunctionName));
    else
        return 0;
}

//==============================================================================

}   // namespace Compiler
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Some minor cleaning up [ci skip].<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// Compiler engine class
//==============================================================================

#include "compilerengine.h"
#include "compilermath.h"
#include "corecliutils.h"

//==============================================================================

#include <QApplication>
#include <QFile>
#include <QIODevice>
#include <QTemporaryFile>
#include <QTextStream>

//==============================================================================

#include "llvmdisablewarnings.h"
    #include "llvm/IR/LLVMContext.h"
    #include "llvm/IR/Module.h"
    #include "llvm/Support/TargetSelect.h"
    #include "llvm/Support/Host.h"

    #include "clang/Basic/DiagnosticOptions.h"
    #include "clang/CodeGen/CodeGenAction.h"
    #include "clang/Driver/Compilation.h"
    #include "clang/Driver/Driver.h"
    #include "clang/Driver/Tool.h"
    #include "clang/Frontend/CompilerInstance.h"
    #include "clang/Frontend/TextDiagnosticPrinter.h"
#include "llvmenablewarnings.h"

//==============================================================================

#include <string>

//==============================================================================

namespace OpenCOR {
namespace Compiler {

//==============================================================================

CompilerEngine::CompilerEngine() :
    mExecutionEngine(std::unique_ptr<llvm::ExecutionEngine>()),
    mError(QString())
{
}

//==============================================================================

CompilerEngine::~CompilerEngine()
{
    // Reset ourselves

    reset();
}

//==============================================================================

void CompilerEngine::reset(const bool &pResetError)
{
    // Reset some internal objects

    delete mExecutionEngine.release();

    mExecutionEngine = std::unique_ptr<llvm::ExecutionEngine>();

    if (pResetError)
        mError = QString();
}

//==============================================================================

bool CompilerEngine::hasError() const
{
    // Return whether an error occurred

    return mError.size();
}

//==============================================================================

QString CompilerEngine::error() const
{
    // Return the compiler engine's error

    return mError;
}

//==============================================================================

bool CompilerEngine::compileCode(const QString &pCode)
{
    // Reset our compiler engine

    reset();

    // Determine our target triple
    // Note: normally, we would call llvm::sys::getProcessTriple(), but this
    //       returns the information about the system on which LLVM was built.
    //       In most cases it is fine, but on OS X it may be a problem. Indeed,
    //       with OS X 10.9, Apple decided to extend the C standard by adding
    //       some functions (e.g. __exp10()). So, if the given code needs one of
    //       those functions, then OpenCOR will crash if run on an 'old' version
    //       of OS X. So, to avoid this issue, we set the target triple
    //       ourselves, based on the system on which OpenCOR is being used...

    std::string targetTriple;

#if defined(Q_OS_WIN)
    targetTriple = (sizeof(void *) == 4)?"i686-pc-windows-msvc-elf":"x86_64-pc-windows-msvc-elf";
    // Note: MCJIT currently works only through the ELF object format, hence we
    //       are appending "-elf"...
    //---GRY--- WE WERE ORIGINALLY TRYING TO USE i686-pc-win32-elf AND
    //          x86_64-pc-win32-elf FOR WIN32 AND WIN64, RESPECTIVELY, BUT
    //          THOUGH IT WORKS FINE ON WIN64, IT DOESN'T ON WIN32. INDEED, ON
    //          THAT PLATFORM, WE GET THE SAME PROBLEM AS THE ONE REPORTED IN
    //          ISSUE #583 (SEE https://github.com/opencor/opencor/issues/583).
    //          NOW, THINGS MIGHT BE DIFFERENT ONCE LLVM 3.6.1 IS OUT, SO WE
    //          MIGHT WANT TO REVISIT THE SITUATION THEN. UNTIL THEN, WE USE
    //          i686-pc-windows-msvc-elf AND x86_64-pc-windows-msvc-elf SINCE
    //          WE KNOW THEM TO WORK FINE (THANKS TO THE LLVM PATCH USED TO FIX
    //          ISSUE #583)...
#elif defined(Q_OS_LINUX)
    targetTriple = (sizeof(void *) == 4)?"i686-pc-linux-gnu":"x86_64-pc-linux-gnu";
#elif defined(Q_OS_MAC)
    targetTriple = "x86_64-apple-darwin"+std::to_string(QSysInfo::MacintoshVersion+2);
#else
    #error Unsupported platform
#endif

    // Get a driver to compile our code

#ifdef QT_DEBUG
    llvm::raw_ostream &outputStream = llvm::outs();
#else
    llvm::raw_ostream &outputStream = llvm::nulls();
#endif
    llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagnosticOptions = new clang::DiagnosticOptions();

    clang::DiagnosticsEngine diagnosticsEngine(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()),
                                               &*diagnosticOptions,
                                               new clang::TextDiagnosticPrinter(outputStream, &*diagnosticOptions));

    clang::driver::Driver driver("clang", targetTriple, diagnosticsEngine);

    driver.setCheckInputsExist(false);

    // Get a compilation object to which we pass some arguments

    llvm::StringRef dummyFileName("dummyFile.c");

    llvm::SmallVector<const char *, 16> compilationArguments;

    compilationArguments.push_back("clang");
    compilationArguments.push_back("-fsyntax-only");
    compilationArguments.push_back("-O3");
    compilationArguments.push_back("-ffast-math");
    compilationArguments.push_back("-Werror");
    compilationArguments.push_back(dummyFileName.data());

    std::unique_ptr<clang::driver::Compilation> compilation(driver.BuildCompilation(compilationArguments));

    if (!compilation) {
        mError = tr("the compilation object could not be created");

        return false;
    }

    // The compilation object should have only one command, so if it doesn't
    // then something went wrong

    const clang::driver::JobList &jobList = compilation->getJobs();

    if (    (jobList.size() != 1)
        || !llvm::isa<clang::driver::Command>(*jobList.begin())) {
        mError = tr("the compilation object must contain only one command");

        return false;
    }

    // Retrieve the command job

    const clang::driver::Command &command = llvm::cast<clang::driver::Command>(*jobList.begin());
    QString commandName = command.getCreator().getName();

    if (commandName.compare("clang")) {
        mError = tr("a clang command was expected, but a %1 command was found instead").arg(commandName);

        return false;
    }

    // Create a compiler invocation using our command's arguments

    const clang::driver::ArgStringList &commandArguments = command.getArguments();
    std::unique_ptr<clang::CompilerInvocation> compilerInvocation(new clang::CompilerInvocation());

    clang::CompilerInvocation::CreateFromArgs(*compilerInvocation,
                                              commandArguments.data(),
                                              commandArguments.data()+commandArguments.size(),
                                              diagnosticsEngine);

    // Map our dummy file to a memory buffer

    QByteArray codeByteArray = pCode.toUtf8();

    compilerInvocation->getPreprocessorOpts().addRemappedFile(dummyFileName, llvm::MemoryBuffer::getMemBuffer(codeByteArray.constData()).release());

    // Create a compiler instance to handle the actual work

    clang::CompilerInstance compilerInstance;

    compilerInstance.setInvocation(compilerInvocation.release());

    // Create the compiler instance's diagnostics engine

    compilerInstance.createDiagnostics();

    if (!compilerInstance.hasDiagnostics()) {
        mError = tr("the diagnostics engine could not be created");

        return false;
    }

    // Create and execute the frontend to generate an LLVM bitcode module
    // Note: the LLVM team has been meaning to modify
    //       CompilerInstance::ExecuteAction() so that we could specify the
    //       output stream we want to use (rather than always use llvm::errs()),
    //       but they have yet to actually do it, so we modified it ourselves...

    std::unique_ptr<clang::CodeGenAction> codeGenerationAction(new clang::EmitLLVMOnlyAction(&llvm::getGlobalContext()));

    if (!compilerInstance.ExecuteAction(*codeGenerationAction, outputStream)) {
        mError = tr("the code could not be compiled");

        reset(false);

        return false;
    }

    // Retrieve the LLVM bitcode module

    std::unique_ptr<llvm::Module> module = codeGenerationAction->takeModule();

    // Initialise the native target (and its ASM printer), so not only can we
    // then create an execution engine, but more importantly its data layout
    // will match that of our target platform

    llvm::InitializeNativeTarget();
    llvm::InitializeNativeTargetAsmPrinter();

    // Create and keep track of an execution engine

    mExecutionEngine = std::unique_ptr<llvm::ExecutionEngine>(llvm::EngineBuilder(std::move(module)).setEngineKind(llvm::EngineKind::JIT).create());

    if (!mExecutionEngine) {
        mError = tr("the execution engine could not be created");

        delete module.release();

        return false;
    }

    return true;
}

//==============================================================================

void * CompilerEngine::getFunction(const QString &pFunctionName)
{
    // Return the address of the requested function

    if (mExecutionEngine)
        return (void *) mExecutionEngine->getFunctionAddress(qPrintable(pFunctionName));
    else
        return 0;
}

//==============================================================================

}   // namespace Compiler
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>#include "ItemList.h"

ItemList::ItemList(int x, int y, string str) {
	handle = GetStdHandle(STD_OUTPUT_HANDLE);
	coord = { (short)x, (short)y };
	buffer = new char[10];
	isClicked = false;
	isChecked = false;
	lbl = str;
	size = str.length() + 4;
	curserPosition = 0;
}

void ItemList::create() {
	SetConsoleCursorPosition(handle, coord);

	CONSOLE_SCREEN_BUFFER_INFO cbi;
	GetConsoleScreenBufferInfo(handle, &cbi);

	CONSOLE_CURSOR_INFO cci = { 100, FALSE };
	SetConsoleCursorInfo(handle, &cci);

	DWORD wAttr2 = BACKGROUND_GREEN | BACKGROUND_RED;
	SetConsoleTextAttribute(handle, wAttr2);
	cout << "[ ] " + lbl << endl;
}

void ItemList::check() {
	DWORD background;

	SetConsoleCursorPosition(handle, coord);
	if (!isChecked) {

		cout << "[X] " << lbl;

		if (!FillConsoleOutputAttribute(handle,
			BACKGROUND_BLUE | BACKGROUND_RED,
			size, coord, &background)) {
			cout << "failed to change the background" << endl;
			exit(1);
		}
		isChecked = true;
	}
	else {

		cout << "[ ] " << lbl;

		if (!FillConsoleOutputAttribute(
			handle, BACKGROUND_GREEN | BACKGROUND_RED,
			size, coord, &background)) {
			cout << "failed to change the background" << endl;
			exit(1);
		}
		isChecked = false;
	}
}
void ItemList::click() {
	DWORD color =  FOREGROUND_GREEN | FOREGROUND_RED;
	DWORD background;
	if (!FillConsoleOutputAttribute(handle, color, size, coord, &background)) {
		cout << "failed to change the background" << endl;
		exit(1);
	}
	isClicked = true;
}
void ItemList::unclick() {
	DWORD color;
	if (!isChecked) {
		color = BACKGROUND_GREEN | BACKGROUND_RED;
	}
	else {
		color = BACKGROUND_BLUE | BACKGROUND_RED;
	}
	DWORD background;
	if (!FillConsoleOutputAttribute(handle, color, size, coord, &background)) {
		cout << "failed to change the background" << endl;
		exit(1);
	}
	isClicked = false;
}

ItemList::~ItemList() {
}
<commit_msg>Update CheckList.cpp<commit_after>#include "CheckList.h"

CheckList::CheckList(string opts[], int optAmount, short x, short y){
	amount = optAmount;
	coord = { x, y };
	ifClicked = true;
	options = opts;
	handler = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cci = { 100, FALSE };
	SetConsoleCursorInfo(handler, &cci);
	width = 0;
	for (int i = 0; i < amount; i++) {
		if (width < opts[i].length()) width = opts[i].length();
		items.push_back(ItemList(x, y + i, opts[i]));
		items[i].create();
		isChecked.push_back(0);
		isClicked.push_back(0);
	}
	items[0].click();
	isClicked[0] = 1;
}

void CheckList::handleInput(INPUT_RECORD iRecord) {

	if (!ifClicked) return;
	handler = GetStdHandle(STD_OUTPUT_HANDLE);
	switch (iRecord.EventType)
	{
	case KEY_EVENT: // keyboard input
		keyEventProc(iRecord.Event.KeyEvent);
		break;

	case MOUSE_EVENT: // mouse input
		MouseEventProc(iRecord.Event.MouseEvent);
		break;

	default:
		break;
	}
}

void CheckList::keyEventProc(KEY_EVENT_RECORD ker) {
	CONSOLE_SCREEN_BUFFER_INFO ct;
	if (!GetConsoleScreenBufferInfo(handler, &ct))
	{
		cout << "GetConsoleScreenBufferInfo failed" << GetLastError << endl;
		return;
	}
	if (ker.bKeyDown) {
		int clickedItem = whoClicked();
		if (clickedItem < 0) return;
		//ENTER key pressed
		if (ker.wVirtualKeyCode == VK_RETURN) {
			items[clickedItem].check();
		}
		else if (ker.wVirtualKeyCode == VK_TAB || ker.wVirtualKeyCode == VK_DOWN || ker.wVirtualKeyCode == VK_NUMPAD2) {
			if (clickedItem < amount - 1) {
				items[clickedItem].unclick();
				isClicked[clickedItem] = 0;
				items[clickedItem + 1].click();
				isClicked[clickedItem + 1] = 1;
			}
		}
		else if (ker.wVirtualKeyCode == VK_UP || ker.wVirtualKeyCode == VK_NUMPAD8) {
			if (clickedItem > 0) {
				items[clickedItem].unclick();
				isClicked[clickedItem] = 0;
				items[clickedItem - 1].click();
				isClicked[clickedItem - 1] = 1;
			}
		}
	}

}

void CheckList::MouseEventProc(MOUSE_EVENT_RECORD mer) {
#ifndef MOUSE_HWHEELED
#define MOUSE_HWHEELED 0x0008
#endif
	switch (mer.dwEventFlags) {

	case 0:
		//Right button press
		if (mer.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED) {
			if (checkMousePosition(mer.dwMousePosition)) {
				SHORT yPosition = mer.dwMousePosition.Y;
				items[yPosition - coord.Y].check();
			}
		}
		break;
	case MOUSE_MOVED:
		if (checkMousePosition(mer.dwMousePosition)) {
			int clickedItem = whoClicked();
			if (clickedItem < 0) return;
			SHORT yPosition = mer.dwMousePosition.Y;
			items[clickedItem].unclick();
			isClicked[clickedItem] = 0;
			items[yPosition - coord.Y].click();
			isClicked[yPosition - coord.Y] = 1;
		}
		break;
	}
}

boolean CheckList::checkMousePosition(COORD dwMousePosition) {
	if (dwMousePosition.X >= coord.X &&
		dwMousePosition.X <= (coord.X + width) &&
		dwMousePosition.Y >= coord.Y &&
		dwMousePosition.Y <= coord.Y + amount-1) {
		return true;
	}
	else {
		return false;
	}
}

int CheckList::whoClicked() {
	for (int i = 0; i < amount; i++) {
		if (isClicked[i]) return i;
	}
	return -1;
}
vector<int>  CheckList::whoChecked() {
	return isChecked;
}
CheckList::~CheckList(){
}
<|endoftext|>


<commit_before>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

// $Id$
// $MpId: AliMpMotifPosition.cxx,v 1.9 2006/05/24 13:58:41 ivana Exp $
//
// Class AliMpMotifPosition
// ------------------------
// Class that represents a placed motif.
// Included in AliRoot: 2003/05/02
// Authors: David Guez, Ivana Hrivnacova; IPN Orsay

#include "AliMpMotifPosition.h"
#include "AliMpMotifPositionPadIterator.h"
#include "AliMpMotifType.h"

#include <Riostream.h>

/// \cond CLASSIMP
ClassImp(AliMpMotifPosition)
/// \endcond

//______________________________________________________________________________
AliMpMotifPosition::AliMpMotifPosition(Int_t id, AliMpVMotif* motif,
                                       TVector2 position)
  : AliMpVIndexed(),
    fID(id),
    fMotif(motif),
    fPosition(position)
{
/// Standard constructor
}

//______________________________________________________________________________
AliMpMotifPosition::AliMpMotifPosition()
  : AliMpVIndexed(),
    fID(0),
    fMotif(0),
    fPosition(TVector2(0.,0.))
{
/// Default constructor
}

//______________________________________________________________________________
AliMpMotifPosition::~AliMpMotifPosition()\
{
/// Destructor
}

//______________________________________________________________________________
AliMpVPadIterator* AliMpMotifPosition::CreateIterator() const
{
/// Return motif position iterator

  return new AliMpMotifPositionPadIterator(this);
}

//______________________________________________________________________________
Bool_t AliMpMotifPosition::HasPad(const AliMpIntPair& indices) const
{
/// Return true if pad with the specified indices exists in
/// this motif position.

  if (!HasIndices(indices)) return kFALSE;

  if (fMotif->GetMotifType()->IsFull()) return kTRUE;

  return fMotif->GetMotifType()->HasPad(indices-GetLowIndicesLimit());
}

//_____________________________________________________________________________
void
AliMpMotifPosition::SetID(Int_t id)
{
/// Set ID

  fID = id;
}

//_____________________________________________________________________________
void
AliMpMotifPosition::SetPosition(const TVector2& pos)
{
/// Set position

  fPosition = pos;
}

//_____________________________________________________________________________
void
AliMpMotifPosition::Print(Option_t* option) const
{
/// Printing

  cout << "MOTIFPOSITION " << GetID() << " MOTIF "
       << GetMotif()->GetID()
       << " at (" << Position().X() << ","
       << Position().Y() << ") "
       << " iMin=(" << GetLowIndicesLimit().GetFirst()
       << "," << GetLowIndicesLimit().GetSecond()
       << ") iMax=(" << GetHighIndicesLimit().GetFirst()
       << "," << GetHighIndicesLimit().GetSecond()
       << ")" << std::endl;

  if ( option && option[0] == 'M' )
    {
      GetMotif()->Print(option+1);
    }
}
<commit_msg>Remove trailing backslash (Laurent)<commit_after>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

// $Id$
// $MpId: AliMpMotifPosition.cxx,v 1.9 2006/05/24 13:58:41 ivana Exp $
//
// Class AliMpMotifPosition
// ------------------------
// Class that represents a placed motif.
// Included in AliRoot: 2003/05/02
// Authors: David Guez, Ivana Hrivnacova; IPN Orsay

#include "AliMpMotifPosition.h"
#include "AliMpMotifPositionPadIterator.h"
#include "AliMpMotifType.h"

#include <Riostream.h>

/// \cond CLASSIMP
ClassImp(AliMpMotifPosition)
/// \endcond

//______________________________________________________________________________
AliMpMotifPosition::AliMpMotifPosition(Int_t id, AliMpVMotif* motif,
                                       TVector2 position)
  : AliMpVIndexed(),
    fID(id),
    fMotif(motif),
    fPosition(position)
{
/// Standard constructor
}

//______________________________________________________________________________
AliMpMotifPosition::AliMpMotifPosition()
  : AliMpVIndexed(),
    fID(0),
    fMotif(0),
    fPosition(TVector2(0.,0.))
{
/// Default constructor
}

//______________________________________________________________________________
AliMpMotifPosition::~AliMpMotifPosition()
{
/// Destructor
}

//______________________________________________________________________________
AliMpVPadIterator* AliMpMotifPosition::CreateIterator() const
{
/// Return motif position iterator

  return new AliMpMotifPositionPadIterator(this);
}

//______________________________________________________________________________
Bool_t AliMpMotifPosition::HasPad(const AliMpIntPair& indices) const
{
/// Return true if pad with the specified indices exists in
/// this motif position.

  if (!HasIndices(indices)) return kFALSE;

  if (fMotif->GetMotifType()->IsFull()) return kTRUE;

  return fMotif->GetMotifType()->HasPad(indices-GetLowIndicesLimit());
}

//_____________________________________________________________________________
void
AliMpMotifPosition::SetID(Int_t id)
{
/// Set ID

  fID = id;
}

//_____________________________________________________________________________
void
AliMpMotifPosition::SetPosition(const TVector2& pos)
{
/// Set position

  fPosition = pos;
}

//_____________________________________________________________________________
void
AliMpMotifPosition::Print(Option_t* option) const
{
/// Printing

  cout << "MOTIFPOSITION " << GetID() << " MOTIF "
       << GetMotif()->GetID()
       << " at (" << Position().X() << ","
       << Position().Y() << ") "
       << " iMin=(" << GetLowIndicesLimit().GetFirst()
       << "," << GetLowIndicesLimit().GetSecond()
       << ") iMax=(" << GetHighIndicesLimit().GetFirst()
       << "," << GetHighIndicesLimit().GetSecond()
       << ")" << std::endl;

  if ( option && option[0] == 'M' )
    {
      GetMotif()->Print(option+1);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde
 * 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 the <organization> 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 EARLIER MENTIONED AUTHORS ``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 <copyright holder> 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 "CppUTest/TestHarness.h"
#include "CppUTest/TestRegistry.h"
#include "CppUTest/PlatformSpecificFunctions.h"
#include "CppUTest/TestOutput.h"

bool doubles_equal(double d1, double d2, double threshold)
{
	if (PlatformSpecificIsNan(d1) || PlatformSpecificIsNan(d2) || PlatformSpecificIsNan(threshold))
		return false;
	return PlatformSpecificFabs(d1 - d2) <= threshold;
}

/* Sometimes stubs use the CppUTest assertions.
 * Its not correct to do so, but this small helper class will prevent a segmentation fault and instead
 * will give an error message and also the file/line of the check that was executed outside the tests.
 */
class OutsideTestRunnerUTest: public UtestShell
{
public:
	static OutsideTestRunnerUTest& instance()
	{
		static OutsideTestRunnerUTest instance_;
		return instance_;
	}
	virtual TestResult& getTestResult()
	{
		return defaultTestResult;
	}
	virtual void exitCurrentTest()
	{
	}
	virtual ~OutsideTestRunnerUTest()
	{
	}
private:
	OutsideTestRunnerUTest() :
		UtestShell("\n\t NOTE: Assertion happened without being in a test run (perhaps in main?)", "\n\t       Something is very wrong. Check this assertion and fix", "unknown file", 0),
				defaultTestResult(defaultOutput)
	{
	}
	ConsoleTestOutput defaultOutput;
	TestResult defaultTestResult;
};

UtestShell::UtestShell() :
	group_("UndefinedTestGroup"), name_("UndefinedTest"), file_("UndefinedFile"), lineNumber_(0), next_(&NullTestShell::instance()), isRunAsSeperateProcess_(false)
{
}

UtestShell::UtestShell(const char* groupName, const char* testName, const char* fileName, int lineNumber) :
		group_(groupName), name_(testName), file_(fileName), lineNumber_(lineNumber), next_(&NullTestShell::instance()), isRunAsSeperateProcess_(false)
{
}

UtestShell::UtestShell(const char* groupName, const char* testName, const char* fileName, int lineNumber, UtestShell* nextTest) :
	group_(groupName), name_(testName), file_(fileName), lineNumber_(lineNumber), next_(nextTest), isRunAsSeperateProcess_(false)
{
}

UtestShell::~UtestShell()
{
}

static void defaultCrashMethod()
{
	UtestShell* ptr = (UtestShell*) 0x0; ptr->countTests();
}

static void (*pleaseCrashMeRightNow) () = defaultCrashMethod;

void UtestShell::setCrashMethod(void (*crashme)())
{
	pleaseCrashMeRightNow = crashme;
}

void UtestShell::resetCrashMethod()
{
	pleaseCrashMeRightNow = defaultCrashMethod;
}

void UtestShell::crash()
{
	pleaseCrashMeRightNow();
}

void UtestShell::runOneTestWithPlugins(TestPlugin* plugin, TestResult& result)
{
	executePlatformSpecificRunOneTest(this, plugin, result);
}

Utest* UtestShell::createTest()
{
	return new Utest();
}

void UtestShell::destroyTest(Utest* test)
{
	delete test;
}

void UtestShell::runOneTest(TestPlugin* plugin, TestResult& result)
{
	plugin->runAllPreTestAction(*this, result);

	//save test context, so that test class can be tested
	UtestShell* savedTest = UtestShell::getCurrent();
	TestResult* savedResult = UtestShell::getTestResult();

	result.countRun();
	UtestShell::setTestResult(&result);
	UtestShell::setCurrentTest(this);

	Utest* testToRun = createTest();
	testToRun->run();
	destroyTest(testToRun);

	UtestShell::setCurrentTest(savedTest);
	UtestShell::setTestResult(savedResult);

	plugin->runAllPostTestAction(*this, result);
}

void UtestShell::exitCurrentTest()
{
	executePlatformSpecificExitCurrentTest();
}

UtestShell *UtestShell::getNext() const
{
	return next_;
}

UtestShell* UtestShell::addTest(UtestShell *test)
{
	next_ = test;
	return this;
}

int UtestShell::countTests()
{
	return next_->countTests() + 1;
}

bool UtestShell::isNull() const
{
	return false;
}

SimpleString UtestShell::getMacroName() const
{
	return "TEST";
}

const SimpleString UtestShell::getName() const
{
	return SimpleString(name_);
}

const SimpleString UtestShell::getGroup() const
{
	return SimpleString(group_);
}

SimpleString UtestShell::getFormattedName() const
{
	SimpleString formattedName(getMacroName());
	formattedName += "(";
	formattedName += group_;
	formattedName += ", ";
	formattedName += name_;
	formattedName += ")";

	return formattedName;
}

const char* UtestShell::getProgressIndicator() const
{
	return ".";
}

bool UtestShell::isRunInSeperateProcess() const
{
	return isRunAsSeperateProcess_;
}

void UtestShell::setRunInSeperateProcess()
{
	isRunAsSeperateProcess_ = true;
}

void UtestShell::setFileName(const char* fileName)
{
	file_ = fileName;
}

void UtestShell::setLineNumber(int lineNumber)
{
	lineNumber_ = lineNumber;
}

void UtestShell::setGroupName(const char* groupName)
{
	group_ = groupName;
}

void UtestShell::setTestName(const char* testName)
{
	name_ = testName;
}

const SimpleString UtestShell::getFile() const
{
	return SimpleString(file_);
}

int UtestShell::getLineNumber() const
{
	return lineNumber_;
}

bool UtestShell::shouldRun(const TestFilter& groupFilter, const TestFilter& nameFilter) const
{
	if (groupFilter.match(group_) && nameFilter.match(name_)) return true;

	return false;
}

void UtestShell::failWith(const TestFailure& failure)
{
	getTestResult()->addFailure(failure);
    UtestShell::getCurrent()->exitCurrentTest();
}

void UtestShell::assertTrue(bool condition, const char * checkString, const char* conditionString, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (!condition)
		failWith(CheckFailure(this, fileName, lineNumber, checkString, conditionString));
}

void UtestShell::fail(const char *text, const char* fileName, int lineNumber)
{
	failWith(FailFailure(this, fileName, lineNumber, text));
}

void UtestShell::assertCstrEqual(const char* expected, const char* actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (actual == 0 && expected == 0) return;
	if (actual == 0 || expected == 0)
		failWith(StringEqualFailure(this, fileName, lineNumber, expected, actual));
	if (PlatformSpecificStrCmp(expected, actual) != 0)
		failWith(StringEqualFailure(this, fileName, lineNumber, expected, actual));
}

void UtestShell::assertCstrNoCaseEqual(const char* expected, const char* actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (actual == 0 && expected == 0) return;
	if (actual == 0 || expected == 0)
		failWith(StringEqualNoCaseFailure(this, fileName, lineNumber, expected, actual));
	if (!SimpleString(expected).equalsNoCase(actual))
		failWith(StringEqualNoCaseFailure(this, fileName, lineNumber, expected, actual));
}

void UtestShell::assertCstrContains(const char* expected, const char* actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (actual == 0 && expected == 0) return;
    if(actual == 0 || expected == 0)
    	failWith(ContainsFailure(this, fileName, lineNumber, expected, actual));
    if (!SimpleString(actual).contains(expected))
    	failWith(ContainsFailure(this, fileName, lineNumber, expected, actual));
}

void UtestShell::assertCstrNoCaseContains(const char* expected, const char* actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (actual == 0 && expected == 0) return;
    if(actual == 0 || expected == 0)
    	failWith(ContainsFailure(this, fileName, lineNumber, expected, actual));
    if (!SimpleString(actual).containsNoCase(expected))
    	failWith(ContainsFailure(this, fileName, lineNumber, expected, actual));
}

void UtestShell::assertLongsEqual(long expected, long actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (expected != actual) {
		LongsEqualFailure f(this, fileName, lineNumber, expected, actual);
		getTestResult()->addFailure(f);
	    UtestShell::getCurrent()->exitCurrentTest();
	}
}

void UtestShell::assertPointersEqual(const void* expected, const void* actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (expected != actual)
		failWith(EqualsFailure(this, fileName, lineNumber, StringFrom(expected), StringFrom(actual)));
}

void UtestShell::assertDoublesEqual(double expected, double actual, double threshold, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (!doubles_equal(expected, actual, threshold))
		failWith(DoublesEqualFailure(this, fileName, lineNumber, expected, actual, threshold));
}

void UtestShell::print(const char *text, const char* fileName, int lineNumber)
{
	SimpleString stringToPrint = "\n";
	stringToPrint += fileName;
	stringToPrint += ":";
	stringToPrint += StringFrom(lineNumber);
	stringToPrint += " ";
	stringToPrint += text;
	getTestResult()->print(stringToPrint.asCharString());
}

void UtestShell::print(const SimpleString& text, const char* fileName, int lineNumber)
{
	print(text.asCharString(), fileName, lineNumber);
}

TestResult* UtestShell::testResult_ = NULL;
UtestShell* UtestShell::currentTest_ = NULL;

void UtestShell::setTestResult(TestResult* result)
{
	testResult_ = result;
}

void UtestShell::setCurrentTest(UtestShell* test)
{
	currentTest_ = test;
}

TestResult* UtestShell::getTestResult()
{
	if (testResult_ == NULL)
		return &OutsideTestRunnerUTest::instance().getTestResult();
	return testResult_;
}

UtestShell* UtestShell::getCurrent()
{
	if (currentTest_ == NULL)
		return &OutsideTestRunnerUTest::instance();
	return currentTest_;
}

////////////// Utest ////////////

Utest::Utest()
{
}

Utest::~Utest()
{
}

void Utest::run()
{

#if CPPUTEST_USE_STD_CPP_LIB
	try {
#endif
		if (executePlatformSpecificSetup(this)) {
			executePlatformSpecificTestBody(this);
		}
		executePlatformSpecificTeardown(this);

#if CPPUTEST_USE_STD_CPP_LIB
	}
	catch (CppUTestFailedException&)
	{
		try {
			executePlatformSpecificTeardown(this);
		}
		catch (CppUTestFailedException&)
		{
		}
	}
#endif
}

void Utest::setup()
{
}

void Utest::testBody()
{
}

void Utest::teardown()
{
}

////////////// NullTestShell ////////////

NullTestShell::NullTestShell() :
	UtestShell("NullGroup", "NullName", "NullFile", -1, 0)
{
}

NullTestShell::NullTestShell(const char* fileName, int lineNumber) :
	UtestShell("NullGroup", "NullName", fileName, lineNumber, 0)
{
}

NullTestShell::~NullTestShell()
{
}

NullTestShell& NullTestShell::instance()
{
	static NullTestShell _instance;
	return _instance;
}

int NullTestShell::countTests()
{
	return 0;
}

UtestShell* NullTestShell::getNext() const
{
	return &instance();
}

bool NullTestShell::isNull() const
{
	return true;
}

void NullTestShell::testBody()
{
}

//////////////////// ExecFunctionTest

ExecFunctionTest::ExecFunctionTest(ExecFunctionTestShell* shell)
	: shell_(shell)
{
}

void ExecFunctionTest::testBody()
{
	if (shell_->testFunction_) shell_->testFunction_();
}

void ExecFunctionTest::setup()
{
	if (shell_->setup_) shell_->setup_();
}

void ExecFunctionTest::teardown()
{
	if (shell_->teardown_) shell_->teardown_();
}

/////////////// IgnoredUtestShell /////////////
IgnoredUtestShell::IgnoredUtestShell()
{
}

IgnoredUtestShell::~IgnoredUtestShell()
{
}

const char* IgnoredUtestShell::getProgressIndicator() const
{
	return "!";
}

SimpleString IgnoredUtestShell::getMacroName() const
{
	return "IGNORE_TEST";
}

void IgnoredUtestShell::runOneTestWithPlugins(TestPlugin* plugin, TestResult& result)
{
	result.countIgnored();
}

////////////// TestInstaller ////////////

TestInstaller::TestInstaller(UtestShell& shell, const char* groupName, const char* testName, const char* fileName, int lineNumber)
{
	shell.setGroupName(groupName);
	shell.setTestName(testName);
	shell.setFileName(fileName);
	shell.setLineNumber(lineNumber);
	TestRegistry::getCurrentRegistry()->addTest(&shell);
}

TestInstaller::~TestInstaller()
{
}

void TestInstaller::unDo()
{
	TestRegistry::getCurrentRegistry()->unDoLastAddTest();
}
<commit_msg><commit_after>/*
 * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde
 * 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 the <organization> 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 EARLIER MENTIONED AUTHORS ``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 <copyright holder> 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 "CppUTest/TestHarness.h"
#include "CppUTest/TestRegistry.h"
#include "CppUTest/PlatformSpecificFunctions.h"
#include "CppUTest/TestOutput.h"

bool doubles_equal(double d1, double d2, double threshold)
{
	if (PlatformSpecificIsNan(d1) || PlatformSpecificIsNan(d2) || PlatformSpecificIsNan(threshold))
		return false;
	return PlatformSpecificFabs(d1 - d2) <= threshold;
}

/* Sometimes stubs use the CppUTest assertions.
 * Its not correct to do so, but this small helper class will prevent a segmentation fault and instead
 * will give an error message and also the file/line of the check that was executed outside the tests.
 */
class OutsideTestRunnerUTest: public UtestShell
{
public:
	static OutsideTestRunnerUTest& instance()
	{
		static OutsideTestRunnerUTest instance_;
		return instance_;
	}
	virtual TestResult& getTestResult()
	{
		return defaultTestResult;
	}
	virtual void exitCurrentTest()
	{
	}
	virtual ~OutsideTestRunnerUTest()
	{
	}
private:
	OutsideTestRunnerUTest() :
		UtestShell("\n\t NOTE: Assertion happened without being in a test run (perhaps in main?)", "\n\t       Something is very wrong. Check this assertion and fix", "unknown file", 0),
				defaultTestResult(defaultOutput)
	{
	}
	ConsoleTestOutput defaultOutput;
	TestResult defaultTestResult;
};

UtestShell::UtestShell() :
	group_("UndefinedTestGroup"), name_("UndefinedTest"), file_("UndefinedFile"), lineNumber_(0), next_(&NullTestShell::instance()), isRunAsSeperateProcess_(false)
{
}

UtestShell::UtestShell(const char* groupName, const char* testName, const char* fileName, int lineNumber) :
		group_(groupName), name_(testName), file_(fileName), lineNumber_(lineNumber), next_(&NullTestShell::instance()), isRunAsSeperateProcess_(false)
{
}

UtestShell::UtestShell(const char* groupName, const char* testName, const char* fileName, int lineNumber, UtestShell* nextTest) :
	group_(groupName), name_(testName), file_(fileName), lineNumber_(lineNumber), next_(nextTest), isRunAsSeperateProcess_(false)
{
}

UtestShell::~UtestShell()
{
}

static void defaultCrashMethod()
{
	UtestShell* ptr = (UtestShell*) 0x0; ptr->countTests();
}

static void (*pleaseCrashMeRightNow) () = defaultCrashMethod;

void UtestShell::setCrashMethod(void (*crashme)())
{
	pleaseCrashMeRightNow = crashme;
}

void UtestShell::resetCrashMethod()
{
	pleaseCrashMeRightNow = defaultCrashMethod;
}

void UtestShell::crash()
{
	pleaseCrashMeRightNow();
}

void UtestShell::runOneTestWithPlugins(TestPlugin* plugin, TestResult& result)
{
	executePlatformSpecificRunOneTest(this, plugin, result);
}

Utest* UtestShell::createTest()
{
	return new Utest();
}

void UtestShell::destroyTest(Utest* test)
{
	delete test;
}

void UtestShell::runOneTest(TestPlugin* plugin, TestResult& result)
{
	plugin->runAllPreTestAction(*this, result);

	//save test context, so that test class can be tested
	UtestShell* savedTest = UtestShell::getCurrent();
	TestResult* savedResult = UtestShell::getTestResult();

	result.countRun();
	UtestShell::setTestResult(&result);
	UtestShell::setCurrentTest(this);

	Utest* testToRun = createTest();
	testToRun->run();
	destroyTest(testToRun);

	UtestShell::setCurrentTest(savedTest);
	UtestShell::setTestResult(savedResult);

	plugin->runAllPostTestAction(*this, result);
}

void UtestShell::exitCurrentTest()
{
	executePlatformSpecificExitCurrentTest();
}

UtestShell *UtestShell::getNext() const
{
	return next_;
}

UtestShell* UtestShell::addTest(UtestShell *test)
{
	next_ = test;
	return this;
}

int UtestShell::countTests()
{
	return next_->countTests() + 1;
}

bool UtestShell::isNull() const
{
	return false;
}

SimpleString UtestShell::getMacroName() const
{
	return "TEST";
}

const SimpleString UtestShell::getName() const
{
	return SimpleString(name_);
}

const SimpleString UtestShell::getGroup() const
{
	return SimpleString(group_);
}

SimpleString UtestShell::getFormattedName() const
{
	SimpleString formattedName(getMacroName());
	formattedName += "(";
	formattedName += group_;
	formattedName += ", ";
	formattedName += name_;
	formattedName += ")";

	return formattedName;
}

const char* UtestShell::getProgressIndicator() const
{
	return ".";
}

bool UtestShell::isRunInSeperateProcess() const
{
	return isRunAsSeperateProcess_;
}

void UtestShell::setRunInSeperateProcess()
{
	isRunAsSeperateProcess_ = true;
}

void UtestShell::setFileName(const char* fileName)
{
	file_ = fileName;
}

void UtestShell::setLineNumber(int lineNumber)
{
	lineNumber_ = lineNumber;
}

void UtestShell::setGroupName(const char* groupName)
{
	group_ = groupName;
}

void UtestShell::setTestName(const char* testName)
{
	name_ = testName;
}

const SimpleString UtestShell::getFile() const
{
	return SimpleString(file_);
}

int UtestShell::getLineNumber() const
{
	return lineNumber_;
}

bool UtestShell::shouldRun(const TestFilter& groupFilter, const TestFilter& nameFilter) const
{
	if (groupFilter.match(group_) && nameFilter.match(name_)) return true;

	return false;
}

void UtestShell::failWith(const TestFailure& failure)
{
	getTestResult()->addFailure(failure);
    UtestShell::getCurrent()->exitCurrentTest();
}

void UtestShell::assertTrue(bool condition, const char * checkString, const char* conditionString, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (!condition)
		failWith(CheckFailure(this, fileName, lineNumber, checkString, conditionString));
}

void UtestShell::fail(const char *text, const char* fileName, int lineNumber)
{
	failWith(FailFailure(this, fileName, lineNumber, text));
}

void UtestShell::assertCstrEqual(const char* expected, const char* actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (actual == 0 && expected == 0) return;
	if (actual == 0 || expected == 0)
		failWith(StringEqualFailure(this, fileName, lineNumber, expected, actual));
	if (PlatformSpecificStrCmp(expected, actual) != 0)
		failWith(StringEqualFailure(this, fileName, lineNumber, expected, actual));
}

void UtestShell::assertCstrNoCaseEqual(const char* expected, const char* actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (actual == 0 && expected == 0) return;
	if (actual == 0 || expected == 0)
		failWith(StringEqualNoCaseFailure(this, fileName, lineNumber, expected, actual));
	if (!SimpleString(expected).equalsNoCase(actual))
		failWith(StringEqualNoCaseFailure(this, fileName, lineNumber, expected, actual));
}

void UtestShell::assertCstrContains(const char* expected, const char* actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (actual == 0 && expected == 0) return;
    if(actual == 0 || expected == 0)
    	failWith(ContainsFailure(this, fileName, lineNumber, expected, actual));
    if (!SimpleString(actual).contains(expected))
    	failWith(ContainsFailure(this, fileName, lineNumber, expected, actual));
}

void UtestShell::assertCstrNoCaseContains(const char* expected, const char* actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (actual == 0 && expected == 0) return;
    if(actual == 0 || expected == 0)
    	failWith(ContainsFailure(this, fileName, lineNumber, expected, actual));
    if (!SimpleString(actual).containsNoCase(expected))
    	failWith(ContainsFailure(this, fileName, lineNumber, expected, actual));
}

void UtestShell::assertLongsEqual(long expected, long actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (expected != actual) {
		LongsEqualFailure f(this, fileName, lineNumber, expected, actual);
		getTestResult()->addFailure(f);
	    UtestShell::getCurrent()->exitCurrentTest();
	}
}

void UtestShell::assertPointersEqual(const void* expected, const void* actual, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (expected != actual)
		failWith(EqualsFailure(this, fileName, lineNumber, StringFrom(expected), StringFrom(actual)));
}

void UtestShell::assertDoublesEqual(double expected, double actual, double threshold, const char* fileName, int lineNumber)
{
	getTestResult()->countCheck();
	if (!doubles_equal(expected, actual, threshold))
		failWith(DoublesEqualFailure(this, fileName, lineNumber, expected, actual, threshold));
}

void UtestShell::print(const char *text, const char* fileName, int lineNumber)
{
	SimpleString stringToPrint = "\n";
	stringToPrint += fileName;
	stringToPrint += ":";
	stringToPrint += StringFrom(lineNumber);
	stringToPrint += " ";
	stringToPrint += text;
	getTestResult()->print(stringToPrint.asCharString());
}

void UtestShell::print(const SimpleString& text, const char* fileName, int lineNumber)
{
	print(text.asCharString(), fileName, lineNumber);
}

TestResult* UtestShell::testResult_ = NULL;
UtestShell* UtestShell::currentTest_ = NULL;

void UtestShell::setTestResult(TestResult* result)
{
	testResult_ = result;
}

void UtestShell::setCurrentTest(UtestShell* test)
{
	currentTest_ = test;
}

TestResult* UtestShell::getTestResult()
{
	if (testResult_ == NULL)
		return &OutsideTestRunnerUTest::instance().getTestResult();
	return testResult_;
}

UtestShell* UtestShell::getCurrent()
{
	if (currentTest_ == NULL)
		return &OutsideTestRunnerUTest::instance();
	return currentTest_;
}

////////////// Utest ////////////

Utest::Utest()
{
}

Utest::~Utest()
{
}

void Utest::run()
{

#if CPPUTEST_USE_STD_CPP_LIB
	try {
#endif
		if (executePlatformSpecificSetup(this)) {
			executePlatformSpecificTestBody(this);
		}
		executePlatformSpecificTeardown(this);

#if CPPUTEST_USE_STD_CPP_LIB
	}
	catch (CppUTestFailedException&)
	{
		try {
			executePlatformSpecificTeardown(this);
		}
		catch (CppUTestFailedException&)
		{
		}
	}
#endif
}

void Utest::setup()
{
}

void Utest::testBody()
{
}

void Utest::teardown()
{
}

////////////// NullTestShell ////////////

NullTestShell::NullTestShell() :
	UtestShell("NullGroup", "NullName", "NullFile", -1, 0)
{
}

NullTestShell::NullTestShell(const char* fileName, int lineNumber) :
	UtestShell("NullGroup", "NullName", fileName, lineNumber, 0)
{
}

NullTestShell::~NullTestShell()
{
}

NullTestShell& NullTestShell::instance()
{
	static NullTestShell _instance;
	return _instance;
}

int NullTestShell::countTests()
{
	return 0;
}

UtestShell* NullTestShell::getNext() const
{
	return &instance();
}

bool NullTestShell::isNull() const
{
	return true;
}

void NullTestShell::testBody()
{
}

//////////////////// ExecFunctionTest

ExecFunctionTest::ExecFunctionTest(ExecFunctionTestShell* shell)
	: shell_(shell)
{
}

void ExecFunctionTest::testBody()
{
	if (shell_->testFunction_) shell_->testFunction_();
}

void ExecFunctionTest::setup()
{
	if (shell_->setup_) shell_->setup_();
}

void ExecFunctionTest::teardown()
{
	if (shell_->teardown_) shell_->teardown_();
}

/////////////// IgnoredUtestShell /////////////
IgnoredUtestShell::IgnoredUtestShell()
{
}

IgnoredUtestShell::~IgnoredUtestShell()
{
}

const char* IgnoredUtestShell::getProgressIndicator() const
{
	return "!";
}

SimpleString IgnoredUtestShell::getMacroName() const
{
	return "IGNORE_TEST";
}

void IgnoredUtestShell::runOneTestWithPlugins(TestPlugin* /* plugin */, TestResult& result)
{
	result.countIgnored();
}

////////////// TestInstaller ////////////

TestInstaller::TestInstaller(UtestShell& shell, const char* groupName, const char* testName, const char* fileName, int lineNumber)
{
	shell.setGroupName(groupName);
	shell.setTestName(testName);
	shell.setFileName(fileName);
	shell.setLineNumber(lineNumber);
	TestRegistry::getCurrentRegistry()->addTest(&shell);
}

TestInstaller::~TestInstaller()
{
}

void TestInstaller::unDo()
{
	TestRegistry::getCurrentRegistry()->unDoLastAddTest();
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "slave/containerizer/mesos/isolators/volume/secret.hpp"

#include <string>
#include <vector>

#include <mesos/secret/resolver.hpp>

#include <process/collect.hpp>
#include <process/future.hpp>
#include <process/id.hpp>
#include <process/owned.hpp>

#include <stout/foreach.hpp>
#include <stout/stringify.hpp>
#include <stout/strings.hpp>

#include <stout/os/mkdir.hpp>
#include <stout/os/touch.hpp>
#include <stout/os/write.hpp>

#ifdef __linux__
#include "linux/ns.hpp"
#endif // __linux__

#include "common/validation.hpp"

using std::string;
using std::vector;

using process::Failure;
using process::Future;
using process::Owned;

using mesos::slave::ContainerClass;
using mesos::slave::ContainerConfig;
using mesos::slave::ContainerLaunchInfo;
using mesos::slave::ContainerState;
using mesos::slave::Isolator;

namespace mesos {
namespace internal {
namespace slave {

constexpr char SECRET_DIR[] = ".secret";

Try<Isolator*> VolumeSecretIsolatorProcess::create(
    const Flags& flags,
    SecretResolver* secretResolver)
{
  if (flags.launcher != "linux" ||
      !strings::contains(flags.isolation, "filesystem/linux")) {
    return Error("Volume secret isolation requires filesystem/linux isolator.");
  }

  const string hostSecretTmpDir = path::join(flags.runtime_dir, SECRET_DIR);

  Try<Nothing> mkdir = os::mkdir(hostSecretTmpDir);
  if (mkdir.isError()) {
    return Error("Failed to create secret directory on the host tmpfs:" +
                 mkdir.error());
  }

  Owned<MesosIsolatorProcess> process(new VolumeSecretIsolatorProcess(
      flags,
      secretResolver));

  return new MesosIsolator(process);
}

VolumeSecretIsolatorProcess::VolumeSecretIsolatorProcess(
    const Flags& _flags,
    SecretResolver* secretResolver)
  : ProcessBase(process::ID::generate("volume-secret-isolator")),
    flags(_flags),
    secretResolver(secretResolver) {}

bool VolumeSecretIsolatorProcess::supportsNesting()
{
  return true;
}

Future<Option<ContainerLaunchInfo>> VolumeSecretIsolatorProcess::prepare(
    const ContainerID& containerId,
    const ContainerConfig& containerConfig)
{
  if (!containerConfig.has_container_info()) {
    return None();
  }

  const ContainerInfo& containerInfo = containerConfig.container_info();

  if (containerInfo.type() != ContainerInfo::MESOS) {
    return Failure(
        "Can only prepare the secret volume isolator for a MESOS container");
  }

  if (containerConfig.has_container_class() &&
      containerConfig.container_class() == ContainerClass::DEBUG) {
    return None();
  }

  ContainerLaunchInfo launchInfo;
  launchInfo.add_clone_namespaces(CLONE_NEWNS);

  const string sandboxSecretRootDir =
    path::join(containerConfig.directory(),
               SECRET_DIR + string("-") + stringify(id::UUID::random()));

  // TODO(Kapil): Add some UUID suffix to the secret-root dir to avoid conflicts
  // with user container_path.
  Try<Nothing> mkdir = os::mkdir(sandboxSecretRootDir);
  if (mkdir.isError()) {
    return Failure("Failed to create sandbox secret root directory at '" +
                   sandboxSecretRootDir + "': " + mkdir.error());
  }

  // Mount ramfs in the container.
  CommandInfo* command = launchInfo.add_pre_exec_commands();
  command->set_shell(false);
  command->set_value("mount");
  command->add_arguments("mount");
  command->add_arguments("-n");
  command->add_arguments("-t");
  command->add_arguments("ramfs");
  command->add_arguments("ramfs");
  command->add_arguments(sandboxSecretRootDir);

  vector<Future<Nothing>> futures;
  foreach (const Volume& volume, containerInfo.volumes()) {
    if (!volume.has_source() ||
        !volume.source().has_type() ||
        volume.source().type() != Volume::Source::SECRET) {
      continue;
    }

    if (!volume.source().has_secret()) {
      return Failure("volume.source.secret is not specified");
    }

    if (secretResolver == nullptr) {
      return Failure(
          "Error: Volume has secret but no secret-resolver provided");
    }

    const Secret& secret = volume.source().secret();

    Option<Error> error = common::validation::validateSecret(secret);
    if (error.isSome()) {
      return Failure("Invalid secret specified in volume: " + error->message);
    }

    string targetContainerPath;
    if (path::absolute(volume.container_path())) {
      if (containerConfig.has_rootfs()) {
        targetContainerPath = path::join(
            containerConfig.rootfs(),
            volume.container_path());

        Try<Nothing> mkdir = os::mkdir(Path(targetContainerPath).dirname());
        if (mkdir.isError()) {
          return Failure(
              "Failed to create directory '" +
              Path(targetContainerPath).dirname() + "' "
              "for the target mount file: " + mkdir.error());
        }

        Try<Nothing> touch = os::touch(targetContainerPath);
        if (touch.isError()) {
          return Failure(
              "Failed to create the target mount file at '" +
              targetContainerPath + "': " + touch.error());
        }
      } else {
        targetContainerPath = volume.container_path();

        if (!os::exists(targetContainerPath)) {
          return Failure(
              "Absolute container path '" + targetContainerPath + "' "
              "does not exist");
        }
      }
    } else {
      if (containerConfig.has_rootfs()) {
        targetContainerPath = path::join(
            containerConfig.rootfs(),
            flags.sandbox_directory,
            volume.container_path());
      } else {
        targetContainerPath = path::join(
            containerConfig.directory(),
            volume.container_path());
      }

      // Create the mount point if bind mount is used.
      // NOTE: We cannot create the mount point at 'targetContainerPath' if
      // container has rootfs defined. The bind mount of the sandbox
      // will hide what's inside 'targetContainerPath'. So we should always
      // create the mount point in the sandbox.
      const string mountPoint = path::join(
          containerConfig.directory(),
          volume.container_path());

      Try<Nothing> mkdir = os::mkdir(Path(mountPoint).dirname());
      if (mkdir.isError()) {
        return Failure(
            "Failed to create the target mount file directory at '" +
            Path(mountPoint).dirname() + "': " + mkdir.error());
      }

      Try<Nothing> touch = os::touch(mountPoint);
      if (touch.isError()) {
        return Failure(
            "Failed to create the target mount file at '" +
            targetContainerPath + "': " + touch.error());
      }
    }

    const string hostSecretPath =
      path::join(flags.runtime_dir, SECRET_DIR, stringify(id::UUID::random()));

    const string sandboxSecretPath =
      path::join(sandboxSecretRootDir,
                 volume.container_path());

    Try<Nothing> mkdir = os::mkdir(Path(sandboxSecretPath).dirname());
    if (mkdir.isError()) {
      return Failure(
          "Failed to create the target mount file directory at '" +
          Path(sandboxSecretPath).dirname() + "': " + mkdir.error());
    }

    // Create directory tree inside sandbox secret root dir.
    command = launchInfo.add_pre_exec_commands();
    command->set_shell(false);
    command->set_value("mkdir");
    command->add_arguments("mkdir");
    command->add_arguments("-p");
    command->add_arguments(Path(sandboxSecretPath).dirname());

    // Move secret from hostSecretPath to sandboxSecretPath.
    command = launchInfo.add_pre_exec_commands();
    command->set_shell(false);
    command->set_value("mv");
    command->add_arguments("mv");
    command->add_arguments("-f");
    command->add_arguments(hostSecretPath);
    command->add_arguments(sandboxSecretPath);

    // Bind mount sandboxSecretPath to targetContainerPath
    command = launchInfo.add_pre_exec_commands();
    command->set_shell(false);
    command->set_value("mount");
    command->add_arguments("mount");
    command->add_arguments("-n");
    command->add_arguments("--rbind");
    command->add_arguments(sandboxSecretPath);
    command->add_arguments(targetContainerPath);

    Future<Nothing> future = secretResolver->resolve(secret)
      .then([hostSecretPath](const Secret::Value& value) -> Future<Nothing> {
        Try<Nothing> writeSecret = os::write(hostSecretPath, value.data());
        if (writeSecret.isError()) {
          return Failure(
              "Error writing secret to '" + hostSecretPath + "': " +
              writeSecret.error());
        }
        return Nothing();
      });

    futures.push_back(future);
  }

  return collect(futures)
    .then([launchInfo, containerId](
        const vector<Nothing>& results) -> Future<Option<ContainerLaunchInfo>> {
      LOG(INFO) << results.size() << " secrets have been resolved for "
                << "container " << containerId;
      return launchInfo;
    });
}

} // namespace slave {
} // namespace internal {
} // namespace mesos {
<commit_msg>Updated `volume/secret` isolator to honor volume mode.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "slave/containerizer/mesos/isolators/volume/secret.hpp"

#include <string>
#include <vector>

#include <mesos/secret/resolver.hpp>

#include <process/collect.hpp>
#include <process/future.hpp>
#include <process/id.hpp>
#include <process/owned.hpp>

#include <stout/foreach.hpp>
#include <stout/stringify.hpp>
#include <stout/strings.hpp>

#include <stout/os/mkdir.hpp>
#include <stout/os/touch.hpp>
#include <stout/os/write.hpp>

#ifdef __linux__
#include "linux/ns.hpp"
#endif // __linux__

#include "common/validation.hpp"

using std::string;
using std::vector;

using process::Failure;
using process::Future;
using process::Owned;

using mesos::slave::ContainerClass;
using mesos::slave::ContainerConfig;
using mesos::slave::ContainerLaunchInfo;
using mesos::slave::ContainerState;
using mesos::slave::Isolator;

namespace mesos {
namespace internal {
namespace slave {

constexpr char SECRET_DIR[] = ".secret";

Try<Isolator*> VolumeSecretIsolatorProcess::create(
    const Flags& flags,
    SecretResolver* secretResolver)
{
  if (flags.launcher != "linux" ||
      !strings::contains(flags.isolation, "filesystem/linux")) {
    return Error("Volume secret isolation requires filesystem/linux isolator.");
  }

  const string hostSecretTmpDir = path::join(flags.runtime_dir, SECRET_DIR);

  Try<Nothing> mkdir = os::mkdir(hostSecretTmpDir);
  if (mkdir.isError()) {
    return Error("Failed to create secret directory on the host tmpfs:" +
                 mkdir.error());
  }

  Owned<MesosIsolatorProcess> process(new VolumeSecretIsolatorProcess(
      flags,
      secretResolver));

  return new MesosIsolator(process);
}

VolumeSecretIsolatorProcess::VolumeSecretIsolatorProcess(
    const Flags& _flags,
    SecretResolver* secretResolver)
  : ProcessBase(process::ID::generate("volume-secret-isolator")),
    flags(_flags),
    secretResolver(secretResolver) {}

bool VolumeSecretIsolatorProcess::supportsNesting()
{
  return true;
}

Future<Option<ContainerLaunchInfo>> VolumeSecretIsolatorProcess::prepare(
    const ContainerID& containerId,
    const ContainerConfig& containerConfig)
{
  if (!containerConfig.has_container_info()) {
    return None();
  }

  const ContainerInfo& containerInfo = containerConfig.container_info();

  if (containerInfo.type() != ContainerInfo::MESOS) {
    return Failure(
        "Can only prepare the secret volume isolator for a MESOS container");
  }

  if (containerConfig.has_container_class() &&
      containerConfig.container_class() == ContainerClass::DEBUG) {
    return None();
  }

  ContainerLaunchInfo launchInfo;
  launchInfo.add_clone_namespaces(CLONE_NEWNS);

  const string sandboxSecretRootDir =
    path::join(containerConfig.directory(),
               SECRET_DIR + string("-") + stringify(id::UUID::random()));

  // TODO(Kapil): Add some UUID suffix to the secret-root dir to avoid conflicts
  // with user container_path.
  Try<Nothing> mkdir = os::mkdir(sandboxSecretRootDir);
  if (mkdir.isError()) {
    return Failure("Failed to create sandbox secret root directory at '" +
                   sandboxSecretRootDir + "': " + mkdir.error());
  }

  // Mount ramfs in the container.
  CommandInfo* command = launchInfo.add_pre_exec_commands();
  command->set_shell(false);
  command->set_value("mount");
  command->add_arguments("mount");
  command->add_arguments("-n");
  command->add_arguments("-t");
  command->add_arguments("ramfs");
  command->add_arguments("ramfs");
  command->add_arguments(sandboxSecretRootDir);

  vector<Future<Nothing>> futures;
  foreach (const Volume& volume, containerInfo.volumes()) {
    if (!volume.has_source() ||
        !volume.source().has_type() ||
        volume.source().type() != Volume::Source::SECRET) {
      continue;
    }

    if (!volume.source().has_secret()) {
      return Failure("volume.source.secret is not specified");
    }

    if (secretResolver == nullptr) {
      return Failure(
          "Error: Volume has secret but no secret-resolver provided");
    }

    const Secret& secret = volume.source().secret();

    Option<Error> error = common::validation::validateSecret(secret);
    if (error.isSome()) {
      return Failure("Invalid secret specified in volume: " + error->message);
    }

    string targetContainerPath;
    if (path::absolute(volume.container_path())) {
      if (containerConfig.has_rootfs()) {
        targetContainerPath = path::join(
            containerConfig.rootfs(),
            volume.container_path());

        Try<Nothing> mkdir = os::mkdir(Path(targetContainerPath).dirname());
        if (mkdir.isError()) {
          return Failure(
              "Failed to create directory '" +
              Path(targetContainerPath).dirname() + "' "
              "for the target mount file: " + mkdir.error());
        }

        Try<Nothing> touch = os::touch(targetContainerPath);
        if (touch.isError()) {
          return Failure(
              "Failed to create the target mount file at '" +
              targetContainerPath + "': " + touch.error());
        }
      } else {
        targetContainerPath = volume.container_path();

        if (!os::exists(targetContainerPath)) {
          return Failure(
              "Absolute container path '" + targetContainerPath + "' "
              "does not exist");
        }
      }
    } else {
      if (containerConfig.has_rootfs()) {
        targetContainerPath = path::join(
            containerConfig.rootfs(),
            flags.sandbox_directory,
            volume.container_path());
      } else {
        targetContainerPath = path::join(
            containerConfig.directory(),
            volume.container_path());
      }

      // Create the mount point if bind mount is used.
      // NOTE: We cannot create the mount point at 'targetContainerPath' if
      // container has rootfs defined. The bind mount of the sandbox
      // will hide what's inside 'targetContainerPath'. So we should always
      // create the mount point in the sandbox.
      const string mountPoint = path::join(
          containerConfig.directory(),
          volume.container_path());

      Try<Nothing> mkdir = os::mkdir(Path(mountPoint).dirname());
      if (mkdir.isError()) {
        return Failure(
            "Failed to create the target mount file directory at '" +
            Path(mountPoint).dirname() + "': " + mkdir.error());
      }

      Try<Nothing> touch = os::touch(mountPoint);
      if (touch.isError()) {
        return Failure(
            "Failed to create the target mount file at '" +
            targetContainerPath + "': " + touch.error());
      }
    }

    const string hostSecretPath =
      path::join(flags.runtime_dir, SECRET_DIR, stringify(id::UUID::random()));

    const string sandboxSecretPath =
      path::join(sandboxSecretRootDir,
                 volume.container_path());

    Try<Nothing> mkdir = os::mkdir(Path(sandboxSecretPath).dirname());
    if (mkdir.isError()) {
      return Failure(
          "Failed to create the target mount file directory at '" +
          Path(sandboxSecretPath).dirname() + "': " + mkdir.error());
    }

    // Create directory tree inside sandbox secret root dir.
    command = launchInfo.add_pre_exec_commands();
    command->set_shell(false);
    command->set_value("mkdir");
    command->add_arguments("mkdir");
    command->add_arguments("-p");
    command->add_arguments(Path(sandboxSecretPath).dirname());

    // Move secret from hostSecretPath to sandboxSecretPath.
    command = launchInfo.add_pre_exec_commands();
    command->set_shell(false);
    command->set_value("mv");
    command->add_arguments("mv");
    command->add_arguments("-f");
    command->add_arguments(hostSecretPath);
    command->add_arguments(sandboxSecretPath);

    // Bind mount sandboxSecretPath to targetContainerPath
    command = launchInfo.add_pre_exec_commands();
    command->set_shell(false);
    command->set_value("mount");
    command->add_arguments("mount");
    command->add_arguments("-n");
    command->add_arguments("--rbind");
    command->add_arguments(sandboxSecretPath);
    command->add_arguments(targetContainerPath);

    // If the mount needs to be read-only, do a remount.
    if (volume.mode() == Volume::RO) {
      command = launchInfo.add_pre_exec_commands();
      command->set_shell(false);
      command->set_value("mount");
      command->add_arguments("mount");
      command->add_arguments("-n");
      command->add_arguments("-o");
      command->add_arguments("bind,ro,remount");
      command->add_arguments(sandboxSecretPath);
      command->add_arguments(targetContainerPath);
    }

    Future<Nothing> future = secretResolver->resolve(secret)
      .then([hostSecretPath](const Secret::Value& value) -> Future<Nothing> {
        Try<Nothing> writeSecret = os::write(hostSecretPath, value.data());
        if (writeSecret.isError()) {
          return Failure(
              "Error writing secret to '" + hostSecretPath + "': " +
              writeSecret.error());
        }
        return Nothing();
      });

    futures.push_back(future);
  }

  return collect(futures)
    .then([launchInfo, containerId](
        const vector<Nothing>& results) -> Future<Option<ContainerLaunchInfo>> {
      LOG(INFO) << results.size() << " secrets have been resolved for "
                << "container " << containerId;
      return launchInfo;
    });
}

} // namespace slave {
} // namespace internal {
} // namespace mesos {
<|endoftext|>