<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2014, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed 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 "condor_common.h"
#include "condor_classad.h"
#include "condor_config.h"
#include "docker_proc.h"
#include "starter.h"
#include "docker-api.h"

// TODO: move this to condor_attributes.h
#define ATTR_DOCKER_IMAGE_ID "DockerImageId"

extern CStarter *Starter;

	//
	// TODO: If the starter is running as root (maybe docker should be suid?)
	// we can't allow the normal ssh-to-job code to run.  We'd also probably
	// like ssh-to-job to connect to the docker/internal stuff anyway...
	//

	//
	// TODO: Allow the use of HTCondor file-transfer to provide the image.
	// May require understanding "self-hosting".
	//

	//
	// TODO: Leverage the procd to gather usage information; Docker uses
	// the full container ID as (part of) the cgroup identifier(s).
	//

	//
	// TODO: Can we configure Docker to store its overlay filesystem in
	// the sandbox?  Would that be useful for cleanup, or to help admins
	// allocate disk space?
	//

	//
	// TODO: Write the container ID out to disk (<sandbox>../../.dotfile)
	// like the VM universe so that the startd can clean up if the starter
	// crashes and again, if necessary, on start-up.
	//

DockerProc::DockerProc( ClassAd * jobAd ) : VanillaProc( jobAd ) { }

DockerProc::~DockerProc() { }

int DockerProc::StartJob() {
	std::string imageID;
	if( ! JobAd->LookupString( ATTR_DOCKER_IMAGE_ID, imageID ) ) {
		dprintf( D_ALWAYS | D_FAILURE, "%s not defined in job ad, unable to start job.\n", ATTR_DOCKER_IMAGE_ID );
		return FALSE;
	}

	std::string command;
	JobAd->LookupString( ATTR_JOB_CMD, command );
	dprintf( D_FULLDEBUG, "%s: '%s'\n", ATTR_JOB_CMD, command.c_str() );

	ArgList args;
	args.SetArgV1SyntaxToCurrentPlatform();
	MyString argsError;
	if( ! args.AppendArgsFromClassAd( JobAd, & argsError ) ) {
		dprintf( D_ALWAYS | D_FAILURE, "Failed to read job arguments from job ad: '%s'.\n", argsError.c_str() );
		return FALSE;
	}

	// We can't just use Starter->GetJobEnv() because we need to change e.g.,
	// _CONDOR_SCRATCH_DIR to be sensible for the chroot jail.
	// TODO: pass requested job environment to Docker.
	Env e;

	std::string sandboxPath = Starter->jic->jobRemoteIWD();

	// The GlobalJobID is unsuitable by virtue its octothorpes.  This
	// construction is informative, but could be made even less likely
	// to collide if it had a timestamp.
	std::string dockerName;
	formatstr( dockerName, "%s_cluster%d_proc%d_starterPID%d",
		Starter->getMySlotName().c_str(),
		Starter->jic->jobCluster(),
		Starter->jic->jobProc(),
		getpid() );

	CondorError err;
	// DockerAPI::run() returns a PID from daemonCore->Create_Process(), which
	// makes it suitable for passing up into VanillaProc.  This combination
	// will trigger the reaper(s) when the container terminates.
	int rv = DockerAPI::run( dockerName, imageID, command, args, e, sandboxPath, containerID, JobPid, err );
	if( rv < 0 ) {
		dprintf( D_ALWAYS | D_FAILURE, "DockerAPI::run( %s, %s, ... ) failed with return value %d\n", imageID.c_str(), command.c_str(), rv );
		return FALSE;
	}
	dprintf( D_FULLDEBUG, "DockerAPI::run() returned container ID '%s' and pid %d\n", containerID.c_str(), JobPid );

	return TRUE;
}

// TODO: Implement.
bool DockerProc::JobReaper( int pid, int status ) {
	dprintf( D_ALWAYS, "DockerProc::JobReaper()\n" );
	return VanillaProc::JobReaper( pid, status );
}

// TODO: Implement.
bool DockerProc::JobExit() {
	dprintf( D_ALWAYS, "DockerProc::JobExit()\n" );
	return VanillaProc::JobExit();
}

// TODO: Implement.
void DockerProc::Suspend() {
	dprintf( D_ALWAYS, "DockerProc::Suspend()\n" );
	VanillaProc::Suspend();
}

// TODO: Implement.
void DockerProc::Continue() {
	dprintf( D_ALWAYS, "DockerProc::Continue()\n" );
	VanillaProc::Continue();
}

// TODO: Implement.
bool DockerProc::Remove() {
	dprintf( D_ALWAYS, "DockerProc::Remove()\n" );
	return VanillaProc::Remove();
}

// TODO: Implement.
bool DockerProc::Hold() {
	dprintf( D_ALWAYS, "DockerProc::Hold()\n" );
	return VanillaProc::Hold();
}

// TODO: Implement.
bool DockerProc::ShutdownGraceful() {
	dprintf( D_ALWAYS, "DockerProc::ShutdownGraceful()\n" );
	return VanillaProc::ShutdownGraceful();
}

// TODO: Implement.
bool DockerProc::ShutdownFast() {
	dprintf( D_ALWAYS, "DockerProc::ShutdownFast()\n" );
	return VanillaProc::ShutdownFast();
}

// TODO: Implement.  Do NOT call VanillaProc::PublishUpdateAd(), because it
// will report on the wrong process (the one we're using to generate a job-
// termination event).
bool DockerProc::PublishUpdateAd( ClassAd * jobAd ) {
	dprintf( D_ALWAYS, "DockerProc::PublishUpdateAd()\n" );
	return true;
}

// TODO: Implement.
void DockerProc::PublishToEnv( Env * env ) {
	dprintf( D_ALWAYS, "DockerProc::PublishToEnv()\n" );
	return;
}
<commit_msg>Sketch out a large number of functions.<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2014, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed 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 "condor_common.h"
#include "condor_classad.h"
#include "condor_config.h"
#include "docker_proc.h"
#include "starter.h"
#include "docker-api.h"

// TODO: move this to condor_attributes.h
#define ATTR_DOCKER_IMAGE_ID "DockerImageId"

extern CStarter *Starter;

//
// TODO: If the starter is running as root (maybe docker should be suid?)
// we can't allow the normal ssh-to-job code to run.  We'd also probably
// like ssh-to-job to connect to the docker/internal stuff anyway...
//

//
// TODO: Allow the use of HTCondor file-transfer to provide the image.
// May require understanding "self-hosting".
//

//
// TODO: Leverage the procd to gather usage information; Docker uses
// the full container ID as (part of) the cgroup identifier(s).
//

//
// TODO: Can we configure Docker to store its overlay filesystem in
// the sandbox?  Would that be useful for cleanup, or to help admins
// allocate disk space?
//

//
// TODO: Write the container ID out to disk (<sandbox>../../.dotfile)
// like the VM universe so that the startd can clean up if the starter
// crashes and again, if necessary, on start-up.
//

DockerProc::DockerProc( ClassAd * jobAd ) : VanillaProc( jobAd ) { }

DockerProc::~DockerProc() { }

int DockerProc::StartJob() {
	std::string imageID;
	if( ! JobAd->LookupString( ATTR_DOCKER_IMAGE_ID, imageID ) ) {
		dprintf( D_ALWAYS | D_FAILURE, "%s not defined in job ad, unable to start job.\n", ATTR_DOCKER_IMAGE_ID );
		return FALSE;
	}

	std::string command;
	JobAd->LookupString( ATTR_JOB_CMD, command );
	dprintf( D_FULLDEBUG, "%s: '%s'\n", ATTR_JOB_CMD, command.c_str() );

	ArgList args;
	args.SetArgV1SyntaxToCurrentPlatform();
	MyString argsError;
	if( ! args.AppendArgsFromClassAd( JobAd, & argsError ) ) {
		dprintf( D_ALWAYS | D_FAILURE, "Failed to read job arguments from job ad: '%s'.\n", argsError.c_str() );
		return FALSE;
	}

	// We can't just use Starter->GetJobEnv() because we need to change e.g.,
	// _CONDOR_SCRATCH_DIR to be sensible for the chroot jail.
	// TODO: pass requested job environment to Docker.
	Env e;

	std::string sandboxPath = Starter->jic->jobRemoteIWD();

	// The GlobalJobID is unsuitable by virtue its octothorpes.  This
	// construction is informative, but could be made even less likely
	// to collide if it had a timestamp.
	std::string dockerName;
	formatstr( dockerName, "%s_cluster%d_proc%d_starterPID%d",
		Starter->getMySlotName().c_str(),
		Starter->jic->jobCluster(),
		Starter->jic->jobProc(),
		getpid() );

	CondorError err;
	// DockerAPI::run() returns a PID from daemonCore->Create_Process(), which
	// makes it suitable for passing up into VanillaProc.  This combination
	// will trigger the reaper(s) when the container terminates.
	int rv = DockerAPI::run( dockerName, imageID, command, args, e, sandboxPath, containerID, JobPid, err );
	if( rv < 0 ) {
		dprintf( D_ALWAYS | D_FAILURE, "DockerAPI::run( %s, %s, ... ) failed with return value %d\n", imageID.c_str(), command.c_str(), rv );
		return FALSE;
	}
	dprintf( D_FULLDEBUG, "DockerAPI::run() returned container ID '%s' and pid %d\n", containerID.c_str(), JobPid );

	// TODO: Start a timer to poll for job usage updates.

	++num_pids; // Used by OsProc::PublishUpdateAd().
	return TRUE;
}

bool DockerProc::JobReaper( int pid, int status ) {
	dprintf( D_ALWAYS, "DockerProc::JobReaper()\n" );

	//
	// This should mean that the container has terminated.
	//
	if( pid == JobPid ) {
		// TODO: Verify that the container has terminated.
		// docker inspect -format <isRunning, exitCode?> ${containerID}
		// TODO: Set status appropriately (as if it were from waitpid()).
		// TODO: Record final job usage.

		// We don't have to do any process clean-up, because container.
		// We'll do the disk clean-up after we've transferred files.
	}

	// This helps to make ssh-to-job more plausible.
	return VanillaProc::JobReaper( pid, status );
}

//
// JobExit() is called after file transfer.
//
bool DockerProc::JobExit() {
	dprintf( D_ALWAYS, "DockerProc::JobExit()\n" );

	// TODO: docker rm ${containerID}

	return VanillaProc::JobExit();
}

void DockerProc::Suspend() {
	dprintf( D_ALWAYS, "DockerProc::Suspend()\n" );

	// TODO: docker pause ${containerID} only exists in Docker 1.1+.

	is_suspended = true;
}

void DockerProc::Continue() {
	dprintf( D_ALWAYS, "DockerProc::Continue()\n" );

	if( is_suspended ) {
		// TODO: docker unpause ${containerID} only exists in Docker 1.1+.

		is_suspended = false;
	}
}

//
// Setting requested_exit allows OsProc::JobExit() to handle telling the
// user why the job exited.
//

bool DockerProc::Remove() {
	dprintf( D_ALWAYS, "DockerProc::Remove()\n" );

	if( is_suspended ) { Continue(); }
	requested_exit = true;

	// TODO: docker kill --signal=${rm_kill_sig} ${containerID}

	// Do NOT send any signals to the waiting process.  It should only
	// react when the container does.

	// If rm_kill_sig is not SIGKILL, the process may linger.  Returning
	// false indicates that shutdown is pending.
	return false;
}

bool DockerProc::Hold() {
	dprintf( D_ALWAYS, "DockerProc::Hold()\n" );

	if( is_suspended ) { Continue(); }
	requested_exit = true;

	// TODO: docker kill --signal=${hold_kill_sig} ${containerID}

	// Do NOT send any signals to the waiting process.  It should only
	// react when the container does.

	// If rm_kill_sig is not SIGKILL, the process may linger.  Returning
	// false indicates that shutdown is pending.
	return false;
}

bool DockerProc::ShutdownGraceful() {
	dprintf( D_ALWAYS, "DockerProc::ShutdownGraceful()\n" );

	if( containerID.empty() ) {
		// We haven't started a Docker yet, probably because we're still
		// doing file transfer.  Since we're all done, just return true;
		// the FileTransfer object will clean itself up.
		return true;
	}

	if( is_suspended ) { Continue(); }
	requested_exit = true;

	// TODO: rm_kill_sig defaults to soft_kill_sig
	// TODO: docker kill --signal=${rm_kill_sig} ${containerID}

	// Do NOT send any signals to the waiting process.  It should only
	// react when the container does.

	// If rm_kill_sig is not SIGKILL, the process may linger.  Returning
	// false indicates that shutdown is pending.
	return false;
}

bool DockerProc::ShutdownFast() {
	dprintf( D_ALWAYS, "DockerProc::ShutdownFast()\n" );

	if( containerID.empty() ) {
		// We haven't started a Docker yet, probably because we're still
		// doing file transfer.  Since we're all done, just return true;
		// the FileTransfer object will clean itself up.
		return true;
	}

	// There's no point unpausing the container (and possibly swapping
	// it all back in again) if we're just going to be sending it a SIGKILL,
	// so don't bother to Continue() the process if it's been suspended.
	requested_exit = true;

	// TODO: docker kill --signal=SIGKILL ${containerID}

	// Do NOT send any signals to the waiting process.  It should only
	// react when the container does.

	// Based on the other comments, you'd expect this to return true.
	// It could, but it's simpler to just to let the usual routines
	// handle the job clean-up than to duplicate them all here.
	return false;
}

bool DockerProc::PublishUpdateAd( ClassAd * ad ) {
	dprintf( D_ALWAYS, "DockerProc::PublishUpdateAd()\n" );

	// TODO: get usage from procd somehow.  Set num_pids, if we can.  See
	// VanillaProc::PublishUpdateAd() for the attributes to set.

	//
	// If we want to use the existing reporting code (probably a good
	// idea), we'll need to make sure that m_proc_exited, is_checkpointed,
	// is_suspended, num_pids, and dumped_core are set for OsProc; and that
	// job_start_time, job_exit_time, and exit_status are set.
	//
	// We set is_suspended and num_pids already, except for the TODO above.
	// DockerProc::JobReaper() already sets m_proc_exited, exit_code, and
	// dumped_core (indirectly, via OsProc::JobReaper()).
	//
	// We will need to set is_checkpointed appropriately when we support it.
	//
	// TODO: We could approximate job_start_time and job_exit_time internally,
	// or set them during our status polling.
	//

	return OsProc::PublishUpdateAd( ad );
}

// TODO: Implement.
void DockerProc::PublishToEnv( Env * env ) {
	dprintf( D_ALWAYS, "DockerProc::PublishToEnv()\n" );
	return;
}
<|endoftext|>


<commit_before>//
// Aspia Project
// Copyright (C) 2018 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "host/win/host_process_impl.h"

#include <userenv.h>
#include <wtsapi32.h>

#include "base/win/process_util.h"
#include "base/logging.h"

namespace host {

namespace {

// Name of the default session desktop.
const wchar_t kDefaultDesktopName[] = L"winsta0\\default";

// Creates a copy of the current process token for the given |session_id| so
// it can be used to launch a process in that session.
HostProcess::ErrorCode createSessionToken(DWORD session_id, base::win::ScopedHandle* token_out)
{
    base::win::ScopedHandle session_token;
    const DWORD desired_access = TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID |
        TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY;

    if (!copyProcessToken(desired_access, &session_token))
        return HostProcess::OtherError;

    base::win::ScopedHandle privileged_token;

    if (!createPrivilegedToken(&privileged_token))
        return HostProcess::OtherError;

    if (!ImpersonateLoggedOnUser(privileged_token))
    {
        PLOG(LS_WARNING) << "ImpersonateLoggedOnUser failed";
        return HostProcess::OtherError;
    }

    // Change the session ID of the token.
    BOOL ret = SetTokenInformation(session_token, TokenSessionId, &session_id, sizeof(session_id));

    if (!RevertToSelf())
    {
        LOG(LS_FATAL) << "RevertToSelf failed";
        return HostProcess::OtherError;
    }

    if (!ret)
    {
        PLOG(LS_WARNING) << "SetTokenInformation failed";
        return HostProcess::OtherError;
    }

    DWORD ui_access = 1;
    if (!SetTokenInformation(session_token, TokenUIAccess, &ui_access, sizeof(ui_access)))
    {
        PLOG(LS_WARNING) << "SetTokenInformation failed";
        return HostProcess::OtherError;
    }

    token_out->reset(session_token.release());
    return HostProcess::NoError;
}

HostProcess::ErrorCode createLoggedOnUserToken(
    DWORD session_id, base::win::ScopedHandle* token_out)
{
    base::win::ScopedHandle privileged_token;

    if (!createPrivilegedToken(&privileged_token))
        return HostProcess::OtherError;

    if (!ImpersonateLoggedOnUser(privileged_token))
    {
        PLOG(LS_WARNING) << "ImpersonateLoggedOnUser failed";
        return HostProcess::OtherError;
    }

    HostProcess::ErrorCode error_code = HostProcess::NoError;

    base::win::ScopedHandle user_token;
    if (!WTSQueryUserToken(session_id, user_token.recieve()))
    {
        DWORD system_error_code = GetLastError();

        if (system_error_code != ERROR_NO_TOKEN)
        {
            error_code = HostProcess::OtherError;
            LOG(LS_WARNING) << "WTSQueryUserToken failed: "
                            << base::systemErrorCodeToString(system_error_code);
        }
        else
        {
            error_code = HostProcess::NoLoggedOnUser;
        }
    }

    if (!RevertToSelf())
    {
        LOG(LS_FATAL) << "RevertToSelf failed";
        return HostProcess::OtherError;
    }

    if (error_code == HostProcess::NoError)
    {
        TOKEN_ELEVATION_TYPE elevation_type;
        DWORD returned_length;

        if (!GetTokenInformation(user_token,
                                 TokenElevationType,
                                 &elevation_type,
                                 sizeof(elevation_type),
                                 &returned_length))
        {
            PLOG(LS_WARNING) << "GetTokenInformation failed";
            return HostProcess::OtherError;
        }

        switch (elevation_type)
        {
            // The token is a limited token.
            case TokenElevationTypeLimited:
            {
                TOKEN_LINKED_TOKEN linked_token_info;

                // Get the unfiltered token for a silent UAC bypass.
                if (!GetTokenInformation(user_token,
                                         TokenLinkedToken,
                                         &linked_token_info,
                                         sizeof(linked_token_info),
                                         &returned_length))
                {
                    PLOG(LS_WARNING) << "GetTokenInformation failed";
                    return HostProcess::OtherError;
                }

                // Attach linked token.
                token_out->reset(linked_token_info.LinkedToken);
            }
            break;

            case TokenElevationTypeDefault: // The token does not have a linked token.
            case TokenElevationTypeFull:    // The token is an elevated token.
            default:
                token_out->reset(user_token.release());
                break;
        }

        DWORD ui_access = 1;
        if (!SetTokenInformation(token_out->get(), TokenUIAccess, &ui_access, sizeof(ui_access)))
        {
            PLOG(LS_WARNING) << "SetTokenInformation failed";
            return HostProcess::OtherError;
        }
    }

    return error_code;
}

BOOL CALLBACK terminateEnumProc(HWND hwnd, LPARAM lparam)
{
    DWORD process_id = static_cast<DWORD>(lparam);

    DWORD current_process_id = 0;
    GetWindowThreadProcessId(hwnd, &current_process_id);

    if (process_id == current_process_id)
        PostMessageW(hwnd, WM_CLOSE, 0, 0);

    return TRUE;
}

} // namespace

HostProcessImpl::HostProcessImpl(HostProcess* process)
    : process_(process)
{
    // Nothing
}

HostProcessImpl::~HostProcessImpl()
{
    delete finish_notifier_;
}

void HostProcessImpl::startProcess()
{
    state_ = HostProcess::Starting;

    base::win::ScopedHandle session_token;

    if (account_ == HostProcess::System)
    {
        HostProcess::ErrorCode error_code = createSessionToken(session_id_, &session_token);
        if (error_code != HostProcess::NoError)
        {
            state_ = HostProcess::NotRunning;
            emit process_->errorOccurred(error_code);
            return;
        }
    }
    else
    {
        DCHECK_EQ(account_, HostProcess::User);

        HostProcess::ErrorCode error_code = createLoggedOnUserToken(session_id_, &session_token);
        if (error_code != HostProcess::NoError)
        {
            state_ = HostProcess::NotRunning;
            emit process_->errorOccurred(error_code);
            return;
        }
    }

    if (!startProcessWithToken(session_token))
    {
        state_ = HostProcess::NotRunning;
        emit process_->errorOccurred(HostProcess::OtherError);
        return;
    }

    finish_notifier_ = new QWinEventNotifier(process_handle_.get());

    QObject::connect(finish_notifier_, &QWinEventNotifier::activated, [this](HANDLE /* handle */)
    {
        state_ = HostProcess::NotRunning;
        emit process_->finished();
    });

    finish_notifier_->setEnabled(true);

    state_ = HostProcess::Running;
}

void HostProcessImpl::killProcess()
{
    if (!process_handle_.isValid())
    {
        LOG(LS_WARNING) << "Invalid process handle";
        return;
    }

    TerminateProcess(process_handle_, 0);
}

void HostProcessImpl::terminateProcess()
{
    if (!process_handle_.isValid())
    {
        LOG(LS_WARNING) << "Invalid process handle";
        return;
    }

    EnumWindows(terminateEnumProc, process_id_);
    PostThreadMessageW(thread_id_, WM_CLOSE, 0, 0);
}

bool HostProcessImpl::startProcessWithToken(HANDLE token)
{
    STARTUPINFOW startup_info;
    memset(&startup_info, 0, sizeof(startup_info));

    startup_info.cb = sizeof(startup_info);
    startup_info.lpDesktop = const_cast<wchar_t*>(kDefaultDesktopName);

    void* environment = nullptr;

    if (!CreateEnvironmentBlock(&environment, token, FALSE))
    {
        PLOG(LS_WARNING) << "CreateEnvironmentBlock failed";
        return false;
    }

    PROCESS_INFORMATION process_info;
    memset(&process_info, 0, sizeof(process_info));

    QString command_line = base::win::createCommandLine(program_, arguments_);

    if (!CreateProcessAsUserW(token,
                              nullptr,
                              const_cast<wchar_t*>(qUtf16Printable(command_line)),
                              nullptr,
                              nullptr,
                              FALSE,
                              CREATE_UNICODE_ENVIRONMENT | HIGH_PRIORITY_CLASS,
                              environment,
                              nullptr,
                              &startup_info,
                              &process_info))
    {
        PLOG(LS_WARNING) << "CreateProcessAsUserW failed";
        DestroyEnvironmentBlock(environment);
        return false;
    }

    thread_handle_.reset(process_info.hThread);
    process_handle_.reset(process_info.hProcess);
    thread_id_ = process_info.dwThreadId;
    process_id_ = process_info.dwProcessId;

    DestroyEnvironmentBlock(environment);
    return true;
}

} // namespace host
<commit_msg>- We must crash if RevertToSelf returns FALSE.<commit_after>//
// Aspia Project
// Copyright (C) 2018 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "host/win/host_process_impl.h"

#include <userenv.h>
#include <wtsapi32.h>

#include "base/win/process_util.h"
#include "base/logging.h"

namespace host {

namespace {

// Name of the default session desktop.
const wchar_t kDefaultDesktopName[] = L"winsta0\\default";

// Creates a copy of the current process token for the given |session_id| so
// it can be used to launch a process in that session.
HostProcess::ErrorCode createSessionToken(DWORD session_id, base::win::ScopedHandle* token_out)
{
    base::win::ScopedHandle session_token;
    const DWORD desired_access = TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID |
        TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY;

    if (!copyProcessToken(desired_access, &session_token))
        return HostProcess::OtherError;

    base::win::ScopedHandle privileged_token;

    if (!createPrivilegedToken(&privileged_token))
        return HostProcess::OtherError;

    if (!ImpersonateLoggedOnUser(privileged_token))
    {
        PLOG(LS_WARNING) << "ImpersonateLoggedOnUser failed";
        return HostProcess::OtherError;
    }

    // Change the session ID of the token.
    BOOL result = SetTokenInformation(session_token, TokenSessionId, &session_id, sizeof(session_id));

    BOOL ret = RevertToSelf();
    CHECK(ret);

    if (!result)
    {
        PLOG(LS_WARNING) << "SetTokenInformation failed";
        return HostProcess::OtherError;
    }

    DWORD ui_access = 1;
    if (!SetTokenInformation(session_token, TokenUIAccess, &ui_access, sizeof(ui_access)))
    {
        PLOG(LS_WARNING) << "SetTokenInformation failed";
        return HostProcess::OtherError;
    }

    token_out->reset(session_token.release());
    return HostProcess::NoError;
}

HostProcess::ErrorCode createLoggedOnUserToken(
    DWORD session_id, base::win::ScopedHandle* token_out)
{
    base::win::ScopedHandle privileged_token;

    if (!createPrivilegedToken(&privileged_token))
        return HostProcess::OtherError;

    if (!ImpersonateLoggedOnUser(privileged_token))
    {
        PLOG(LS_WARNING) << "ImpersonateLoggedOnUser failed";
        return HostProcess::OtherError;
    }

    HostProcess::ErrorCode error_code = HostProcess::NoError;

    base::win::ScopedHandle user_token;
    if (!WTSQueryUserToken(session_id, user_token.recieve()))
    {
        DWORD system_error_code = GetLastError();

        if (system_error_code != ERROR_NO_TOKEN)
        {
            error_code = HostProcess::OtherError;
            LOG(LS_WARNING) << "WTSQueryUserToken failed: "
                            << base::systemErrorCodeToString(system_error_code);
        }
        else
        {
            error_code = HostProcess::NoLoggedOnUser;
        }
    }

    BOOL ret = RevertToSelf();
    CHECK(ret);

    if (error_code == HostProcess::NoError)
    {
        TOKEN_ELEVATION_TYPE elevation_type;
        DWORD returned_length;

        if (!GetTokenInformation(user_token,
                                 TokenElevationType,
                                 &elevation_type,
                                 sizeof(elevation_type),
                                 &returned_length))
        {
            PLOG(LS_WARNING) << "GetTokenInformation failed";
            return HostProcess::OtherError;
        }

        switch (elevation_type)
        {
            // The token is a limited token.
            case TokenElevationTypeLimited:
            {
                TOKEN_LINKED_TOKEN linked_token_info;

                // Get the unfiltered token for a silent UAC bypass.
                if (!GetTokenInformation(user_token,
                                         TokenLinkedToken,
                                         &linked_token_info,
                                         sizeof(linked_token_info),
                                         &returned_length))
                {
                    PLOG(LS_WARNING) << "GetTokenInformation failed";
                    return HostProcess::OtherError;
                }

                // Attach linked token.
                token_out->reset(linked_token_info.LinkedToken);
            }
            break;

            case TokenElevationTypeDefault: // The token does not have a linked token.
            case TokenElevationTypeFull:    // The token is an elevated token.
            default:
                token_out->reset(user_token.release());
                break;
        }

        DWORD ui_access = 1;
        if (!SetTokenInformation(token_out->get(), TokenUIAccess, &ui_access, sizeof(ui_access)))
        {
            PLOG(LS_WARNING) << "SetTokenInformation failed";
            return HostProcess::OtherError;
        }
    }

    return error_code;
}

BOOL CALLBACK terminateEnumProc(HWND hwnd, LPARAM lparam)
{
    DWORD process_id = static_cast<DWORD>(lparam);

    DWORD current_process_id = 0;
    GetWindowThreadProcessId(hwnd, &current_process_id);

    if (process_id == current_process_id)
        PostMessageW(hwnd, WM_CLOSE, 0, 0);

    return TRUE;
}

} // namespace

HostProcessImpl::HostProcessImpl(HostProcess* process)
    : process_(process)
{
    // Nothing
}

HostProcessImpl::~HostProcessImpl()
{
    delete finish_notifier_;
}

void HostProcessImpl::startProcess()
{
    state_ = HostProcess::Starting;

    base::win::ScopedHandle session_token;

    if (account_ == HostProcess::System)
    {
        HostProcess::ErrorCode error_code = createSessionToken(session_id_, &session_token);
        if (error_code != HostProcess::NoError)
        {
            state_ = HostProcess::NotRunning;
            emit process_->errorOccurred(error_code);
            return;
        }
    }
    else
    {
        DCHECK_EQ(account_, HostProcess::User);

        HostProcess::ErrorCode error_code = createLoggedOnUserToken(session_id_, &session_token);
        if (error_code != HostProcess::NoError)
        {
            state_ = HostProcess::NotRunning;
            emit process_->errorOccurred(error_code);
            return;
        }
    }

    if (!startProcessWithToken(session_token))
    {
        state_ = HostProcess::NotRunning;
        emit process_->errorOccurred(HostProcess::OtherError);
        return;
    }

    finish_notifier_ = new QWinEventNotifier(process_handle_.get());

    QObject::connect(finish_notifier_, &QWinEventNotifier::activated, [this](HANDLE /* handle */)
    {
        state_ = HostProcess::NotRunning;
        emit process_->finished();
    });

    finish_notifier_->setEnabled(true);

    state_ = HostProcess::Running;
}

void HostProcessImpl::killProcess()
{
    if (!process_handle_.isValid())
    {
        LOG(LS_WARNING) << "Invalid process handle";
        return;
    }

    TerminateProcess(process_handle_, 0);
}

void HostProcessImpl::terminateProcess()
{
    if (!process_handle_.isValid())
    {
        LOG(LS_WARNING) << "Invalid process handle";
        return;
    }

    EnumWindows(terminateEnumProc, process_id_);
    PostThreadMessageW(thread_id_, WM_CLOSE, 0, 0);
}

bool HostProcessImpl::startProcessWithToken(HANDLE token)
{
    STARTUPINFOW startup_info;
    memset(&startup_info, 0, sizeof(startup_info));

    startup_info.cb = sizeof(startup_info);
    startup_info.lpDesktop = const_cast<wchar_t*>(kDefaultDesktopName);

    void* environment = nullptr;

    if (!CreateEnvironmentBlock(&environment, token, FALSE))
    {
        PLOG(LS_WARNING) << "CreateEnvironmentBlock failed";
        return false;
    }

    PROCESS_INFORMATION process_info;
    memset(&process_info, 0, sizeof(process_info));

    QString command_line = base::win::createCommandLine(program_, arguments_);

    if (!CreateProcessAsUserW(token,
                              nullptr,
                              const_cast<wchar_t*>(qUtf16Printable(command_line)),
                              nullptr,
                              nullptr,
                              FALSE,
                              CREATE_UNICODE_ENVIRONMENT | HIGH_PRIORITY_CLASS,
                              environment,
                              nullptr,
                              &startup_info,
                              &process_info))
    {
        PLOG(LS_WARNING) << "CreateProcessAsUserW failed";
        DestroyEnvironmentBlock(environment);
        return false;
    }

    thread_handle_.reset(process_info.hThread);
    process_handle_.reset(process_info.hProcess);
    thread_id_ = process_info.dwThreadId;
    process_id_ = process_info.dwProcessId;

    DestroyEnvironmentBlock(environment);
    return true;
}

} // namespace host
<|endoftext|>


<commit_before>//#define _DEBUG

#include <algorithm>
#include <climits>
#include <cstddef>
#include <memory>
#include <string>
#include <vector>
#ifdef _DEBUG
  #include <iostream>
#endif

#include "js6chars.hpp"
std::string char_repr(char value, bool* cannot_use, char forbidden);
std::string str_repr(const char* str, bool* cannot_use, char forbidden);
std::string str_repr(std::string const& in, bool* cannot_use, char forbidden);

static std::string small_number_repr(int value)
{
  return value == 0
      ? "+[]"
      : value == 1
        ? "++[[]][+[]]"
        : "++[" + number_repr(value -1) + "][+[]]";
}

static std::string number_repr_helper(int value)
{
  if (value < 10) return small_number_repr(value) + "+[]";
  return number_repr_helper(value/10) + "+(" + small_number_repr(value%10) + ")";
}

std::string number_repr(int value)
{
  if (value < 0)
  {
    return std::string("+(") + char_repr('-') + "+(" + number_repr_helper(-value) + "))";
  }
  return value < 10
      ? small_number_repr(value)
      : std::string("+(") + number_repr_helper(value) + ")";
}

namespace {
template <std::size_t N> std::string from_known(char value, const char (&real_str)[N], std::string&& generated)
{
  std::string shortest_match;
  char const* it = std::find(std::begin(real_str), std::end(real_str), value);
  for ( ; it != std::end(real_str) ; it = std::find(std::next(it), std::end(real_str), value))
  {
    std::string match = "(" + generated + "+[])" + "[" + number_repr((int)(it - real_str)) + "]";
    if (shortest_match.empty() || shortest_match.size() > match.size())
    {
      shortest_match = match;
    }
  }
  return shortest_match;
}
}

struct Generator { virtual std::string operator() (char, bool*, char) = 0; };
// 0123456789 should be omitted from require variable as trivally constructibles

struct NumberGenerator : Generator
{
  static constexpr const char* generate = "0123456789";
  static constexpr const char* require = "";
  std::string operator() (char value, bool* /*cannot_use*/, char /*forbidden*/) override { return number_repr(value-'0') + "+[]"; }
};
struct UndefinedGenerator : Generator
{
  static constexpr const char* generate = "undefi";
  static constexpr const char* require = "";
  std::string operator() (char value, bool* /*cannot_use*/, char /*forbidden*/) override { return from_known(value, "undefined", "[][+[]]"); }
};
struct NanGenerator : Generator
{
  static constexpr const char* generate = "Na";
  static constexpr const char* require = "u";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "NaN", "+" + char_repr('u', cannot_use, forbidden)); }
};
struct SmallerNanGenerator : Generator
{
  static constexpr const char* generate = "Na";
  static constexpr const char* require = "";
  std::string operator() (char value, bool* /*cannot_use*/, char forbidden) override { return forbidden != '!' ? from_known(value, "NaN", "+(![]+[])") : ""; }
};
struct PlusGenerator : Generator
{
  static constexpr const char* generate = "+";
  static constexpr const char* require = "e";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "1e+30", "+(" + str_repr("1e30", cannot_use, forbidden) + ")"); }
};
struct FindGenerator : Generator
{
  static constexpr const char* generate = "ucto ()";
  static constexpr const char* require = "find";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function find()", "[][" + str_repr("find", cannot_use, forbidden) + "]"); }
};
struct TrueGenerator : Generator
{
  static constexpr const char* generate = "true";
  static constexpr const char* require = "";
  std::string operator() (char value, bool* /*cannot_use*/, char forbidden) override { return forbidden != '!' ? from_known(value, "true", "!![]") : ""; }
};
struct FalseGenerator : Generator
{
  static constexpr const char* generate = "false";
  static constexpr const char* require = "";
  std::string operator() (char value, bool* /*cannot_use*/, char forbidden) override { return forbidden != '!' ? from_known(value, "false", "![]") : ""; }
};
struct InfinityGenerator : Generator
{
  static constexpr const char* generate = "Infity";
  static constexpr const char* require = "e";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "Infinity", "+(" + str_repr("1e1000", cannot_use, forbidden) + ")"); }
};
struct SquareBracketGenerator : Generator
{
  static constexpr const char* generate = "[objct AayIa]";
  static constexpr const char* require = "entris";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "[object Array Iterator]", "[][" + str_repr("entries", cannot_use, forbidden) + "]"); }
};
struct ClassArrayGenerator : Generator
{
  static constexpr const char* generate = "fi Aay()";
  static constexpr const char* require = "constru";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function Array()", "[][" + str_repr("constructor", cannot_use, forbidden) + "]"); }
};
struct ClassBooleanGenerator : Generator
{
  static constexpr const char* generate = "fi Blea()";
  static constexpr const char* require = "constru";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function Boolean()", "(![])[" + str_repr("constructor", cannot_use, forbidden) + "]"); }
};
struct ClassNumberGenerator : Generator
{
  static constexpr const char* generate = "fi Nmbe()";
  static constexpr const char* require = "constru";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function Number()", "(+[])[" + str_repr("constructor", cannot_use, forbidden) + "]"); }
};
struct ClassStringGenerator : Generator
{
  static constexpr const char* generate = "fi Sg()";
  static constexpr const char* require = "constru";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function String()", "([]+[])[" + str_repr("constructor", cannot_use, forbidden) + "]"); }
};
struct ClassFunctionGenerator : Generator
{
  static constexpr const char* generate = " F()";
  static constexpr const char* require = "construfid";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function Function()", "[][" + str_repr("find", cannot_use, forbidden) + "][" + str_repr("constructor", cannot_use, forbidden) + "]"); }
};
struct NumberInBaseGenerator : Generator
{
  static constexpr const char* generate = "abcdefhjklmpqsuvwxyz";
  static constexpr const char* require = "toSring";
  std::string operator() (char value, bool* cannot_use, char forbidden) override
  {
    int base = 11 + value -'a';
    if (base <= 30) { // max is 36
      base = ((int)(base/10) +1) * 10;
    }
    return "(" + number_repr(10 + value -'a') + ")[" + str_repr("toString", cannot_use, forbidden) + "](" + number_repr(base) + ")";
  }
};
struct CGenerator : Generator
{
  static constexpr const char* generate = "C";
  static constexpr const char* require = "construfid[]+()";// ! cannot be encountered because str_repr("20N", '!')
  std::string operator() (char, bool* cannot_use, char forbidden) override { return "[][" + str_repr("find", cannot_use, forbidden) + "][" + str_repr("constructor", cannot_use, forbidden) + "](" + str_repr("return atob(" + str_repr("20N", cannot_use, '!') + ")[1]", cannot_use, forbidden) + ")"; }
};
struct AllGenerator : Generator
{
  static constexpr const char* require = "construfmChade";// and also !
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return "([]+[])[" + str_repr("constructor", cannot_use, forbidden) + "][" + str_repr("fromCharCode", cannot_use, forbidden) + "](" + number_repr((int)value) + ")"; }
};

template <class Gen> void push_dependency(auto&& tree, char c)
{
  constexpr const char* str = Gen::require;
#ifdef _DEBUG
  std::cout << "  (" << ((int)c) << ")'" << c << "' requires \"" << str << "\"" << std::endl;
#endif
  tree[c - CHAR_MIN].emplace_back(str, std::make_unique<Gen>());
}
template <class Gen> void push_dependencies(auto&& tree)
{
  for (auto it = Gen::generate ; !!*it ; ++it) { push_dependency<Gen>(tree, *it); }
}

auto build_dependency_tree()
{
  // for each char we declare the list of all the available Generator able to build it
  // give nthe definition of required entries
  // eg.:
  //   '[' -> [ (find), call [].find ]
  std::vector< std::vector< std::pair<std::string, std::unique_ptr<Generator>> > > tree(256);

#ifdef _DEBUG
  std::cout << "Start build_dependency_tree" << std::endl;
#endif
  push_dependencies<NumberGenerator>(tree);
  push_dependencies<UndefinedGenerator>(tree);
  push_dependencies<NanGenerator>(tree);
  push_dependencies<SmallerNanGenerator>(tree);
  push_dependencies<PlusGenerator>(tree);
  push_dependencies<FindGenerator>(tree);
  push_dependencies<TrueGenerator>(tree);
  push_dependencies<FalseGenerator>(tree);
  push_dependencies<InfinityGenerator>(tree);
  push_dependencies<SquareBracketGenerator>(tree);
  push_dependencies<ClassArrayGenerator>(tree);
  push_dependencies<ClassBooleanGenerator>(tree);
  push_dependencies<ClassNumberGenerator>(tree);
  push_dependencies<ClassStringGenerator>(tree);
  push_dependencies<ClassFunctionGenerator>(tree);
  push_dependencies<NumberInBaseGenerator>(tree);
  push_dependencies<CGenerator>(tree);
  push_dependencies<NumberInBaseGenerator>(tree);
  push_dependencies<ClassNumberGenerator>(tree);
  push_dependencies<ClassStringGenerator>(tree);
  push_dependencies<ClassFunctionGenerator>(tree);
  push_dependencies<NumberInBaseGenerator>(tree);
  push_dependencies<CGenerator>(tree);
  for (int c = (int)CHAR_MIN ; c <= (int)CHAR_MAX ; ++c)
  {
    unsigned idx = c - CHAR_MIN;
    if (tree[idx].empty())
    {
      push_dependency<AllGenerator>(tree, (char)c);
    }
  }
#ifdef _DEBUG
  std::cout << "End build_dependency_tree" << std::endl;
#endif

  return tree;
}

std::string char_repr(char value, bool* cannot_use, char forbidden)
{
  static const auto tree = build_dependency_tree();
#ifdef _DEBUG
  std::cout << "Generating (" << ((int)value) << ")'" << value << "'";
  if (forbidden) std::cout << " with forbidden (" << ((int)forbidden) << ")'" << forbidden << "'";
  std::cout << std::endl;
#endif
  auto const& choices = tree[value - CHAR_MIN];
#ifdef _DEBUG
  std::cout << "  # " << choices.size() << " candidates" << std::endl;
#endif

  // Keep only generators that can be used (do no require stuff being computed)
  std::vector<Generator*> generators;
  for (auto const& choice : choices)
  {
    std::string const& require = choice.first;
#ifdef _DEBUG
    std::cout << "  candidate with require: " << require << std::endl;
#endif
    if (std::find_if(require.begin(), require.end(), [cannot_use](auto c) { return cannot_use[c - CHAR_MIN]; }) == require.end())
    {
      generators.push_back(choice.second.get());
    }
  }
#ifdef _DEBUG
  std::cout << "  # " << generators.size() << " selected" << std::endl;
#endif

  // Find the best match

  cannot_use[value - CHAR_MIN] = true;

  std::string best_match;
  for (auto gen : generators)
  {
    std::string match = gen->operator()(value, cannot_use, forbidden);
#ifdef _DEBUG
    std::cout << "  :(" << ((int)value) << ")'" << value << "': " << match << std::endl;
#endif
    if (best_match.empty() || best_match.size() > match.size())
    {
      best_match = match;
    }
  }

  cannot_use[value - CHAR_MIN] = false;

  return best_match;
}
std::string char_repr(char value)
{
  bool tab[CHAR_MAX-CHAR_MIN] = { 0 };
  std::fill(std::begin(tab), std::end(tab), false);
  return char_repr(value, tab, '\0');
}

std::string str_repr(const char* str, bool* cannot_use, char forbidden)
{
  std::string out {};
  for (auto it = str ; *it ; ++it)
  {
    if (cannot_use[*it - CHAR_MIN]) { return out; }
  }
  for (auto it = str ; *it ; ++it)
  {
    if (it != str)
    {
      out += '+';
    }
    out += '(';
    auto forchar = char_repr(*it, cannot_use, forbidden);
    if (forchar.empty()) { return ""; }
    out += forchar;
    out += ')';
  }
  return out;
}
std::string str_repr(std::string const& in, bool* cannot_use, char forbidden)
{
  return str_repr(in.c_str(), cannot_use, forbidden);
}
std::string str_repr(const char* str)
{
  bool tab[CHAR_MAX-CHAR_MIN] = { 0 };
  std::fill(std::begin(tab), std::end(tab), false);
  return str_repr(str, tab, '\0');
}
std::string str_repr(std::string const& in) { return str_repr(in.c_str()); }

std::string run_command(const char* str)
{
  return std::string("[][") + str_repr("constructor") + "][" + str_repr("constructor") + "](" + str_repr(str) + ")()";
}
std::string run_command(std::string const& in)
{
  return run_command(in.c_str());
}

<commit_msg>Typo: was not calling the Function generated to retrieve C<commit_after>//#define _DEBUG

#include <algorithm>
#include <climits>
#include <cstddef>
#include <memory>
#include <string>
#include <vector>
#ifdef _DEBUG
  #include <iostream>
#endif

#include "js6chars.hpp"
std::string char_repr(char value, bool* cannot_use, char forbidden);
std::string str_repr(const char* str, bool* cannot_use, char forbidden);
std::string str_repr(std::string const& in, bool* cannot_use, char forbidden);

static std::string small_number_repr(int value)
{
  return value == 0
      ? "+[]"
      : value == 1
        ? "++[[]][+[]]"
        : "++[" + number_repr(value -1) + "][+[]]";
}

static std::string number_repr_helper(int value)
{
  if (value < 10) return small_number_repr(value) + "+[]";
  return number_repr_helper(value/10) + "+(" + small_number_repr(value%10) + ")";
}

std::string number_repr(int value)
{
  if (value < 0)
  {
    return std::string("+(") + char_repr('-') + "+(" + number_repr_helper(-value) + "))";
  }
  return value < 10
      ? small_number_repr(value)
      : std::string("+(") + number_repr_helper(value) + ")";
}

namespace {
template <std::size_t N> std::string from_known(char value, const char (&real_str)[N], std::string&& generated)
{
  std::string shortest_match;
  char const* it = std::find(std::begin(real_str), std::end(real_str), value);
  for ( ; it != std::end(real_str) ; it = std::find(std::next(it), std::end(real_str), value))
  {
    std::string match = "(" + generated + "+[])" + "[" + number_repr((int)(it - real_str)) + "]";
    if (shortest_match.empty() || shortest_match.size() > match.size())
    {
      shortest_match = match;
    }
  }
  return shortest_match;
}
}

struct Generator { virtual std::string operator() (char, bool*, char) = 0; };
// 0123456789 should be omitted from require variable as trivally constructibles

struct NumberGenerator : Generator
{
  static constexpr const char* generate = "0123456789";
  static constexpr const char* require = "";
  std::string operator() (char value, bool* /*cannot_use*/, char /*forbidden*/) override { return number_repr(value-'0') + "+[]"; }
};
struct UndefinedGenerator : Generator
{
  static constexpr const char* generate = "undefi";
  static constexpr const char* require = "";
  std::string operator() (char value, bool* /*cannot_use*/, char /*forbidden*/) override { return from_known(value, "undefined", "[][+[]]"); }
};
struct NanGenerator : Generator
{
  static constexpr const char* generate = "Na";
  static constexpr const char* require = "u";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "NaN", "+" + char_repr('u', cannot_use, forbidden)); }
};
struct SmallerNanGenerator : Generator
{
  static constexpr const char* generate = "Na";
  static constexpr const char* require = "";
  std::string operator() (char value, bool* /*cannot_use*/, char forbidden) override { return forbidden != '!' ? from_known(value, "NaN", "+(![]+[])") : ""; }
};
struct PlusGenerator : Generator
{
  static constexpr const char* generate = "+";
  static constexpr const char* require = "e";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "1e+30", "+(" + str_repr("1e30", cannot_use, forbidden) + ")"); }
};
struct FindGenerator : Generator
{
  static constexpr const char* generate = "ucto ()";
  static constexpr const char* require = "find";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function find()", "[][" + str_repr("find", cannot_use, forbidden) + "]"); }
};
struct TrueGenerator : Generator
{
  static constexpr const char* generate = "true";
  static constexpr const char* require = "";
  std::string operator() (char value, bool* /*cannot_use*/, char forbidden) override { return forbidden != '!' ? from_known(value, "true", "!![]") : ""; }
};
struct FalseGenerator : Generator
{
  static constexpr const char* generate = "false";
  static constexpr const char* require = "";
  std::string operator() (char value, bool* /*cannot_use*/, char forbidden) override { return forbidden != '!' ? from_known(value, "false", "![]") : ""; }
};
struct InfinityGenerator : Generator
{
  static constexpr const char* generate = "Infity";
  static constexpr const char* require = "e";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "Infinity", "+(" + str_repr("1e1000", cannot_use, forbidden) + ")"); }
};
struct SquareBracketGenerator : Generator
{
  static constexpr const char* generate = "[objct AayIa]";
  static constexpr const char* require = "entris";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "[object Array Iterator]", "[][" + str_repr("entries", cannot_use, forbidden) + "]"); }
};
struct ClassArrayGenerator : Generator
{
  static constexpr const char* generate = "fi Aay()";
  static constexpr const char* require = "constru";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function Array()", "[][" + str_repr("constructor", cannot_use, forbidden) + "]"); }
};
struct ClassBooleanGenerator : Generator
{
  static constexpr const char* generate = "fi Blea()";
  static constexpr const char* require = "constru";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function Boolean()", "(![])[" + str_repr("constructor", cannot_use, forbidden) + "]"); }
};
struct ClassNumberGenerator : Generator
{
  static constexpr const char* generate = "fi Nmbe()";
  static constexpr const char* require = "constru";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function Number()", "(+[])[" + str_repr("constructor", cannot_use, forbidden) + "]"); }
};
struct ClassStringGenerator : Generator
{
  static constexpr const char* generate = "fi Sg()";
  static constexpr const char* require = "constru";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function String()", "([]+[])[" + str_repr("constructor", cannot_use, forbidden) + "]"); }
};
struct ClassFunctionGenerator : Generator
{
  static constexpr const char* generate = " F()";
  static constexpr const char* require = "construfid";
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return from_known(value, "function Function()", "[][" + str_repr("find", cannot_use, forbidden) + "][" + str_repr("constructor", cannot_use, forbidden) + "]"); }
};
struct NumberInBaseGenerator : Generator
{
  static constexpr const char* generate = "abcdefhjklmpqsuvwxyz";
  static constexpr const char* require = "toSring";
  std::string operator() (char value, bool* cannot_use, char forbidden) override
  {
    int base = 11 + value -'a';
    if (base <= 30) { // max is 36
      base = ((int)(base/10) +1) * 10;
    }
    return "(" + number_repr(10 + value -'a') + ")[" + str_repr("toString", cannot_use, forbidden) + "](" + number_repr(base) + ")";
  }
};
struct CGenerator : Generator
{
  static constexpr const char* generate = "C";
  static constexpr const char* require = "construfid[]+()";// ! cannot be encountered because str_repr("20N", '!')
  std::string operator() (char, bool* cannot_use, char forbidden) override { return "[][" + str_repr("find", cannot_use, forbidden) + "][" + str_repr("constructor", cannot_use, forbidden) + "](" + str_repr("return atob(" + str_repr("20N", cannot_use, '!') + ")[1]", cannot_use, forbidden) + ")()"; }
};
struct AllGenerator : Generator
{
  static constexpr const char* require = "construfmChade";// and also !
  std::string operator() (char value, bool* cannot_use, char forbidden) override { return "([]+[])[" + str_repr("constructor", cannot_use, forbidden) + "][" + str_repr("fromCharCode", cannot_use, forbidden) + "](" + number_repr((int)value) + ")"; }
};

template <class Gen> void push_dependency(auto&& tree, char c)
{
  constexpr const char* str = Gen::require;
#ifdef _DEBUG
  std::cout << "  (" << ((int)c) << ")'" << c << "' requires \"" << str << "\"" << std::endl;
#endif
  tree[c - CHAR_MIN].emplace_back(str, std::make_unique<Gen>());
}
template <class Gen> void push_dependencies(auto&& tree)
{
  for (auto it = Gen::generate ; !!*it ; ++it) { push_dependency<Gen>(tree, *it); }
}

auto build_dependency_tree()
{
  // for each char we declare the list of all the available Generator able to build it
  // give nthe definition of required entries
  // eg.:
  //   '[' -> [ (find), call [].find ]
  std::vector< std::vector< std::pair<std::string, std::unique_ptr<Generator>> > > tree(256);

#ifdef _DEBUG
  std::cout << "Start build_dependency_tree" << std::endl;
#endif
  push_dependencies<NumberGenerator>(tree);
  push_dependencies<UndefinedGenerator>(tree);
  push_dependencies<NanGenerator>(tree);
  push_dependencies<SmallerNanGenerator>(tree);
  push_dependencies<PlusGenerator>(tree);
  push_dependencies<FindGenerator>(tree);
  push_dependencies<TrueGenerator>(tree);
  push_dependencies<FalseGenerator>(tree);
  push_dependencies<InfinityGenerator>(tree);
  push_dependencies<SquareBracketGenerator>(tree);
  push_dependencies<ClassArrayGenerator>(tree);
  push_dependencies<ClassBooleanGenerator>(tree);
  push_dependencies<ClassNumberGenerator>(tree);
  push_dependencies<ClassStringGenerator>(tree);
  push_dependencies<ClassFunctionGenerator>(tree);
  push_dependencies<NumberInBaseGenerator>(tree);
  push_dependencies<CGenerator>(tree);
  push_dependencies<NumberInBaseGenerator>(tree);
  push_dependencies<ClassNumberGenerator>(tree);
  push_dependencies<ClassStringGenerator>(tree);
  push_dependencies<ClassFunctionGenerator>(tree);
  push_dependencies<NumberInBaseGenerator>(tree);
  push_dependencies<CGenerator>(tree);
  for (int c = (int)CHAR_MIN ; c <= (int)CHAR_MAX ; ++c)
  {
    unsigned idx = c - CHAR_MIN;
    if (tree[idx].empty())
    {
      push_dependency<AllGenerator>(tree, (char)c);
    }
  }
#ifdef _DEBUG
  std::cout << "End build_dependency_tree" << std::endl;
#endif

  return tree;
}

std::string char_repr(char value, bool* cannot_use, char forbidden)
{
  static const auto tree = build_dependency_tree();
#ifdef _DEBUG
  std::cout << "Generating (" << ((int)value) << ")'" << value << "'";
  if (forbidden) std::cout << " with forbidden (" << ((int)forbidden) << ")'" << forbidden << "'";
  std::cout << std::endl;
#endif
  auto const& choices = tree[value - CHAR_MIN];
#ifdef _DEBUG
  std::cout << "  # " << choices.size() << " candidates" << std::endl;
#endif

  // Keep only generators that can be used (do no require stuff being computed)
  std::vector<Generator*> generators;
  for (auto const& choice : choices)
  {
    std::string const& require = choice.first;
#ifdef _DEBUG
    std::cout << "  candidate with require: " << require << std::endl;
#endif
    if (std::find_if(require.begin(), require.end(), [cannot_use](auto c) { return cannot_use[c - CHAR_MIN]; }) == require.end())
    {
      generators.push_back(choice.second.get());
    }
  }
#ifdef _DEBUG
  std::cout << "  # " << generators.size() << " selected" << std::endl;
#endif

  // Find the best match

  cannot_use[value - CHAR_MIN] = true;

  std::string best_match;
  for (auto gen : generators)
  {
    std::string match = gen->operator()(value, cannot_use, forbidden);
#ifdef _DEBUG
    std::cout << "  :(" << ((int)value) << ")'" << value << "': " << match << std::endl;
#endif
    if (best_match.empty() || best_match.size() > match.size())
    {
      best_match = match;
    }
  }

  cannot_use[value - CHAR_MIN] = false;

  return best_match;
}
std::string char_repr(char value)
{
  bool tab[CHAR_MAX-CHAR_MIN] = { 0 };
  std::fill(std::begin(tab), std::end(tab), false);
  return char_repr(value, tab, '\0');
}

std::string str_repr(const char* str, bool* cannot_use, char forbidden)
{
  std::string out {};
  for (auto it = str ; *it ; ++it)
  {
    if (cannot_use[*it - CHAR_MIN]) { return out; }
  }
  for (auto it = str ; *it ; ++it)
  {
    if (it != str)
    {
      out += '+';
    }
    out += '(';
    auto forchar = char_repr(*it, cannot_use, forbidden);
    if (forchar.empty()) { return ""; }
    out += forchar;
    out += ')';
  }
  return out;
}
std::string str_repr(std::string const& in, bool* cannot_use, char forbidden)
{
  return str_repr(in.c_str(), cannot_use, forbidden);
}
std::string str_repr(const char* str)
{
  bool tab[CHAR_MAX-CHAR_MIN] = { 0 };
  std::fill(std::begin(tab), std::end(tab), false);
  return str_repr(str, tab, '\0');
}
std::string str_repr(std::string const& in) { return str_repr(in.c_str()); }

std::string run_command(const char* str)
{
  return std::string("[][") + str_repr("constructor") + "][" + str_repr("constructor") + "](" + str_repr(str) + ")()";
}
std::string run_command(std::string const& in)
{
  return run_command(in.c_str());
}

<|endoftext|>


<commit_before>/*
    Copyright (c) 2008 Volker Krause <vkrause@kde.org>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "configdialog.h"
#include "settings.h"

#include <kconfigdialogmanager.h>
#include <kurlrequester.h>

ConfigDialog::ConfigDialog(QWidget * parent) :
    KDialog( parent )
{
  ui.setupUi( mainWidget() );
  mManager = new KConfigDialogManager( this, Settings::self() );
  mManager->updateWidgets();
  ui.kcfg_Path->setMode( KFile::Directory | KFile::ExistingOnly );
  ui.kcfg_Path->setUrl( KUrl( Settings::self()->path() ) );

  connect( this, SIGNAL(okClicked()), SLOT(save()) );
}

void ConfigDialog::save()
{
  mManager->updateSettings();
  Settings::self()->setPath( ui.kcfg_Path->url().path() );
}

#include "configdialog.moc"
<commit_msg>Actually write the changes to disk. Should fix the loss of settings on restart.<commit_after>/*
    Copyright (c) 2008 Volker Krause <vkrause@kde.org>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "configdialog.h"
#include "settings.h"

#include <kconfigdialogmanager.h>
#include <kurlrequester.h>

ConfigDialog::ConfigDialog(QWidget * parent) :
    KDialog( parent )
{
  ui.setupUi( mainWidget() );
  mManager = new KConfigDialogManager( this, Settings::self() );
  mManager->updateWidgets();
  ui.kcfg_Path->setMode( KFile::Directory | KFile::ExistingOnly );
  ui.kcfg_Path->setUrl( KUrl( Settings::self()->path() ) );

  connect( this, SIGNAL(okClicked()), SLOT(save()) );
}

void ConfigDialog::save()
{
  mManager->updateSettings();
  Settings::self()->setPath( ui.kcfg_Path->url().path() );
  Settings::self()->writeConfig();
}

#include "configdialog.moc"
<|endoftext|>


<commit_before>// RUN: %clangxx_msan -O0 %s -o %t
// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t m1 2>&1 | FileCheck %s
// RUN: MSAN_OPTIONS=allocator_may_return_null=1     %run %t m1 2>&1
// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t psm1 2>&1 | FileCheck %s
// RUN: MSAN_OPTIONS=allocator_may_return_null=1     %run %t psm1 2>&1

// UNSUPPORTED: win32, freebsd

// Checks that pvalloc overflows are caught. If the allocator is allowed to
// return null, the errno should be set to ENOMEM.

#include <assert.h>
#include <errno.h>
#include <malloc.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
  void *p;
  size_t page_size;

  assert(argc == 2);

  page_size = sysconf(_SC_PAGESIZE);
  // Check that the page size is a power of two.
  assert((page_size & (page_size - 1)) == 0);

  if (!strcmp(argv[1], "m1")) {
    p = pvalloc((uintptr_t)-1);
    assert(!p);
    assert(errno == ENOMEM);
  }
  if (!strcmp(argv[1], "psm1")) {
    p = pvalloc((uintptr_t)-(page_size - 1));
    assert(!p);
    assert(errno == ENOMEM);
  }

  return 0;
}

// CHECK: MemorySanitizer's allocator is terminating the process
<commit_msg>Disable test/msan/pvalloc.cc on NetBSD<commit_after>// RUN: %clangxx_msan -O0 %s -o %t
// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t m1 2>&1 | FileCheck %s
// RUN: MSAN_OPTIONS=allocator_may_return_null=1     %run %t m1 2>&1
// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t psm1 2>&1 | FileCheck %s
// RUN: MSAN_OPTIONS=allocator_may_return_null=1     %run %t psm1 2>&1

// UNSUPPORTED: win32, freebsd, netbsd

// Checks that pvalloc overflows are caught. If the allocator is allowed to
// return null, the errno should be set to ENOMEM.

#include <assert.h>
#include <errno.h>
#include <malloc.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
  void *p;
  size_t page_size;

  assert(argc == 2);

  page_size = sysconf(_SC_PAGESIZE);
  // Check that the page size is a power of two.
  assert((page_size & (page_size - 1)) == 0);

  if (!strcmp(argv[1], "m1")) {
    p = pvalloc((uintptr_t)-1);
    assert(!p);
    assert(errno == ENOMEM);
  }
  if (!strcmp(argv[1], "psm1")) {
    p = pvalloc((uintptr_t)-(page_size - 1));
    assert(!p);
    assert(errno == ENOMEM);
  }

  return 0;
}

// CHECK: MemorySanitizer's allocator is terminating the process
<|endoftext|>


<commit_before>/*
//////////////////////////////////
// This File has been generated //
// by Expressik light generator //
//  Powered by : Eve CSTB       //
//////////////////////////////////

 * *************************************************************************
 *                                                                         *
 *     STEP Early Classes C++                                              *
 *                                                                         *
 *     Copyright (C) 2009 CSTB                                             *
 *                                                                         *
 *                                                                         *
 *   For further information please contact                                *
 *                                                                         *
 *         eve@cstb.fr                                                     *
 *   or                                                                    *
 *         Mod-Eve, CSTB                                                   *
 *         290, route des Lucioles                                         *
 *         BP 209                                                          *
 *         06904 Sophia Antipolis, France                                  *
 *                                                                         *
 ***************************************************************************
*/

#include "ifc2x3/IfcGeometricRepresentationContext.h"

#include "ifc2x3/CopyOp.h"
#include "ifc2x3/IfcAxis2Placement.h"
#include "ifc2x3/IfcDirection.h"
#include "ifc2x3/IfcGeometricRepresentationSubContext.h"
#include "ifc2x3/IfcRepresentationContext.h"
#include "ifc2x3/Visitor.h"
#include <Step/BaseExpressDataSet.h>
#include <Step/BaseObject.h>
#include <Step/ClassType.h>
#include <Step/Referenced.h>
#include <Step/SPFFunctions.h>
#include <Step/logger.h>
#include <stdlib.h>
#include <string>
#include <vector>

using namespace ifc2x3;

IfcGeometricRepresentationContext::IfcGeometricRepresentationContext(Step::Id id, Step::SPFData *args) : IfcRepresentationContext(id, args) {
    m_coordinateSpaceDimension = Step::getUnset(m_coordinateSpaceDimension);
    m_precision = Step::getUnset(m_precision);
    m_worldCoordinateSystem = NULL;
    m_trueNorth = NULL;
}

IfcGeometricRepresentationContext::~IfcGeometricRepresentationContext() {
}

bool IfcGeometricRepresentationContext::acceptVisitor(Step::BaseVisitor *visitor) {
    return static_cast< Visitor * > (visitor)->visitIfcGeometricRepresentationContext(this);
}

const std::string &IfcGeometricRepresentationContext::type() const {
    return IfcGeometricRepresentationContext::s_type.getName();
}

const Step::ClassType &IfcGeometricRepresentationContext::getClassType() {
    return IfcGeometricRepresentationContext::s_type;
}

const Step::ClassType &IfcGeometricRepresentationContext::getType() const {
    return IfcGeometricRepresentationContext::s_type;
}

bool IfcGeometricRepresentationContext::isOfType(const Step::ClassType &t) const {
    return IfcGeometricRepresentationContext::s_type == t ? true : IfcRepresentationContext::isOfType(t);
}

IfcDimensionCount IfcGeometricRepresentationContext::getCoordinateSpaceDimension() {
    if (Step::BaseObject::inited()) {
        return m_coordinateSpaceDimension;
    }
    else {
        return Step::getUnset(m_coordinateSpaceDimension);
    }
}

const IfcDimensionCount IfcGeometricRepresentationContext::getCoordinateSpaceDimension() const {
    IfcGeometricRepresentationContext * deConstObject = const_cast< IfcGeometricRepresentationContext * > (this);
    return deConstObject->getCoordinateSpaceDimension();
}

void IfcGeometricRepresentationContext::setCoordinateSpaceDimension(IfcDimensionCount value) {
    m_coordinateSpaceDimension = value;
}

void IfcGeometricRepresentationContext::unsetCoordinateSpaceDimension() {
    m_coordinateSpaceDimension = Step::getUnset(getCoordinateSpaceDimension());
}

bool IfcGeometricRepresentationContext::testCoordinateSpaceDimension() const {
    return !Step::isUnset(getCoordinateSpaceDimension());
}

Step::Real IfcGeometricRepresentationContext::getPrecision() {
    if (Step::BaseObject::inited()) {
        return m_precision;
    }
    else {
        return Step::getUnset(m_precision);
    }
}

const Step::Real IfcGeometricRepresentationContext::getPrecision() const {
    IfcGeometricRepresentationContext * deConstObject = const_cast< IfcGeometricRepresentationContext * > (this);
    return deConstObject->getPrecision();
}

void IfcGeometricRepresentationContext::setPrecision(Step::Real value) {
    m_precision = value;
}

void IfcGeometricRepresentationContext::unsetPrecision() {
    m_precision = Step::getUnset(getPrecision());
}

bool IfcGeometricRepresentationContext::testPrecision() const {
    return !Step::isUnset(getPrecision());
}

IfcAxis2Placement *IfcGeometricRepresentationContext::getWorldCoordinateSystem() {
    if (Step::BaseObject::inited()) {
        return m_worldCoordinateSystem.get();
    }
    else {
        return NULL;
    }
}

const IfcAxis2Placement *IfcGeometricRepresentationContext::getWorldCoordinateSystem() const {
    IfcGeometricRepresentationContext * deConstObject = const_cast< IfcGeometricRepresentationContext * > (this);
    return deConstObject->getWorldCoordinateSystem();
}

void IfcGeometricRepresentationContext::setWorldCoordinateSystem(const Step::RefPtr< IfcAxis2Placement > &value) {
    m_worldCoordinateSystem = value;
}

void IfcGeometricRepresentationContext::unsetWorldCoordinateSystem() {
    m_worldCoordinateSystem = Step::getUnset(getWorldCoordinateSystem());
}

bool IfcGeometricRepresentationContext::testWorldCoordinateSystem() const {
    return !Step::isUnset(getWorldCoordinateSystem());
}

IfcDirection *IfcGeometricRepresentationContext::getTrueNorth() {
    if (Step::BaseObject::inited()) {
        return m_trueNorth.get();
    }
    else {
        return NULL;
    }
}

const IfcDirection *IfcGeometricRepresentationContext::getTrueNorth() const {
    IfcGeometricRepresentationContext * deConstObject = const_cast< IfcGeometricRepresentationContext * > (this);
    return deConstObject->getTrueNorth();
}

void IfcGeometricRepresentationContext::setTrueNorth(const Step::RefPtr< IfcDirection > &value) {
    m_trueNorth = value;
}

void IfcGeometricRepresentationContext::unsetTrueNorth() {
    m_trueNorth = Step::getUnset(getTrueNorth());
}

bool IfcGeometricRepresentationContext::testTrueNorth() const {
    return !Step::isUnset(getTrueNorth());
}

Inverse_Set_IfcGeometricRepresentationSubContext_0_n &IfcGeometricRepresentationContext::getHasSubContexts() {
    if (Step::BaseObject::inited()) {
        return m_hasSubContexts;
    }
    else {
        m_hasSubContexts.setUnset(true);
        return m_hasSubContexts;
    }
}

const Inverse_Set_IfcGeometricRepresentationSubContext_0_n &IfcGeometricRepresentationContext::getHasSubContexts() const {
    IfcGeometricRepresentationContext * deConstObject = const_cast< IfcGeometricRepresentationContext * > (this);
    return deConstObject->getHasSubContexts();
}

bool IfcGeometricRepresentationContext::testHasSubContexts() const {
    return !Step::isUnset(getHasSubContexts());
}

bool IfcGeometricRepresentationContext::init() {
    bool status = IfcRepresentationContext::init();
    std::string arg;
    std::vector< Step::Id > *inverses;
    if (!status) {
        return false;
    }
    arg = m_args->getNext();
    if (arg == "$" || arg == "*") {
        m_coordinateSpaceDimension = Step::getUnset(m_coordinateSpaceDimension);
    }
    else {
        m_coordinateSpaceDimension = Step::spfToInteger(arg);
    }
    arg = m_args->getNext();
    if (arg == "$" || arg == "*") {
        m_precision = Step::getUnset(m_precision);
    }
    else {
        m_precision = Step::spfToReal(arg);
    }
    arg = m_args->getNext();
    if (arg == "$" || arg == "*") {
        m_worldCoordinateSystem = NULL;
    }
    else {
        m_worldCoordinateSystem = new IfcAxis2Placement;
        if (arg[0] == '#') {
            m_worldCoordinateSystem->set(m_expressDataSet->get(atoi(arg.c_str() + 1)));
        }
        else if (arg[arg.length() - 1] == ')') {
            std::string type1;
            unsigned int i1;
            i1 = arg.find('(');
            if (i1 != std::string::npos) {
                type1 = arg.substr(0, i1);
                arg = arg.substr(i1 + 1, arg.length() - i1 - 2);
            }
        }
    }
    arg = m_args->getNext();
    if (arg == "$" || arg == "*") {
        m_trueNorth = NULL;
    }
    else {
        m_trueNorth = static_cast< IfcDirection * > (m_expressDataSet->get(Step::getIdParam(arg)));
    }
    inverses = m_args->getInverses(IfcGeometricRepresentationSubContext::getClassType(), 6);
    if (inverses) {
        unsigned int i;
        m_hasSubContexts.setUnset(false);
        for (i = 0; i < inverses->size(); i++) {
            m_hasSubContexts.insert(static_cast< IfcGeometricRepresentationSubContext * > (m_expressDataSet->get((*inverses)[i])));
        }
    }
    return true;
}

void IfcGeometricRepresentationContext::copy(const IfcGeometricRepresentationContext &obj, const CopyOp &copyop) {
    IfcRepresentationContext::copy(obj, copyop);
    setCoordinateSpaceDimension(obj.m_coordinateSpaceDimension);
    setPrecision(obj.m_precision);
    m_worldCoordinateSystem = new IfcAxis2Placement;
    m_worldCoordinateSystem->copy(*(obj.m_worldCoordinateSystem.get()), copyop);
    setTrueNorth((IfcDirection*)copyop(obj.m_trueNorth.get()));
    return;
}

IFC2X3_DLL_DEF Step::ClassType IfcGeometricRepresentationContext::s_type("IfcGeometricRepresentationContext");
<commit_msg>the select attribute should have been allocated<commit_after>/*
//////////////////////////////////
// This File has been generated //
// by Expressik light generator //
//  Powered by : Eve CSTB       //
//////////////////////////////////

 * *************************************************************************
 *                                                                         *
 *     STEP Early Classes C++                                              *
 *                                                                         *
 *     Copyright (C) 2009 CSTB                                             *
 *                                                                         *
 *                                                                         *
 *   For further information please contact                                *
 *                                                                         *
 *         eve@cstb.fr                                                     *
 *   or                                                                    *
 *         Mod-Eve, CSTB                                                   *
 *         290, route des Lucioles                                         *
 *         BP 209                                                          *
 *         06904 Sophia Antipolis, France                                  *
 *                                                                         *
 ***************************************************************************
*/

#include "ifc2x3/IfcGeometricRepresentationContext.h"

#include "ifc2x3/CopyOp.h"
#include "ifc2x3/IfcAxis2Placement.h"
#include "ifc2x3/IfcDirection.h"
#include "ifc2x3/IfcGeometricRepresentationSubContext.h"
#include "ifc2x3/IfcRepresentationContext.h"
#include "ifc2x3/Visitor.h"
#include <Step/BaseExpressDataSet.h>
#include <Step/BaseObject.h>
#include <Step/ClassType.h>
#include <Step/Referenced.h>
#include <Step/SPFFunctions.h>
#include <Step/logger.h>
#include <stdlib.h>
#include <string>
#include <vector>

using namespace ifc2x3;

IfcGeometricRepresentationContext::IfcGeometricRepresentationContext(Step::Id id, Step::SPFData *args) : IfcRepresentationContext(id, args) {
    m_coordinateSpaceDimension = Step::getUnset(m_coordinateSpaceDimension);
    m_precision = Step::getUnset(m_precision);
    m_worldCoordinateSystem = new IfcAxis2Placement;
    m_trueNorth = NULL;
}

IfcGeometricRepresentationContext::~IfcGeometricRepresentationContext() {
}

bool IfcGeometricRepresentationContext::acceptVisitor(Step::BaseVisitor *visitor) {
    return static_cast< Visitor * > (visitor)->visitIfcGeometricRepresentationContext(this);
}

const std::string &IfcGeometricRepresentationContext::type() const {
    return IfcGeometricRepresentationContext::s_type.getName();
}

const Step::ClassType &IfcGeometricRepresentationContext::getClassType() {
    return IfcGeometricRepresentationContext::s_type;
}

const Step::ClassType &IfcGeometricRepresentationContext::getType() const {
    return IfcGeometricRepresentationContext::s_type;
}

bool IfcGeometricRepresentationContext::isOfType(const Step::ClassType &t) const {
    return IfcGeometricRepresentationContext::s_type == t ? true : IfcRepresentationContext::isOfType(t);
}

IfcDimensionCount IfcGeometricRepresentationContext::getCoordinateSpaceDimension() {
    if (Step::BaseObject::inited()) {
        return m_coordinateSpaceDimension;
    }
    else {
        return Step::getUnset(m_coordinateSpaceDimension);
    }
}

const IfcDimensionCount IfcGeometricRepresentationContext::getCoordinateSpaceDimension() const {
    IfcGeometricRepresentationContext * deConstObject = const_cast< IfcGeometricRepresentationContext * > (this);
    return deConstObject->getCoordinateSpaceDimension();
}

void IfcGeometricRepresentationContext::setCoordinateSpaceDimension(IfcDimensionCount value) {
    m_coordinateSpaceDimension = value;
}

void IfcGeometricRepresentationContext::unsetCoordinateSpaceDimension() {
    m_coordinateSpaceDimension = Step::getUnset(getCoordinateSpaceDimension());
}

bool IfcGeometricRepresentationContext::testCoordinateSpaceDimension() const {
    return !Step::isUnset(getCoordinateSpaceDimension());
}

Step::Real IfcGeometricRepresentationContext::getPrecision() {
    if (Step::BaseObject::inited()) {
        return m_precision;
    }
    else {
        return Step::getUnset(m_precision);
    }
}

const Step::Real IfcGeometricRepresentationContext::getPrecision() const {
    IfcGeometricRepresentationContext * deConstObject = const_cast< IfcGeometricRepresentationContext * > (this);
    return deConstObject->getPrecision();
}

void IfcGeometricRepresentationContext::setPrecision(Step::Real value) {
    m_precision = value;
}

void IfcGeometricRepresentationContext::unsetPrecision() {
    m_precision = Step::getUnset(getPrecision());
}

bool IfcGeometricRepresentationContext::testPrecision() const {
    return !Step::isUnset(getPrecision());
}

IfcAxis2Placement *IfcGeometricRepresentationContext::getWorldCoordinateSystem() {
    if (Step::BaseObject::inited()) {
        return m_worldCoordinateSystem.get();
    }
    else {
        return NULL;
    }
}

const IfcAxis2Placement *IfcGeometricRepresentationContext::getWorldCoordinateSystem() const {
    IfcGeometricRepresentationContext * deConstObject = const_cast< IfcGeometricRepresentationContext * > (this);
    return deConstObject->getWorldCoordinateSystem();
}

void IfcGeometricRepresentationContext::setWorldCoordinateSystem(const Step::RefPtr< IfcAxis2Placement > &value) {
    m_worldCoordinateSystem = value;
}

void IfcGeometricRepresentationContext::unsetWorldCoordinateSystem() {
    m_worldCoordinateSystem = Step::getUnset(getWorldCoordinateSystem());
}

bool IfcGeometricRepresentationContext::testWorldCoordinateSystem() const {
    return !Step::isUnset(getWorldCoordinateSystem());
}

IfcDirection *IfcGeometricRepresentationContext::getTrueNorth() {
    if (Step::BaseObject::inited()) {
        return m_trueNorth.get();
    }
    else {
        return NULL;
    }
}

const IfcDirection *IfcGeometricRepresentationContext::getTrueNorth() const {
    IfcGeometricRepresentationContext * deConstObject = const_cast< IfcGeometricRepresentationContext * > (this);
    return deConstObject->getTrueNorth();
}

void IfcGeometricRepresentationContext::setTrueNorth(const Step::RefPtr< IfcDirection > &value) {
    m_trueNorth = value;
}

void IfcGeometricRepresentationContext::unsetTrueNorth() {
    m_trueNorth = Step::getUnset(getTrueNorth());
}

bool IfcGeometricRepresentationContext::testTrueNorth() const {
    return !Step::isUnset(getTrueNorth());
}

Inverse_Set_IfcGeometricRepresentationSubContext_0_n &IfcGeometricRepresentationContext::getHasSubContexts() {
    if (Step::BaseObject::inited()) {
        return m_hasSubContexts;
    }
    else {
        m_hasSubContexts.setUnset(true);
        return m_hasSubContexts;
    }
}

const Inverse_Set_IfcGeometricRepresentationSubContext_0_n &IfcGeometricRepresentationContext::getHasSubContexts() const {
    IfcGeometricRepresentationContext * deConstObject = const_cast< IfcGeometricRepresentationContext * > (this);
    return deConstObject->getHasSubContexts();
}

bool IfcGeometricRepresentationContext::testHasSubContexts() const {
    return !Step::isUnset(getHasSubContexts());
}

bool IfcGeometricRepresentationContext::init() {
    bool status = IfcRepresentationContext::init();
    std::string arg;
    std::vector< Step::Id > *inverses;
    if (!status) {
        return false;
    }
    arg = m_args->getNext();
    if (arg == "$" || arg == "*") {
        m_coordinateSpaceDimension = Step::getUnset(m_coordinateSpaceDimension);
    }
    else {
        m_coordinateSpaceDimension = Step::spfToInteger(arg);
    }
    arg = m_args->getNext();
    if (arg == "$" || arg == "*") {
        m_precision = Step::getUnset(m_precision);
    }
    else {
        m_precision = Step::spfToReal(arg);
    }
    arg = m_args->getNext();
    if (arg == "$" || arg == "*") {
        m_worldCoordinateSystem = NULL;
    }
    else {
        m_worldCoordinateSystem = new IfcAxis2Placement;
        if (arg[0] == '#') {
            m_worldCoordinateSystem->set(m_expressDataSet->get(atoi(arg.c_str() + 1)));
        }
        else if (arg[arg.length() - 1] == ')') {
            std::string type1;
            unsigned int i1;
            i1 = arg.find('(');
            if (i1 != std::string::npos) {
                type1 = arg.substr(0, i1);
                arg = arg.substr(i1 + 1, arg.length() - i1 - 2);
            }
        }
    }
    arg = m_args->getNext();
    if (arg == "$" || arg == "*") {
        m_trueNorth = NULL;
    }
    else {
        m_trueNorth = static_cast< IfcDirection * > (m_expressDataSet->get(Step::getIdParam(arg)));
    }
    inverses = m_args->getInverses(IfcGeometricRepresentationSubContext::getClassType(), 6);
    if (inverses) {
        unsigned int i;
        m_hasSubContexts.setUnset(false);
        for (i = 0; i < inverses->size(); i++) {
            m_hasSubContexts.insert(static_cast< IfcGeometricRepresentationSubContext * > (m_expressDataSet->get((*inverses)[i])));
        }
    }
    return true;
}

void IfcGeometricRepresentationContext::copy(const IfcGeometricRepresentationContext &obj, const CopyOp &copyop) {
    IfcRepresentationContext::copy(obj, copyop);
    setCoordinateSpaceDimension(obj.m_coordinateSpaceDimension);
    setPrecision(obj.m_precision);
    m_worldCoordinateSystem = new IfcAxis2Placement;
    m_worldCoordinateSystem->copy(*(obj.m_worldCoordinateSystem.get()), copyop);
    setTrueNorth((IfcDirection*)copyop(obj.m_trueNorth.get()));
    return;
}

IFC2X3_DLL_DEF Step::ClassType IfcGeometricRepresentationContext::s_type("IfcGeometricRepresentationContext");
<|endoftext|>


<commit_before>
#include "gtest/gtest.h"

#include <TFile.h>
#include <TMath.h>
#include <TTree.h>
#include <TString.h>
#include <TSystem.h>

#include "TMVA/CrossValidation.h"
#include "TMVA/DataLoader.h"
#include "TMVA/Reader.h"
#include "TMVA/Tools.h"

#include <vector>

constexpr UInt_t NUM_FOLDS = 2;
constexpr UInt_t NUM_EVENTS = 100;
constexpr UInt_t NUM_EVENTS_SIG = NUM_EVENTS / 2;
constexpr UInt_t NUM_EVENTS_BKG = NUM_EVENTS - NUM_EVENTS_SIG;

/**
 * Generates two slightly offset gaussians and returns a non-owning pointer
 * to a TTree.
 */
TTree *genTree(Int_t nPoints, Double_t offset, Double_t scale = 0.3, UInt_t seed = 100)
{
   TRandom3 rng(seed);
   Float_t x = 0;
   Float_t y = 0;
   UInt_t id = 0;

   auto data = new TTree();
   data->Branch("x", &x, "x/F");
   data->Branch("y", &y, "y/F");
   data->Branch("EventNumber", &id, "EventNumber/I");

   for (Int_t n = 0; n < nPoints; ++n) {
      x = rng.Gaus(offset, scale);
      y = rng.Gaus(offset, scale);
      data->Fill();
      ++id;
   }

   // Important: Disconnects the tree from the memory locations of x and y.
   data->ResetBranchAddresses();
   return data;
}

/**
 * Trains a TMVA method using the specified number of worker processes.
 * @param  numWorkers Number of processes to use for parallel fold execution.
 * @return            The path to the weight file where the method was stored
 *                    and the time it took to execute the evaluation, exluding
 *                    data generation, in seconds.
 */
std::pair<std::string, double> runCrossValidation(UInt_t numWorkers)
{
   using clock_t = std::chrono::high_resolution_clock;
   using milli_t = std::chrono::duration<double, std::milli>;

   TTree *sigTree = genTree(NUM_EVENTS_SIG, 0.3, 0.3, 100);
   TTree *bkgTree = genTree(NUM_EVENTS_BKG, 0.3, 0.3, 101);

   auto *dataloader = new TMVA::DataLoader("cv-multiproc");
   dataloader->AddSignalTree(sigTree);
   dataloader->AddBackgroundTree(bkgTree);

   dataloader->AddVariable("x", 'D');
   dataloader->AddVariable("y", 'D');
   dataloader->AddSpectator("EventNumber", 'I');

   TString dataloaderOptions = Form("SplitMode=Block:nTrain_Signal=%i"
                                    ":nTrain_Background=%i:!V",
                                    NUM_EVENTS_SIG, NUM_EVENTS_BKG);
   dataloader->PrepareTrainingAndTestTree("", dataloaderOptions);

   // TMVA::CrossValidation takes ownership of dataloader
   std::string splitExpr = "int([EventNumber])%int([NumFolds]";
   TMVA::CrossValidation cv{Form("%i-proc", numWorkers), dataloader,
                            Form("Silent:AnalysisType=Classification"
                                 ":NumWorkerProcs=%i:NumFolds=%i"
                                 ":SplitExpr=%s)",
                                 numWorkers, NUM_FOLDS, splitExpr.c_str())};

   cv.BookMethod(TMVA::Types::kBDT, "BDT", "!H:!V:NTrees=100:MaxDepth=3");

   auto StartTime = clock_t::now();
   cv.Evaluate();
   auto EndTime = clock_t::now();
   double duration = milli_t(EndTime - StartTime).count() / 1000.0;

   delete sigTree;
   delete bkgTree;

   std::string dsname{dataloader->GetName()};
   std::string cvname{cv.GetName()};
   std::string weightPath = dsname + "/weights/" + cvname + "_BDT.weights.xml";
   return std::make_pair(weightPath, duration);
}

/**
 * Verify that the two methods generate the same output when presented the same
 * input.
 * @param methodA Path to a method weight file.
 * @param methodB Another path to method weight file.
 */
void verify(std::string methodA, std::string methodB)
{
   TMVA::Reader reader;
   Float_t x;
   Float_t y;
   Float_t id;

   reader.AddVariable("x", &x);
   reader.AddVariable("y", &y);
   reader.AddSpectator("EventNumber", &id);

   reader.BookMVA("BDT1", methodA.c_str());
   reader.BookMVA("BDT2", methodB.c_str());

   TTree *tree = genTree(NUM_EVENTS, 0.3, 0.3, 200);

   for (Long64_t ievt = 0; ievt < NUM_EVENTS; ievt++) {
      tree->GetEntry(ievt);

      Float_t valA = reader.EvaluateMVA("BDT1");
      Float_t valB = reader.EvaluateMVA("BDT2");

      ASSERT_EQ(valA, valB) << "Output when using a single proces and multiple"
                               " processes differ! >:(";
   }
}

TEST(CrossValidationMultiprocess, EqualOutputTo)
{
   // Generate two methods, one created with a single process, the other in
   // parallel using TProcessExecutor.
   auto p1 = runCrossValidation(1);
   auto p2 = runCrossValidation(2);

   // Print duration
   double duration1 = std::get<1>(p1);
   double duration2 = std::get<1>(p2);
   std::cout << "CV with 1 process took  : " << duration1 << std::endl;
   std::cout << "CV with 2 processes took: " << duration2 << std::endl;

   // Verify that the two models generate the same output given the same input.
   std::string weightPath1 = std::get<0>(p1);
   std::string weightPath2 = std::get<0>(p2);
   verify(weightPath1, weightPath2);
}<commit_msg>[TMVA] CV - Fix warnings<commit_after>
#include "gtest/gtest.h"

#include <TFile.h>
#include <TMath.h>
#include <TTree.h>
#include <TString.h>
#include <TSystem.h>

#include "TMVA/CrossValidation.h"
#include "TMVA/DataLoader.h"
#include "TMVA/Reader.h"
#include "TMVA/Tools.h"

#include <vector>

constexpr UInt_t NUM_FOLDS = 2;
constexpr UInt_t NUM_EVENTS = 100;
constexpr UInt_t NUM_EVENTS_SIG = NUM_EVENTS / 2;
constexpr UInt_t NUM_EVENTS_BKG = NUM_EVENTS - NUM_EVENTS_SIG;

/**
 * Generates two slightly offset gaussians and returns a non-owning pointer
 * to a TTree.
 */
TTree *genTree(Int_t nPoints, Double_t offset, Double_t scale = 0.3, UInt_t seed = 100)
{
   TRandom3 rng(seed);
   Float_t x = 0;
   Float_t y = 0;
   UInt_t id = 0;

   auto data = new TTree();
   data->Branch("x", &x, "x/F");
   data->Branch("y", &y, "y/F");
   data->Branch("EventNumber", &id, "EventNumber/I");

   for (Int_t n = 0; n < nPoints; ++n) {
      x = rng.Gaus(offset, scale);
      y = rng.Gaus(offset, scale);
      data->Fill();
      ++id;
   }

   // Important: Disconnects the tree from the memory locations of x and y.
   data->ResetBranchAddresses();
   return data;
}

/**
 * Trains a TMVA method using the specified number of worker processes.
 * @param  numWorkers Number of processes to use for parallel fold execution.
 * @return            The path to the weight file where the method was stored
 *                    and the time it took to execute the evaluation, exluding
 *                    data generation, in seconds.
 */
std::pair<std::string, double> runCrossValidation(UInt_t numWorkers)
{
   using cv_clock_t = std::chrono::high_resolution_clock;
   using cv_milli_t = std::chrono::duration<double, std::milli>;

   TTree *sigTree = genTree(NUM_EVENTS_SIG, 0.3, 0.3, 100);
   TTree *bkgTree = genTree(NUM_EVENTS_BKG, 0.3, 0.3, 101);

   auto *dataloader = new TMVA::DataLoader("cv-multiproc");
   dataloader->AddSignalTree(sigTree);
   dataloader->AddBackgroundTree(bkgTree);

   dataloader->AddVariable("x", 'D');
   dataloader->AddVariable("y", 'D');
   dataloader->AddSpectator("EventNumber", 'I');

   TString dataloaderOptions = Form("SplitMode=Block:nTrain_Signal=%i"
                                    ":nTrain_Background=%i:!V",
                                    NUM_EVENTS_SIG, NUM_EVENTS_BKG);
   dataloader->PrepareTrainingAndTestTree("", dataloaderOptions);

   // TMVA::CrossValidation takes ownership of dataloader
   std::string splitExpr = "int([EventNumber])%int([NumFolds]";
   TMVA::CrossValidation cv{Form("%i-proc", numWorkers), dataloader,
                            Form("Silent:AnalysisType=Classification"
                                 ":NumWorkerProcs=%i:NumFolds=%i"
                                 ":SplitExpr=%s)",
                                 numWorkers, NUM_FOLDS, splitExpr.c_str())};

   cv.BookMethod(TMVA::Types::kBDT, "BDT", "!H:!V:NTrees=100:MaxDepth=3");

   auto StartTime = cv_clock_t::now();
   cv.Evaluate();
   auto EndTime = cv_clock_t::now();
   double duration = cv_milli_t(EndTime - StartTime).count() / 1000.0;

   delete sigTree;
   delete bkgTree;

   std::string dsname{dataloader->GetName()};
   std::string cvname{cv.GetName()};
   std::string weightPath = dsname + "/weights/" + cvname + "_BDT.weights.xml";
   return std::make_pair(weightPath, duration);
}

/**
 * Verify that the two methods generate the same output when presented the same
 * input.
 * @param methodA Path to a method weight file.
 * @param methodB Another path to method weight file.
 */
void verify(std::string methodA, std::string methodB)
{
   TMVA::Reader reader;
   Float_t x;
   Float_t y;
   Float_t id;

   reader.AddVariable("x", &x);
   reader.AddVariable("y", &y);
   reader.AddSpectator("EventNumber", &id);

   reader.BookMVA("BDT1", methodA.c_str());
   reader.BookMVA("BDT2", methodB.c_str());

   TTree *tree = genTree(NUM_EVENTS, 0.3, 0.3, 200);

   for (Long64_t ievt = 0; ievt < NUM_EVENTS; ievt++) {
      tree->GetEntry(ievt);

      Float_t valA = reader.EvaluateMVA("BDT1");
      Float_t valB = reader.EvaluateMVA("BDT2");

      ASSERT_EQ(valA, valB) << "Output when using a single process and multiple"
                               " processes differ! >:(";
   }
}

TEST(CrossValidationMultiprocess, EqualOutputTo)
{
   // Generate two methods, one created with a single process, the other in
   // parallel using TProcessExecutor.
   auto p1 = runCrossValidation(1);
   auto p2 = runCrossValidation(2);

   // Print duration
   double duration1 = std::get<1>(p1);
   double duration2 = std::get<1>(p2);
   std::cout << "CV with 1 process took  : " << duration1 << std::endl;
   std::cout << "CV with 2 processes took: " << duration2 << std::endl;

   // Verify that the two models generate the same output given the same input.
   std::string weightPath1 = std::get<0>(p1);
   std::string weightPath2 = std::get<0>(p2);
   verify(weightPath1, weightPath2);
}<|endoftext|>