<commit_before>/**
 * \file
 * \brief SortedContainer class header
 *
 * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-11-27
 */

#ifndef INCLUDE_DISTORTOS_INTERNAL_CONTAINERS_SORTEDCONTAINER_HPP_
#define INCLUDE_DISTORTOS_INTERNAL_CONTAINERS_SORTEDCONTAINER_HPP_

#include <algorithm>

namespace distortos
{

namespace internal
{

/**
 * \brief SortedContainerBase class is a wrapper around Container which can be used as base of SortedContainer
 *
 * \param Container is the underlying container, it must provide following functions: begin(), emplace(), empty(),
 * end(), pop_front() and splice(). It must contain following types: allocator_type, const_iterator, iterator and
 * value_type. Optionally functions erase() and size() of Container are forwarded if they exist.
 */

template<typename Container>
class SortedContainerBase
{
public:

	/// iterator
	using iterator = typename Container::iterator;

	/// const_iterator
	using const_iterator = typename Container::const_iterator;

	/// value_type
	using value_type = typename Container::value_type;

	/// allocator_type
	using allocator_type = typename Container::allocator_type;

	/**
	 * \brief Primary template used to test presence of C::splice()
	 *
	 * \param C is the type of container that will be tested
	 * \param Signature is the signature of C::splice() that will be tested
	 */

	template<typename C, typename Signature>
	struct TestHasSplice;

	/**
	 * \brief Template used to test presence of R C::splice(Args...)
	 *
	 * \param C is the type of container that will be tested
	 * \param R is the type returned by tested C::splice()
	 * \param Args are the types of arguments for tested C::splice()
	 */

	template<typename C, typename R, typename... Args>
	struct TestHasSplice<C, R(Args...)>
	{
		/**
		* \brief Overload selected when R C::splice(Args...) function is present.
		*
		* \return std::true_type
		*/

		template<typename CC = C>
		constexpr static auto test(CC* p) ->
				typename std::is_same<decltype(p->splice(std::declval<Args>()...)), R>::type;

		/**
		* \brief Overload selected when R C::splice(Args...) function is not present.
		*
		* \return std::false_type
		*/

		constexpr static auto test(...) -> std::false_type;
	};

	/**
	 * \brief Predicate telling whether void Container::splice(iterator, Container&, iterator) function is present (it
	 * inherits from std::true_type in that case) or not (inherits from std::false_type).
	 *
	 * \note Correct prototype should be void Container::splice(const_iterator, Container&, const_iterator),
	 * but libstdc++v3 used \a iterator instead of \a const_iterator until release of GCC 4.9. The prototype used below
	 * works in any case, as \a iterator is convertible to \a const_iterator, but not the other way around.
	 */

	using HasSplice = decltype(TestHasSplice<Container, void(iterator, Container&, iterator)>::
			test(std::declval<Container*>()));

	/**
	 * \brief SortedContainerBase's constructor
	 *
	 * \param [in] allocator is a reference to allocator_type object used to copy-construct allocator of container
	 */

	constexpr explicit SortedContainerBase(const allocator_type& allocator = allocator_type{}) :
			container_{allocator}
	{

	}

	/**
	 * \brief Forwarding of Container::begin()
	 */

	decltype(std::declval<Container>().begin()) begin()
	{
		return container_.begin();
	}

	/**
	 * \brief Forwarding of Container::begin() const
	 */

	decltype(std::declval<const Container>().begin()) begin() const
	{
		return container_.begin();
	}

	/**
	 * \brief Forwarding of Container::empty() const
	 */

	decltype(std::declval<Container>().empty()) empty() const
	{
		return container_.empty();
	}

	/**
	 * \brief Forwarding of Container::end()
	 */

	decltype(std::declval<Container>().end()) end()
	{
		return container_.end();
	}

	/**
	 * \brief Forwarding of Container::end() const
	 */

	decltype(std::declval<const Container>().end()) end() const
	{
		return container_.end();
	}

	/**
	 * \brief Forwarding of Container::erase(...)
	 *
	 * \note forwarded only if Container::erase(Args&&...) exists
	 */

	template<typename... Args, typename C = Container>
	auto erase(Args&&... args) -> decltype(std::declval<C>().erase(std::forward<Args>(args)...))
	{
		return container_.erase(std::forward<Args>(args)...);
	}

	/**
	 * \brief Forwarding of Container::pop_front()
	 */

	decltype(std::declval<Container>().pop_front()) pop_front()
	{
		return container_.pop_front();
	}

	/**
	 * \brief Forwarding of Container::size() const
	 *
	 * \note forwarded only if Container::size() const exists
	 */

	template<typename C = Container>
	decltype(std::declval<const C>().size()) size() const
	{
		return container_.size();
	}

protected:

	/// container used for keeping elements
	Container container_;
};

/**
 * \brief SortedContainer class is a container that keeps the elements sorted during emplace of transfer.
 *
 * \note The elements are sorted as long as the user does not modify the contents via iterators.
 *
 * \param Container is the underlying container, it must provide following functions: begin(), emplace(), empty(),
 * end(), pop_front() and splice(). It must contain following types: allocator_type, const_iterator, iterator and
 * value_type. Optionally functions erase() and size() of Container are forwarded if they exist.
 * \param Compare is a type of functor used for comparison, std::less results in descending order, std::greater - in
 * ascending order.
 * \param HasSplice selects implementation - std::true_type to use Container::emplace() and Container::splice();
 * std::false_type to use Container::emplace_front(), Container::splice_after() and Container::before_begin()
 */

template<typename Container, typename Compare, typename HasSplice = typename SortedContainerBase<Container>::HasSplice>
class SortedContainer : public SortedContainerBase<Container>
{
public:

	/// base of SortedContainer
	using Base = SortedContainerBase<Container>;

	/// import iterator type alias from Base
	using typename Base::iterator;

	/// import const_iterator type alias from Base
	using typename Base::const_iterator;

	/// import value_type type alias from Base
	using typename Base::value_type;

	/// import allocator_type type alias from Base
	using typename Base::allocator_type;

	/// import Base's constructor
	using Base::Base;

	/**
	 * \brief SortedContainer's constructor
	 *
	 * \param [in] compare is a reference to Compare object used to copy-construct comparison functor
	 * \param [in] allocator is a reference to allocator_type object used to copy-construct allocator of container
	 */

	constexpr explicit SortedContainer(const Compare& compare = Compare{},
			const allocator_type& allocator = allocator_type{}) :
			Base{allocator},
			compare_{compare}
	{

	}

	/**
	 * \brief Sorted emplace()
	 *
	 * \param Args are types of argument for value_type constructor
	 *
	 * \param [in] args are arguments for value_type constructor
	 *
	 * \return iterator to emplaced element
	 */

	template<typename... Args>
	iterator sortedEmplace(Args&&... args)
	{
		const auto it = Base::container_.emplace(Base::begin(), std::forward<Args>(args)...);
		sortedSplice(*this, it);
		return it;
	}

	/**
	 * \brief Sorted splice()
	 *
	 * \param [in] other is the container from which the object is transfered
	 * \param [in] otherPosition is the position of the transfered object in the other container
	 */

	void sortedSplice(SortedContainer& other, const iterator otherPosition)
	{
		const auto insertPosition = findInsertPosition(*otherPosition);
		Base::container_.splice(insertPosition, other.container_, otherPosition);
	}

private:

	/**
	 * \brief Finds insert position that satisfies sorting criteria.
	 *
	 * Finds the insert position where Compare of current element and provided value returns true.
	 *
	 * \param [in] value is the value that is going to be emplaced/transfered
	 */

	iterator findInsertPosition(const value_type& value)
	{
		return std::find_if(Base::begin(), Base::end(),
				[this, &value](const value_type& element) -> bool
				{
					return compare_(element, value);
				}
		);
	}

	/// instance of functor used for comparison
	Compare compare_;
};

/**
 * \brief SortedContainer specialization for Containers that don't have Container::emplace() and Container::splice() -
 * like std::forward_list. In this case Container::emplace_front(), Container::splice_after() and
 * Container::before_begin() are used.
 */

template<typename Container, typename Compare>
class SortedContainer<Container, Compare, std::false_type> : public SortedContainerBase<Container>
{
public:

	/// base of SortedContainer
	using Base = SortedContainerBase<Container>;

	/// import iterator type alias from Base
	using typename Base::iterator;

	/// import const_iterator type alias from Base
	using typename Base::const_iterator;

	/// import value_type type alias from Base
	using typename Base::value_type;

	/// import allocator_type type alias from Base
	using typename Base::allocator_type;

	/// import Base's constructor
	using Base::Base;

	/**
	 * \brief SortedContainer's constructor
	 *
	 * \param [in] compare is a reference to Compare object used to copy-construct comparison functor
	 * \param [in] allocator is a reference to allocator_type object used to copy-construct allocator of container
	 */

	constexpr explicit SortedContainer(const Compare& compare = Compare{},
			const allocator_type& allocator = allocator_type{}) :
			Base{allocator},
			compare_{compare}
	{

	}

	/**
	 * \brief Sorted emplace()
	 *
	 * \param Args are types of argument for value_type constructor
	 *
	 * \param [in] args are arguments for value_type constructor
	 *
	 * \return iterator to emplaced element
	 */

	template<typename... Args>
	iterator sortedEmplace(Args&&... args)
	{
		Base::container_.emplace_front(std::forward<Args>(args)...);
		const auto it = Base::container_.begin();
		sortedSpliceAfter(*this, Base::container_.before_begin());
		return it;
	}

	/**
	 * \brief Sorted splice_after()
	 *
	 * \param [in] other is the container from which the object is transfered
	 * \param [in] otherPositionBefore is the position before the transfered object in the other container
	 */

	void sortedSpliceAfter(SortedContainer& other, const iterator otherPositionBefore)
	{
		const auto next = std::next(otherPositionBefore);
		const auto insertPositionBefore = findInsertPositionBefore(*next);
		Base::container_.splice_after(insertPositionBefore, other.container_, otherPositionBefore);
	}

private:

	/**
	 * \brief Finds insert position "before" the position that satisfies sorting criteria.
	 *
	 * Finds the insert position "before" the position where Compare of current element and provided value returns true.
	 *
	 * \param [in] value is the value that is going to be emplaced/transfered
	 */

	iterator findInsertPositionBefore(const value_type& value)
	{
		auto it = Base::container_.before_begin();
		auto next = Base::container_.begin();
		const auto last = Base::container_.end();

		while (next != last)
		{
			if (compare_(*next, value) == true)
				return it;

			it = next;
			++next;
		}
		return it;
	}

	/// instance of functor used for comparison
	Compare compare_;
};

}	// namespace internal

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_INTERNAL_CONTAINERS_SORTEDCONTAINER_HPP_
<commit_msg>remove SortedContainer::size()<commit_after>/**
 * \file
 * \brief SortedContainer class header
 *
 * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-12-01
 */

#ifndef INCLUDE_DISTORTOS_INTERNAL_CONTAINERS_SORTEDCONTAINER_HPP_
#define INCLUDE_DISTORTOS_INTERNAL_CONTAINERS_SORTEDCONTAINER_HPP_

#include <algorithm>

namespace distortos
{

namespace internal
{

/**
 * \brief SortedContainerBase class is a wrapper around Container which can be used as base of SortedContainer
 *
 * \param Container is the underlying container, it must provide following functions: begin(), emplace(), empty(),
 * end(), pop_front() and splice(). It must contain following types: allocator_type, const_iterator, iterator and
 * value_type. Optionally functions erase() and size() of Container are forwarded if they exist.
 */

template<typename Container>
class SortedContainerBase
{
public:

	/// iterator
	using iterator = typename Container::iterator;

	/// const_iterator
	using const_iterator = typename Container::const_iterator;

	/// value_type
	using value_type = typename Container::value_type;

	/// allocator_type
	using allocator_type = typename Container::allocator_type;

	/**
	 * \brief Primary template used to test presence of C::splice()
	 *
	 * \param C is the type of container that will be tested
	 * \param Signature is the signature of C::splice() that will be tested
	 */

	template<typename C, typename Signature>
	struct TestHasSplice;

	/**
	 * \brief Template used to test presence of R C::splice(Args...)
	 *
	 * \param C is the type of container that will be tested
	 * \param R is the type returned by tested C::splice()
	 * \param Args are the types of arguments for tested C::splice()
	 */

	template<typename C, typename R, typename... Args>
	struct TestHasSplice<C, R(Args...)>
	{
		/**
		* \brief Overload selected when R C::splice(Args...) function is present.
		*
		* \return std::true_type
		*/

		template<typename CC = C>
		constexpr static auto test(CC* p) ->
				typename std::is_same<decltype(p->splice(std::declval<Args>()...)), R>::type;

		/**
		* \brief Overload selected when R C::splice(Args...) function is not present.
		*
		* \return std::false_type
		*/

		constexpr static auto test(...) -> std::false_type;
	};

	/**
	 * \brief Predicate telling whether void Container::splice(iterator, Container&, iterator) function is present (it
	 * inherits from std::true_type in that case) or not (inherits from std::false_type).
	 *
	 * \note Correct prototype should be void Container::splice(const_iterator, Container&, const_iterator),
	 * but libstdc++v3 used \a iterator instead of \a const_iterator until release of GCC 4.9. The prototype used below
	 * works in any case, as \a iterator is convertible to \a const_iterator, but not the other way around.
	 */

	using HasSplice = decltype(TestHasSplice<Container, void(iterator, Container&, iterator)>::
			test(std::declval<Container*>()));

	/**
	 * \brief SortedContainerBase's constructor
	 *
	 * \param [in] allocator is a reference to allocator_type object used to copy-construct allocator of container
	 */

	constexpr explicit SortedContainerBase(const allocator_type& allocator = allocator_type{}) :
			container_{allocator}
	{

	}

	/**
	 * \brief Forwarding of Container::begin()
	 */

	decltype(std::declval<Container>().begin()) begin()
	{
		return container_.begin();
	}

	/**
	 * \brief Forwarding of Container::begin() const
	 */

	decltype(std::declval<const Container>().begin()) begin() const
	{
		return container_.begin();
	}

	/**
	 * \brief Forwarding of Container::empty() const
	 */

	decltype(std::declval<Container>().empty()) empty() const
	{
		return container_.empty();
	}

	/**
	 * \brief Forwarding of Container::end()
	 */

	decltype(std::declval<Container>().end()) end()
	{
		return container_.end();
	}

	/**
	 * \brief Forwarding of Container::end() const
	 */

	decltype(std::declval<const Container>().end()) end() const
	{
		return container_.end();
	}

	/**
	 * \brief Forwarding of Container::erase(...)
	 *
	 * \note forwarded only if Container::erase(Args&&...) exists
	 */

	template<typename... Args, typename C = Container>
	auto erase(Args&&... args) -> decltype(std::declval<C>().erase(std::forward<Args>(args)...))
	{
		return container_.erase(std::forward<Args>(args)...);
	}

	/**
	 * \brief Forwarding of Container::pop_front()
	 */

	decltype(std::declval<Container>().pop_front()) pop_front()
	{
		return container_.pop_front();
	}

protected:

	/// container used for keeping elements
	Container container_;
};

/**
 * \brief SortedContainer class is a container that keeps the elements sorted during emplace of transfer.
 *
 * \note The elements are sorted as long as the user does not modify the contents via iterators.
 *
 * \param Container is the underlying container, it must provide following functions: begin(), emplace(), empty(),
 * end(), pop_front() and splice(). It must contain following types: allocator_type, const_iterator, iterator and
 * value_type. Optionally functions erase() and size() of Container are forwarded if they exist.
 * \param Compare is a type of functor used for comparison, std::less results in descending order, std::greater - in
 * ascending order.
 * \param HasSplice selects implementation - std::true_type to use Container::emplace() and Container::splice();
 * std::false_type to use Container::emplace_front(), Container::splice_after() and Container::before_begin()
 */

template<typename Container, typename Compare, typename HasSplice = typename SortedContainerBase<Container>::HasSplice>
class SortedContainer : public SortedContainerBase<Container>
{
public:

	/// base of SortedContainer
	using Base = SortedContainerBase<Container>;

	/// import iterator type alias from Base
	using typename Base::iterator;

	/// import const_iterator type alias from Base
	using typename Base::const_iterator;

	/// import value_type type alias from Base
	using typename Base::value_type;

	/// import allocator_type type alias from Base
	using typename Base::allocator_type;

	/// import Base's constructor
	using Base::Base;

	/**
	 * \brief SortedContainer's constructor
	 *
	 * \param [in] compare is a reference to Compare object used to copy-construct comparison functor
	 * \param [in] allocator is a reference to allocator_type object used to copy-construct allocator of container
	 */

	constexpr explicit SortedContainer(const Compare& compare = Compare{},
			const allocator_type& allocator = allocator_type{}) :
			Base{allocator},
			compare_{compare}
	{

	}

	/**
	 * \brief Sorted emplace()
	 *
	 * \param Args are types of argument for value_type constructor
	 *
	 * \param [in] args are arguments for value_type constructor
	 *
	 * \return iterator to emplaced element
	 */

	template<typename... Args>
	iterator sortedEmplace(Args&&... args)
	{
		const auto it = Base::container_.emplace(Base::begin(), std::forward<Args>(args)...);
		sortedSplice(*this, it);
		return it;
	}

	/**
	 * \brief Sorted splice()
	 *
	 * \param [in] other is the container from which the object is transfered
	 * \param [in] otherPosition is the position of the transfered object in the other container
	 */

	void sortedSplice(SortedContainer& other, const iterator otherPosition)
	{
		const auto insertPosition = findInsertPosition(*otherPosition);
		Base::container_.splice(insertPosition, other.container_, otherPosition);
	}

private:

	/**
	 * \brief Finds insert position that satisfies sorting criteria.
	 *
	 * Finds the insert position where Compare of current element and provided value returns true.
	 *
	 * \param [in] value is the value that is going to be emplaced/transfered
	 */

	iterator findInsertPosition(const value_type& value)
	{
		return std::find_if(Base::begin(), Base::end(),
				[this, &value](const value_type& element) -> bool
				{
					return compare_(element, value);
				}
		);
	}

	/// instance of functor used for comparison
	Compare compare_;
};

/**
 * \brief SortedContainer specialization for Containers that don't have Container::emplace() and Container::splice() -
 * like std::forward_list. In this case Container::emplace_front(), Container::splice_after() and
 * Container::before_begin() are used.
 */

template<typename Container, typename Compare>
class SortedContainer<Container, Compare, std::false_type> : public SortedContainerBase<Container>
{
public:

	/// base of SortedContainer
	using Base = SortedContainerBase<Container>;

	/// import iterator type alias from Base
	using typename Base::iterator;

	/// import const_iterator type alias from Base
	using typename Base::const_iterator;

	/// import value_type type alias from Base
	using typename Base::value_type;

	/// import allocator_type type alias from Base
	using typename Base::allocator_type;

	/// import Base's constructor
	using Base::Base;

	/**
	 * \brief SortedContainer's constructor
	 *
	 * \param [in] compare is a reference to Compare object used to copy-construct comparison functor
	 * \param [in] allocator is a reference to allocator_type object used to copy-construct allocator of container
	 */

	constexpr explicit SortedContainer(const Compare& compare = Compare{},
			const allocator_type& allocator = allocator_type{}) :
			Base{allocator},
			compare_{compare}
	{

	}

	/**
	 * \brief Sorted emplace()
	 *
	 * \param Args are types of argument for value_type constructor
	 *
	 * \param [in] args are arguments for value_type constructor
	 *
	 * \return iterator to emplaced element
	 */

	template<typename... Args>
	iterator sortedEmplace(Args&&... args)
	{
		Base::container_.emplace_front(std::forward<Args>(args)...);
		const auto it = Base::container_.begin();
		sortedSpliceAfter(*this, Base::container_.before_begin());
		return it;
	}

	/**
	 * \brief Sorted splice_after()
	 *
	 * \param [in] other is the container from which the object is transfered
	 * \param [in] otherPositionBefore is the position before the transfered object in the other container
	 */

	void sortedSpliceAfter(SortedContainer& other, const iterator otherPositionBefore)
	{
		const auto next = std::next(otherPositionBefore);
		const auto insertPositionBefore = findInsertPositionBefore(*next);
		Base::container_.splice_after(insertPositionBefore, other.container_, otherPositionBefore);
	}

private:

	/**
	 * \brief Finds insert position "before" the position that satisfies sorting criteria.
	 *
	 * Finds the insert position "before" the position where Compare of current element and provided value returns true.
	 *
	 * \param [in] value is the value that is going to be emplaced/transfered
	 */

	iterator findInsertPositionBefore(const value_type& value)
	{
		auto it = Base::container_.before_begin();
		auto next = Base::container_.begin();
		const auto last = Base::container_.end();

		while (next != last)
		{
			if (compare_(*next, value) == true)
				return it;

			it = next;
			++next;
		}
		return it;
	}

	/// instance of functor used for comparison
	Compare compare_;
};

}	// namespace internal

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_INTERNAL_CONTAINERS_SORTEDCONTAINER_HPP_
<|endoftext|>


<commit_before>// -*- Mode:C++ -*-

/**************************************************************************************************/
/*                                                                                */
/* Copyright (C) 2016 University of Hull                                                          */
/*                                                                                */
/**************************************************************************************************/
/*                                                                                */
/*  module     :  hugh/field/container.cpp                                                        */
/*  project    :                                                                                */
/*  description:                                                                                */
/*                                                                                */
/**************************************************************************************************/

// include i/f header

#include "hugh/field/container.hpp"

// includes, system

#include <algorithm>              // std::find<>
#include <boost/io/ios_state.hpp> // boost::io::ios_all_saver
#include <ostream>                // std::ostream
#include <stdexcept>              // std::logic_error
#include <typeinfo>               // typeid

// includes, project

#include <hugh/field/base.hpp>
#include <hugh/support/io_utils.hpp>
#include <hugh/support/thread.hpp>
#include <hugh/support/type_info.hpp>

#define HUGH_USE_TRACE
#undef HUGH_USE_TRACE
#include <hugh/support/trace.hpp>

// internal unnamed namespace

namespace {

  // types, internal (class, enum, struct, union, typedef)

  class dflt_cntnr_mgr : public hugh::field::container::manager {

    using lock_type  = hugh::support::recursive_lock;
    using lock_guard = hugh::support::recursive_lock_guard;
    using inherited  = hugh::field::container::manager;

  public:

    explicit dflt_cntnr_mgr()
      : inherited           (),
        container_list_lock_()
    {
      TRACE("hugh::field::container::<unnamed>::dflt_cntnr_mgr::dflt_cntnr_mgr");
    }

    virtual ~dflt_cntnr_mgr()
    {
      TRACE("hugh::field::container::<unnamed>::dflt_cntnr_mgr::~dflt_cntnr_mgr");
    }

    virtual void print_on(std::ostream& os) const
    {
      TRACE_NEVER("hugh::field::container::<unnamed>::dflt_cntnr_mgr::print_on");

      lock_guard const lg(container_list_lock_);

      inherited::print_on(os);
    }

    virtual void evaluate()
    {
      TRACE("hugh::field::container::<unnamed>::dflt_cntnr_mgr::evaluate");

      lock_guard const lg(container_list_lock_);

      return inherited::evaluate();
    }

  protected:

    virtual bool schedule(hugh::field::container* a)
    {
      TRACE("hugh::field::container::<unnamed>::dflt_cntnr_mgr::schedule");

      lock_guard const lg(container_list_lock_);

      return inherited::schedule(a);
    }

    virtual bool unschedule(hugh::field::container* a)
    {
      TRACE("hugh::field::container::<unnamed>::dflt_cntnr_mgr::unschedule");

      lock_guard const lg(container_list_lock_);

      return inherited::unschedule(a);
    }

  private:

    mutable lock_type container_list_lock_;

  };

  // variables, internal

  // functions, internal

} // namespace {

namespace hugh {

  namespace field {

    // variables, exported

    /* static */ std::unique_ptr<container::manager> container::mgr(new dflt_cntnr_mgr);

    // functions, exported

    /* virtual */
    container::manager::~manager()
    {
      TRACE("hugh::field::container::manager::~manager");
    }

    /* virtual */ void
    container::manager::print_on(std::ostream& os) const
    {
      TRACE_NEVER("hugh::field::container::manager::print_on");

      using support::ostream::operator<<;

      os << '['
         << container_list_.size()
         << ':'
         << container_list_
         << ']';
    }

    /* virtual */ void
    container::manager::evaluate()
    {
      TRACE("hugh::field::container::manager::evaluate");

      container_list_type tmp;

      std::swap(container_list_, tmp);

      for (auto& c : tmp) {
        c->evaluate();
      }
    }

    /* explicit */
    container::manager::manager()
      : support::printable(),
        container_list_   ()
    {
      TRACE("hugh::field::container::manager::manager");
    }

    /* virtual */ bool
    container::manager::schedule(container* a)
    {
      TRACE("hugh::field::container::manager::schedule");

      bool       result(false);
      auto const found (std::find(container_list_.begin(), container_list_.end(), a));

      if (container_list_.end() == found) {
        container_list_.push_back(a);

        result = true;
      }

      return result;
    }

    /* virtual */ bool
    container::manager::unschedule(container* a)
    {
      TRACE("hugh::field::container::manager::unschedule");

      bool       result(false);
      auto const found (std::find(container_list_.begin(), container_list_.end(), a));

      if (container_list_.end() != found) {
        container_list_.erase(found);

        result = true;
      }

      return result;
    }

    /* static */ container::manager&
    container::eval_manager()
    {
      TRACE("hugh::field::container::manager::eval_manager");

      return *mgr;
    }

    container::field_list_type const&
    container::fields() const
    {
      TRACE("hugh::field::container::manager::fields");

      return field_list_;
    }

    void
    container::touch()
    {
      TRACE("hugh::field::container::touch");

      do_touch();

      mgr->schedule(this);
    }

    void
    container::evaluate()
    {
      TRACE("hugh::field::container::evaluate");

      do_evaluate();

      last_evaluate_ = support::clock::now();
    }

    void
    container::changed(base& f)
    {
      TRACE("hugh::field::container::changed");

      do_changed(f);

      last_change_ = support::clock::now();

      touch();
    }

    namespace {

      thread_local unsigned indent(0);

    } // namespace {

    /* virtual */ void
    container::print_on(std::ostream& os) const
    {
      TRACE_NEVER("hugh::field::container::print_on");

      boost::io::ios_all_saver const ias(os);

      std::string prefix(indent, '\t');

      os << '\n' << prefix << '['
         << support::demangle(typeid(*this))
         << '@'
         << std::hex << std::showbase << this
         << ":["
         << std::dec << last_change_
         << ':'
         << std::dec << last_evaluate_
         << "],";

      ++indent;
      {
        prefix += ' ';

        for (auto const& f : field_list_) {
          os << '\n' << prefix << std::boolalpha << *f;
        }

        os << '\n'
           << prefix << "mgr:" << *mgr
           << ']';
      }
      --indent;
    }

    /* explicit */
    container::container()
      : support::printable(),
        last_change_      (support::clock::now()),
        last_evaluate_    (last_change_),
        field_list_       ()
    {
      TRACE("hugh::field::container::container");
    }

    /* virtual */
    container::~container()
    {
      TRACE("hugh::field::container::~container");

      mgr->unschedule(this);
    }

    /* virtual */ void
    container::do_touch()
    {
      TRACE("hugh::field::container::do_touch");
    }

    /* virtual */ void
    container::do_evaluate()
    {
      TRACE("hugh::field::container::do_evaluate");
    }

    /* virtual */ void
    container::do_changed(base&)
    {
      TRACE("hugh::field::container::do_changed");
    }

    void
    container::add(base* a)
    {
      TRACE("hugh::field::container::add");

      auto const found(std::find(field_list_.begin(), field_list_.end(), a));

      if (field_list_.end() == found) {
        field_list_.push_back(a);
      } else {
        throw std::logic_error("hugh::field::container::add: "
                               "unable to register the same field twice!");
      }
    }

    void
    container::sub(base* a)
    {
      TRACE("hugh::field::container::sub");

      auto const found(std::find(field_list_.begin(), field_list_.end(), a));

      if (field_list_.end() != found) {
        field_list_.erase(found);
      } else {
        throw std::logic_error("hugh::field::container::sub: "
                               "unable to unregister non-existent field!");
      }
    }

  } // namespace field {

} // namespace hugh {
<commit_msg>added: debugging info<commit_after>// -*- Mode:C++ -*-

/**************************************************************************************************/
/*                                                                                */
/* Copyright (C) 2016 University of Hull                                                          */
/*                                                                                */
/**************************************************************************************************/
/*                                                                                */
/*  module     :  hugh/field/container.cpp                                                        */
/*  project    :                                                                                */
/*  description:                                                                                */
/*                                                                                */
/**************************************************************************************************/

// include i/f header

#include "hugh/field/container.hpp"

// includes, system

#include <algorithm>              // std::find<>
#include <boost/io/ios_state.hpp> // boost::io::ios_all_saver
#include <ostream>                // std::ostream
#include <stdexcept>              // std::logic_error
#include <typeinfo>               // typeid

// includes, project

#include <hugh/field/base.hpp>
#include <hugh/support/io_utils.hpp>
#include <hugh/support/thread.hpp>
#include <hugh/support/type_info.hpp>

#define HUGH_USE_TRACE
#undef HUGH_USE_TRACE
#include <hugh/support/trace.hpp>

// internal unnamed namespace

namespace {

  // types, internal (class, enum, struct, union, typedef)

  class dflt_cntnr_mgr : public hugh::field::container::manager {

    using lock_type  = hugh::support::recursive_lock;
    using lock_guard = hugh::support::recursive_lock_guard;
    using inherited  = hugh::field::container::manager;

  public:

    explicit dflt_cntnr_mgr()
      : inherited           (),
        container_list_lock_()
    {
      TRACE("hugh::field::container::<unnamed>::dflt_cntnr_mgr::dflt_cntnr_mgr");
    }

    virtual ~dflt_cntnr_mgr()
    {
      TRACE("hugh::field::container::<unnamed>::dflt_cntnr_mgr::~dflt_cntnr_mgr");
    }

    virtual void print_on(std::ostream& os) const
    {
      TRACE_NEVER("hugh::field::container::<unnamed>::dflt_cntnr_mgr::print_on");

      lock_guard const lg(container_list_lock_);

      inherited::print_on(os);
    }

    virtual void evaluate()
    {
      TRACE("hugh::field::container::<unnamed>::dflt_cntnr_mgr::evaluate");

      lock_guard const lg(container_list_lock_);

      return inherited::evaluate();
    }

  protected:

    virtual bool schedule(hugh::field::container* a)
    {
      TRACE("hugh::field::container::<unnamed>::dflt_cntnr_mgr::schedule");

      lock_guard const lg(container_list_lock_);

      return inherited::schedule(a);
    }

    virtual bool unschedule(hugh::field::container* a)
    {
      TRACE("hugh::field::container::<unnamed>::dflt_cntnr_mgr::unschedule");

      lock_guard const lg(container_list_lock_);

      return inherited::unschedule(a);
    }

  private:

    mutable lock_type container_list_lock_;

  };

  // variables, internal

  // functions, internal

} // namespace {

namespace hugh {

  namespace field {

    // variables, exported

    /* static */ std::unique_ptr<container::manager> container::mgr(new dflt_cntnr_mgr);

    // functions, exported

    /* virtual */
    container::manager::~manager()
    {
      TRACE("hugh::field::container::manager::~manager");
    }

    /* virtual */ void
    container::manager::print_on(std::ostream& os) const
    {
      TRACE_NEVER("hugh::field::container::manager::print_on");

      using support::ostream::operator<<;

      os << '['
         << container_list_.size()
         << ':'
         << container_list_
         << ']';
    }

    /* virtual */ void
    container::manager::evaluate()
    {
      TRACE("hugh::field::container::manager::evaluate");

      container_list_type tmp;

      std::swap(container_list_, tmp);

      for (auto& c : tmp) {
        c->evaluate();
      }
    }

    /* explicit */
    container::manager::manager()
      : support::printable(),
        container_list_   ()
    {
      TRACE("hugh::field::container::manager::manager");
    }

    /* virtual */ bool
    container::manager::schedule(container* a)
    {
      TRACE("hugh::field::container::manager::schedule");

      bool       result(false);
      auto const found (std::find(container_list_.begin(), container_list_.end(), a));

      if (container_list_.end() == found) {
        container_list_.push_back(a);

        result = true;
      }

      return result;
    }

    /* virtual */ bool
    container::manager::unschedule(container* a)
    {
      TRACE("hugh::field::container::manager::unschedule");

      bool       result(false);
      auto const found (std::find(container_list_.begin(), container_list_.end(), a));

      if (container_list_.end() != found) {
        container_list_.erase(found);

        result = true;
      }

      return result;
    }

    /* static */ container::manager&
    container::eval_manager()
    {
      TRACE("hugh::field::container::manager::eval_manager");

      return *mgr;
    }

    container::field_list_type const&
    container::fields() const
    {
      TRACE("hugh::field::container::manager::fields");

      return field_list_;
    }

    void
    container::touch()
    {
      TRACE("hugh::field::container::touch");

      do_touch();

      mgr->schedule(this);
    }

    void
    container::evaluate()
    {
      TRACE("hugh::field::container::evaluate");

      do_evaluate();

      last_evaluate_ = support::clock::now();
    }

    void
    container::changed(base& f)
    {
      TRACE("hugh::field::container::changed");

      do_changed(f);

      last_change_ = support::clock::now();

      touch();
    }

    namespace {

      thread_local unsigned indent(0);

    } // namespace {

    /* virtual */ void
    container::print_on(std::ostream& os) const
    {
      TRACE_NEVER("hugh::field::container::print_on");

      boost::io::ios_all_saver const ias(os);

      std::string prefix(indent, '\t');

      os << '\n' << prefix << '['
         << support::demangle(typeid(*this))
         << '@'
         << std::hex << std::showbase << this
         << ":["
         << std::dec << last_change_
         << ':'
         << std::dec << last_evaluate_
         << "],";

      ++indent;
      {
        prefix += ' ';

        for (auto const& f : field_list_) {
          os << '\n' << prefix << std::boolalpha << *f;
        }

        os << '\n'
           << prefix << "mgr:" << *mgr
           << ']';
      }
      --indent;
    }

    /* explicit */
    container::container()
      : support::printable(),
        last_change_      (support::clock::now()),
        last_evaluate_    (last_change_),
        field_list_       ()
    {
      TRACE("hugh::field::container::container");
    }

    /* virtual */
    container::~container()
    {
      TRACE("hugh::field::container::~container");

      mgr->unschedule(this);
    }

    /* virtual */ void
    container::do_touch()
    {
      TRACE("hugh::field::container::do_touch");
    }

    /* virtual */ void
    container::do_evaluate()
    {
      TRACE("hugh::field::container::do_evaluate");
    }

    /* virtual */ void
    container::do_changed(base&)
    {
      TRACE("hugh::field::container::do_changed");
    }

    void
    container::add(base* a)
    {
      TRACE("hugh::field::container::add(" + a->name() + ") in (" +
            std::to_string(reinterpret_cast<unsigned long>(this)) + ")");

      auto const found(std::find(field_list_.begin(), field_list_.end(), a));

      if (field_list_.end() == found) {
        field_list_.push_back(a);
      } else {
        throw std::logic_error("hugh::field::container::add: "
                               "unable to register the same field twice '" + a->name() + "' (" +
                               std::to_string(reinterpret_cast<unsigned long>(this)) + ")!");
      }
    }

    void
    container::sub(base* a)
    {
      TRACE("hugh::field::container::sub(" + a->name() + ") in (" +
            std::to_string(reinterpret_cast<unsigned long>(this)) + ")");

      auto const found(std::find(field_list_.begin(), field_list_.end(), a));

      if (field_list_.end() != found) {
        field_list_.erase(found);
      } else {
        throw std::logic_error("hugh::field::container::sub: "
                               "unable to unregister non-existent field '" + a->name() + "' (" +
                               std::to_string(reinterpret_cast<unsigned long>(this)) + ")!");
      }
    }

  } // namespace field {

} // namespace hugh {
<|endoftext|>


<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2007, 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 "credd.h"
#include "condor_config.h"
#include "condor_daemon_core.h"
#include "condor_debug.h"
#include "store_cred.h"
#include "internet.h"
#include "get_daemon_name.h"
#include "subsystem_info.h"
#include "credmon_interface.h"
#include "ipv6_hostname.h"
#include "secure_file.h"

//-------------------------------------------------------------

CredDaemon *credd;

CredDaemon::CredDaemon() : m_name(NULL), m_update_collector_tid(-1)
{

	reconfig();

		// Command handler for the user condor_store_cred tool
	daemonCore->Register_Command( STORE_CRED, "STORE_CRED",
								(CommandHandler)&store_cred_handler,
								"store_cred_handler", NULL, WRITE,
								D_FULLDEBUG, true /*force authentication*/ );

		// Command handler for daemons to get the password
	daemonCore->Register_Command( CREDD_GET_PASSWD, "CREDD_GET_PASSWD",
								(CommandHandler)&get_cred_handler,
								"get_cred_handler", NULL, DAEMON,
								D_FULLDEBUG, true /*force authentication*/ );

		// NOP command for testing authentication
	daemonCore->Register_Command( CREDD_NOP, "CREDD_NOP",
								(CommandHandlercpp)&CredDaemon::nop_handler,
								"nop_handler", this, DAEMON,
								D_FULLDEBUG );

		// NOP command for testing authentication
	daemonCore->Register_Command( CREDD_REFRESH_ALL, "CREDD_REFRESH_ALL",
								(CommandHandlercpp)&CredDaemon::refresh_all_handler,
								"refresh_all_handler", this, DAEMON,
								D_FULLDEBUG );

		// See if creds are present for all modules requested
	daemonCore->Register_Command( ZKM_QUERY_CREDS, "ZKM_QUERY_CREDS",
								(CommandHandlercpp)&CredDaemon::zkm_query_creds,
								"zkm_query_creds", this, DAEMON,
								D_FULLDEBUG );

		// set timer to periodically advertise ourself to the collector
	daemonCore->Register_Timer(0, m_update_collector_interval,
		(TimerHandlercpp)&CredDaemon::update_collector, "update_collector", this);

	// only sweep if we have a cred dir
	char* p = param("SEC_CREDENTIAL_DIRECTORY");
	if(p) {
		// didn't need the value, just to see if it's defined.
		free(p);
		dprintf(D_FULLDEBUG, "CREDD: setting sweep_timer_handler\n");
		int sec_cred_sweep_interval = param_integer("SEC_CREDENTIAL_SWEEP_INTERVAL", 30);
		m_cred_sweep_tid = daemonCore->Register_Timer( sec_cred_sweep_interval, sec_cred_sweep_interval,
								(TimerHandlercpp)&CredDaemon::sweep_timer_handler,
								"sweep_timer_handler", this );
	}
}

CredDaemon::~CredDaemon()
{
	// tell our collector we're going away
	invalidate_ad();

	if (m_name != NULL) {
		free(m_name);
	}
}

void
CredDaemon::reconfig()
{
	// get our daemon name; if CREDD_HOST is defined, we use it
	// as our name. this is because clients that have CREDD_HOST
	// defined will query the Collector for a CredD ad that has
	// a name matching its setting for CREDD_HOST. but CREDD_HOST
	// will not necessarily match what default_daemon_name()
	// returns - for example, if CREDD_HOST is set to a DNS alias
	//
	if (m_name != NULL) {
		free(m_name);
	}
	m_name = param("CREDD_HOST");
	if (m_name == NULL) {
		char* tmp = default_daemon_name();
		ASSERT(tmp != NULL);
		m_name = strdup(tmp);
		ASSERT(m_name != NULL);
		delete[] tmp;
	}
	if(m_name == NULL) {
		EXCEPT("default_daemon_name() returned NULL");
	}

	// how often do we update the collector?
	m_update_collector_interval = param_integer ("CREDD_UPDATE_INTERVAL",
												 5 * MINUTE);

	// fill in our classad
	initialize_classad();

	// reset the timer (if it exists) to update the collector
	if (m_update_collector_tid != -1) {
		daemonCore->Reset_Timer(m_update_collector_tid, 0, m_update_collector_interval);
	}
}

void
CredDaemon::sweep_timer_handler( void )
{
	dprintf(D_FULLDEBUG, "CREDD: calling and resetting sweep_timer_handler()\n");
#ifndef WIN32
	credmon_sweep_creds();
#endif  // WIN32
	int sec_cred_sweep_interval = param_integer("SEC_CREDENTIAL_SWEEP_INTERVAL", 30);
	daemonCore->Reset_Timer (m_cred_sweep_tid, sec_cred_sweep_interval, sec_cred_sweep_interval);
}

void
CredDaemon::initialize_classad()
{
	m_classad.Clear();

	SetMyTypeName(m_classad, CREDD_ADTYPE);
	SetTargetTypeName(m_classad, "");

	MyString line;

	line.formatstr("%s = \"%s\"", ATTR_NAME, m_name );
	m_classad.Insert(line.Value());

	line.formatstr ("%s = \"%s\"", ATTR_CREDD_IP_ADDR,
			daemonCore->InfoCommandSinfulString() );
	m_classad.Insert(line.Value());

        // Publish all DaemonCore-specific attributes, which also handles
        // SUBSYS_ATTRS for us.
    daemonCore->publish(&m_classad);
}

void
CredDaemon::update_collector()
{
	daemonCore->sendUpdates(UPDATE_AD_GENERIC, &m_classad, NULL, true);
}

void
CredDaemon::invalidate_ad()
{
	ClassAd query_ad;
	SetMyTypeName(query_ad, QUERY_ADTYPE);
	SetTargetTypeName(query_ad, CREDD_ADTYPE);

	MyString line;
	line.formatstr("%s = TARGET.%s == \"%s\"", ATTR_REQUIREMENTS, ATTR_NAME, m_name);
    query_ad.Insert(line.Value());
	query_ad.Assign(ATTR_NAME,m_name);

	daemonCore->sendUpdates(INVALIDATE_ADS_GENERIC, &query_ad, NULL, true);
}

void
CredDaemon::nop_handler(int, Stream*)
{
	return;
}

void
CredDaemon::zkm_query_creds( int, Stream* s)
{
	ReliSock* r = (ReliSock*)s;
	r->decode();
	int numads;
	r->code(numads);
	// ifdef LINUX because this code doesn't build on Windows and older Macs
#ifdef LINUX
	ClassAd requests[numads];
	for(int i=0; i<numads; i++) {
		getClassAd(r, requests[i]);
	}
#else
	ClassAd request;
	for(int i=0; i<numads; i++) {
		getClassAd(r, request);
	}
#endif
	r->end_of_message();

	dprintf(D_ALWAYS, "ZKM: got zkm_query_creds.  there are %i requests.\n", numads);

	MyString URL;

	// ifdef LINUX because this code doesn't build on Windows and older Macs
#ifdef LINUX
	ClassAd missing[numads];
	int nummissing = 0;
	for(int i=0; i<numads; i++) {
		MyString service;
		MyString handle;
		MyString user;
		requests[i].LookupString("Service", service);
		requests[i].LookupString("Handle", handle);
		requests[i].LookupString("Username", user);

		MyString tmpfname;
		tmpfname = service;
		tmpfname.replaceString("/",":");
		if(handle.Length()) {
			tmpfname += "_";
			tmpfname += handle;
		}
		tmpfname += ".top";

		dprintf(D_ALWAYS, "ZKM: looking for %s\n", tmpfname.Value());
		if (!credmon_poll_continue(user.Value(), 0, tmpfname.Value())) {
			dprintf(D_ALWAYS, "ZKM: missing %s\n", tmpfname.Value());
			missing[nummissing] = requests[i];
			nummissing++;
		}
	}

	if(nummissing > 0) {
		// create unique request file with classad metadata
		char *key = Condor_Crypt_Base::randomHexKey(32);

		MyString contents;

		for (int i=0; i<nummissing; i++) {
			// fill in everything we need to pass
			ClassAd ad;
			MyString tmpname;
			MyString tmpvalue;

			MyString service;
			missing[i].LookupString("Service", service);
			ad.Assign("Provider", service);

			missing[i].LookupString("Username", tmpvalue);
			ad.Assign("LocalUser", tmpvalue);

			missing[i].LookupString("Handle", tmpvalue);
			ad.Assign("Handle", tmpvalue);

			missing[i].LookupString("Scopes", tmpvalue);
			ad.Assign("Scopes", tmpvalue);

			missing[i].LookupString("Audience", tmpvalue);
			ad.Assign("Audience", tmpvalue);

			tmpname = service;
			tmpname += "_CLIENT_ID";
			param(tmpvalue, tmpname.c_str());
			ad.Assign("ClientId", tmpvalue);

			// this is a hack.  secret needs to be in a root-owned file, not in config.
			tmpname = service;
			tmpname += "_CLIENT_SECRET";
			param(tmpvalue, tmpname.c_str());
			ad.Assign("ClientSecret", tmpvalue);

			tmpname = service;
			tmpname += "_RETURN_URL_SUFFIX";
			param(tmpvalue, tmpname.c_str());

			MyString hn = get_local_fqdn();
			char* h = param("ZKMHOST");
			if(h) {
				hn = h;
				free(h);
			}
			URL = "https://";
			URL += hn;
			URL += tmpvalue;
			ad.Assign("ReturnUrl", URL);
			URL = "";

			tmpname = service;
			tmpname += "_AUTHORIZATION_URL";
			param(tmpvalue, tmpname.c_str());
			ad.Assign("AuthorizationUrl", tmpvalue);

			tmpname = service;
			tmpname += "_TOKEN_URL";
			param(tmpvalue, tmpname.c_str());
			ad.Assign("TokenUrl", tmpvalue);

			tmpname = service;
			tmpname += "_USER_URL";
			param(tmpvalue, tmpname.c_str());
			ad.Assign("UserUrl", tmpvalue);

			// serialize classad into string
			MyString ad_string;
			sPrintAd(ad_string, ad);

			dprintf(D_ALWAYS, "ZKM: appending:%s\n", ad_string.Value());
			if(!contents.empty()) {
				contents += "\n";
			}
			contents += ad_string;
		}

		// write the file into sec_credential_dir
		auto_free_ptr cred_dir(param("SEC_CREDENTIAL_DIRECTORY"));
		if(!cred_dir) {
			EXCEPT("NO SEC_CREDENTIAL_DIRECTORY");
		}

		MyString path = (char*)(cred_dir.ptr());
		path += "/";
		path += key;

		dprintf(D_ALWAYS, "ZKM: writing to %s.  data len %i:\n%s\n", path.Value(), contents.Length(), contents.Value());
		int rc = write_secure_file(path.Value(), contents.Value(), contents.Length(), true);

		if (rc != SUCCESS) {
			dprintf(D_ALWAYS, "ZKM: failed to write secure temp file %s\n", path.Value());
		} else {
			MyString hn = get_local_fqdn();
			char* h = param("ZKMHOST");
			if(h) {
				hn = h;
				free(h);
			}
			URL = "https://";
			URL += hn;
			URL += "/key/";
			URL += key;
		}

		dprintf(D_ALWAYS, "ZKM: sending URL '%s'\n", URL.Value());
	}
#else
#endif

	r->encode();
	r->code(URL);
	r->end_of_message();
}

void
CredDaemon::refresh_all_handler( int, Stream* s)
{
	ReliSock* r = (ReliSock*)s;
	r->decode();
	ClassAd ad;
	getClassAd(r, ad);
	r->end_of_message();

	// don't actually care (at the moment) what's in the ad, it's for
	// forward/backward compatibility.

#ifndef WIN32
	// refresh ALL credentials
	if(credmon_poll( NULL, true, true )) {
		ad.Assign("result", "success");
	} else {
		ad.Assign("result", "failure");
	}
#else   // WIN32
	// this command handler shouldn't be getting called on windows, so fail.
	ad.Assign("result", "failure");
#endif  // WIN32

	r->encode();
	dprintf(D_SECURITY | D_FULLDEBUG, "CREDD: refresh_all sending response ad:\n");
	dPrintAd(D_SECURITY | D_FULLDEBUG, ad);
	putClassAd(r, ad);
	r->end_of_message();
}

//-------------------------------------------------------------

void
main_init(int /*argc*/, char * /*argv*/ [])
{
	dprintf(D_ALWAYS, "main_init() called\n");

	credd = new CredDaemon;
}

//-------------------------------------------------------------

void
main_config()
{
	dprintf(D_ALWAYS, "main_config() called\n");

	credd->reconfig();
}

//-------------------------------------------------------------

void main_shutdown_fast()
{
	dprintf(D_ALWAYS, "main_shutdown_fast() called\n");

	delete credd;

	DC_Exit(0);
}

//-------------------------------------------------------------

void main_shutdown_graceful()
{
	dprintf(D_ALWAYS, "main_shutdown_graceful() called\n");

	delete credd;

	DC_Exit(0);
}

//-------------------------------------------------------------

int
main( int argc, char **argv )
{
	set_mySubSystem( "CREDD", SUBSYSTEM_TYPE_DAEMON );

	dc_main_init = main_init;
	dc_main_config = main_config;
	dc_main_shutdown_fast = main_shutdown_fast;
	dc_main_shutdown_graceful = main_shutdown_graceful;
	return dc_main( argc, argv );
}
<commit_msg>Read SciTokens client secret from a secure file. #6861<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, 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 "credd.h"
#include "condor_config.h"
#include "condor_daemon_core.h"
#include "condor_debug.h"
#include "store_cred.h"
#include "internet.h"
#include "get_daemon_name.h"
#include "subsystem_info.h"
#include "credmon_interface.h"
#include "ipv6_hostname.h"
#include "secure_file.h"

//-------------------------------------------------------------

CredDaemon *credd;

CredDaemon::CredDaemon() : m_name(NULL), m_update_collector_tid(-1)
{

	reconfig();

		// Command handler for the user condor_store_cred tool
	daemonCore->Register_Command( STORE_CRED, "STORE_CRED",
								(CommandHandler)&store_cred_handler,
								"store_cred_handler", NULL, WRITE,
								D_FULLDEBUG, true /*force authentication*/ );

		// Command handler for daemons to get the password
	daemonCore->Register_Command( CREDD_GET_PASSWD, "CREDD_GET_PASSWD",
								(CommandHandler)&get_cred_handler,
								"get_cred_handler", NULL, DAEMON,
								D_FULLDEBUG, true /*force authentication*/ );

		// NOP command for testing authentication
	daemonCore->Register_Command( CREDD_NOP, "CREDD_NOP",
								(CommandHandlercpp)&CredDaemon::nop_handler,
								"nop_handler", this, DAEMON,
								D_FULLDEBUG );

		// NOP command for testing authentication
	daemonCore->Register_Command( CREDD_REFRESH_ALL, "CREDD_REFRESH_ALL",
								(CommandHandlercpp)&CredDaemon::refresh_all_handler,
								"refresh_all_handler", this, DAEMON,
								D_FULLDEBUG );

		// See if creds are present for all modules requested
	daemonCore->Register_Command( ZKM_QUERY_CREDS, "ZKM_QUERY_CREDS",
								(CommandHandlercpp)&CredDaemon::zkm_query_creds,
								"zkm_query_creds", this, DAEMON,
								D_FULLDEBUG );

		// set timer to periodically advertise ourself to the collector
	daemonCore->Register_Timer(0, m_update_collector_interval,
		(TimerHandlercpp)&CredDaemon::update_collector, "update_collector", this);

	// only sweep if we have a cred dir
	char* p = param("SEC_CREDENTIAL_DIRECTORY");
	if(p) {
		// didn't need the value, just to see if it's defined.
		free(p);
		dprintf(D_FULLDEBUG, "CREDD: setting sweep_timer_handler\n");
		int sec_cred_sweep_interval = param_integer("SEC_CREDENTIAL_SWEEP_INTERVAL", 30);
		m_cred_sweep_tid = daemonCore->Register_Timer( sec_cred_sweep_interval, sec_cred_sweep_interval,
								(TimerHandlercpp)&CredDaemon::sweep_timer_handler,
								"sweep_timer_handler", this );
	}
}

CredDaemon::~CredDaemon()
{
	// tell our collector we're going away
	invalidate_ad();

	if (m_name != NULL) {
		free(m_name);
	}
}

void
CredDaemon::reconfig()
{
	// get our daemon name; if CREDD_HOST is defined, we use it
	// as our name. this is because clients that have CREDD_HOST
	// defined will query the Collector for a CredD ad that has
	// a name matching its setting for CREDD_HOST. but CREDD_HOST
	// will not necessarily match what default_daemon_name()
	// returns - for example, if CREDD_HOST is set to a DNS alias
	//
	if (m_name != NULL) {
		free(m_name);
	}
	m_name = param("CREDD_HOST");
	if (m_name == NULL) {
		char* tmp = default_daemon_name();
		ASSERT(tmp != NULL);
		m_name = strdup(tmp);
		ASSERT(m_name != NULL);
		delete[] tmp;
	}
	if(m_name == NULL) {
		EXCEPT("default_daemon_name() returned NULL");
	}

	// how often do we update the collector?
	m_update_collector_interval = param_integer ("CREDD_UPDATE_INTERVAL",
												 5 * MINUTE);

	// fill in our classad
	initialize_classad();

	// reset the timer (if it exists) to update the collector
	if (m_update_collector_tid != -1) {
		daemonCore->Reset_Timer(m_update_collector_tid, 0, m_update_collector_interval);
	}
}

void
CredDaemon::sweep_timer_handler( void )
{
	dprintf(D_FULLDEBUG, "CREDD: calling and resetting sweep_timer_handler()\n");
#ifndef WIN32
	credmon_sweep_creds();
#endif  // WIN32
	int sec_cred_sweep_interval = param_integer("SEC_CREDENTIAL_SWEEP_INTERVAL", 30);
	daemonCore->Reset_Timer (m_cred_sweep_tid, sec_cred_sweep_interval, sec_cred_sweep_interval);
}

void
CredDaemon::initialize_classad()
{
	m_classad.Clear();

	SetMyTypeName(m_classad, CREDD_ADTYPE);
	SetTargetTypeName(m_classad, "");

	MyString line;

	line.formatstr("%s = \"%s\"", ATTR_NAME, m_name );
	m_classad.Insert(line.Value());

	line.formatstr ("%s = \"%s\"", ATTR_CREDD_IP_ADDR,
			daemonCore->InfoCommandSinfulString() );
	m_classad.Insert(line.Value());

        // Publish all DaemonCore-specific attributes, which also handles
        // SUBSYS_ATTRS for us.
    daemonCore->publish(&m_classad);
}

void
CredDaemon::update_collector()
{
	daemonCore->sendUpdates(UPDATE_AD_GENERIC, &m_classad, NULL, true);
}

void
CredDaemon::invalidate_ad()
{
	ClassAd query_ad;
	SetMyTypeName(query_ad, QUERY_ADTYPE);
	SetTargetTypeName(query_ad, CREDD_ADTYPE);

	MyString line;
	line.formatstr("%s = TARGET.%s == \"%s\"", ATTR_REQUIREMENTS, ATTR_NAME, m_name);
    query_ad.Insert(line.Value());
	query_ad.Assign(ATTR_NAME,m_name);

	daemonCore->sendUpdates(INVALIDATE_ADS_GENERIC, &query_ad, NULL, true);
}

void
CredDaemon::nop_handler(int, Stream*)
{
	return;
}

void
CredDaemon::zkm_query_creds( int, Stream* s)
{
	ReliSock* r = (ReliSock*)s;
	r->decode();
	int numads;
	r->code(numads);
	// ifdef LINUX because this code doesn't build on Windows and older Macs
#ifdef LINUX
	ClassAd requests[numads];
	for(int i=0; i<numads; i++) {
		getClassAd(r, requests[i]);
	}
#else
	ClassAd request;
	for(int i=0; i<numads; i++) {
		getClassAd(r, request);
	}
#endif
	r->end_of_message();

	dprintf(D_ALWAYS, "ZKM: got zkm_query_creds.  there are %i requests.\n", numads);

	MyString URL;

	// ifdef LINUX because this code doesn't build on Windows and older Macs
#ifdef LINUX
	ClassAd missing[numads];
	int nummissing = 0;
	for(int i=0; i<numads; i++) {
		MyString service;
		MyString handle;
		MyString user;
		requests[i].LookupString("Service", service);
		requests[i].LookupString("Handle", handle);
		requests[i].LookupString("Username", user);

		MyString tmpfname;
		tmpfname = service;
		tmpfname.replaceString("/",":");
		if(handle.Length()) {
			tmpfname += "_";
			tmpfname += handle;
		}
		tmpfname += ".top";

		dprintf(D_ALWAYS, "ZKM: looking for %s\n", tmpfname.Value());
		if (!credmon_poll_continue(user.Value(), 0, tmpfname.Value())) {
			dprintf(D_ALWAYS, "ZKM: missing %s\n", tmpfname.Value());
			missing[nummissing] = requests[i];
			nummissing++;
		}
	}

	if(nummissing > 0) {
		// create unique request file with classad metadata
		char *key = Condor_Crypt_Base::randomHexKey(32);

		MyString contents;

		for (int i=0; i<nummissing; i++) {
			// fill in everything we need to pass
			ClassAd ad;
			MyString tmpname;
			MyString tmpvalue;

			MyString service;
			missing[i].LookupString("Service", service);
			ad.Assign("Provider", service);

			missing[i].LookupString("Username", tmpvalue);
			ad.Assign("LocalUser", tmpvalue);

			missing[i].LookupString("Handle", tmpvalue);
			ad.Assign("Handle", tmpvalue);

			missing[i].LookupString("Scopes", tmpvalue);
			ad.Assign("Scopes", tmpvalue);

			missing[i].LookupString("Audience", tmpvalue);
			ad.Assign("Audience", tmpvalue);

			tmpname = service;
			tmpname += "_CLIENT_ID";
			param(tmpvalue, tmpname.c_str());
			ad.Assign("ClientId", tmpvalue);

			char *buf = NULL;
			size_t buf_len = 0;
			std::string secret;
			tmpname = service;
			tmpname += "_CLIENT_SECRET_FILE";
			param(tmpvalue, tmpname.c_str());
			// Read the file and use the contents before the first
			// newline, if any.
			if(read_secure_file(tmpvalue.Value(), (void**)&buf, &buf_len, true)) {
				for ( size_t i = 0; i < buf_len; i++ ) {
					if ( buf[i] == '\n' ) {
						buf_len = i;
					}
				}
				secret.assign(buf, buf_len);
			}
			ad.Assign("ClientSecret", secret);

			tmpname = service;
			tmpname += "_RETURN_URL_SUFFIX";
			param(tmpvalue, tmpname.c_str());

			MyString hn = get_local_fqdn();
			char* h = param("ZKMHOST");
			if(h) {
				hn = h;
				free(h);
			}
			URL = "https://";
			URL += hn;
			URL += tmpvalue;
			ad.Assign("ReturnUrl", URL);
			URL = "";

			tmpname = service;
			tmpname += "_AUTHORIZATION_URL";
			param(tmpvalue, tmpname.c_str());
			ad.Assign("AuthorizationUrl", tmpvalue);

			tmpname = service;
			tmpname += "_TOKEN_URL";
			param(tmpvalue, tmpname.c_str());
			ad.Assign("TokenUrl", tmpvalue);

			tmpname = service;
			tmpname += "_USER_URL";
			param(tmpvalue, tmpname.c_str());
			ad.Assign("UserUrl", tmpvalue);

			// serialize classad into string
			MyString ad_string;
			sPrintAd(ad_string, ad);

			dprintf(D_ALWAYS, "ZKM: appending:%s\n", ad_string.Value());
			if(!contents.empty()) {
				contents += "\n";
			}
			contents += ad_string;
		}

		// write the file into sec_credential_dir
		auto_free_ptr cred_dir(param("SEC_CREDENTIAL_DIRECTORY"));
		if(!cred_dir) {
			EXCEPT("NO SEC_CREDENTIAL_DIRECTORY");
		}

		MyString path = (char*)(cred_dir.ptr());
		path += "/";
		path += key;

		dprintf(D_ALWAYS, "ZKM: writing to %s.  data len %i:\n%s\n", path.Value(), contents.Length(), contents.Value());
		int rc = write_secure_file(path.Value(), contents.Value(), contents.Length(), true);

		if (rc != SUCCESS) {
			dprintf(D_ALWAYS, "ZKM: failed to write secure temp file %s\n", path.Value());
		} else {
			MyString hn = get_local_fqdn();
			char* h = param("ZKMHOST");
			if(h) {
				hn = h;
				free(h);
			}
			URL = "https://";
			URL += hn;
			URL += "/key/";
			URL += key;
		}

		dprintf(D_ALWAYS, "ZKM: sending URL '%s'\n", URL.Value());
	}
#else
#endif

	r->encode();
	r->code(URL);
	r->end_of_message();
}

void
CredDaemon::refresh_all_handler( int, Stream* s)
{
	ReliSock* r = (ReliSock*)s;
	r->decode();
	ClassAd ad;
	getClassAd(r, ad);
	r->end_of_message();

	// don't actually care (at the moment) what's in the ad, it's for
	// forward/backward compatibility.

#ifndef WIN32
	// refresh ALL credentials
	if(credmon_poll( NULL, true, true )) {
		ad.Assign("result", "success");
	} else {
		ad.Assign("result", "failure");
	}
#else   // WIN32
	// this command handler shouldn't be getting called on windows, so fail.
	ad.Assign("result", "failure");
#endif  // WIN32

	r->encode();
	dprintf(D_SECURITY | D_FULLDEBUG, "CREDD: refresh_all sending response ad:\n");
	dPrintAd(D_SECURITY | D_FULLDEBUG, ad);
	putClassAd(r, ad);
	r->end_of_message();
}

//-------------------------------------------------------------

void
main_init(int /*argc*/, char * /*argv*/ [])
{
	dprintf(D_ALWAYS, "main_init() called\n");

	credd = new CredDaemon;
}

//-------------------------------------------------------------

void
main_config()
{
	dprintf(D_ALWAYS, "main_config() called\n");

	credd->reconfig();
}

//-------------------------------------------------------------

void main_shutdown_fast()
{
	dprintf(D_ALWAYS, "main_shutdown_fast() called\n");

	delete credd;

	DC_Exit(0);
}

//-------------------------------------------------------------

void main_shutdown_graceful()
{
	dprintf(D_ALWAYS, "main_shutdown_graceful() called\n");

	delete credd;

	DC_Exit(0);
}

//-------------------------------------------------------------

int
main( int argc, char **argv )
{
	set_mySubSystem( "CREDD", SUBSYSTEM_TYPE_DAEMON );

	dc_main_init = main_init;
	dc_main_config = main_config;
	dc_main_shutdown_fast = main_shutdown_fast;
	dc_main_shutdown_graceful = main_shutdown_graceful;
	return dc_main( argc, argv );
}
<|endoftext|>


<commit_before>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

#include "condor_common.h"
#include "../condor_daemon_core.V6/condor_daemon_core.h"
#include "string_list.h"
#include "condor_config.h"

#include "eventd.h"

EventDaemon _EventD, *EventD = &_EventD;

EventDaemon::EventDaemon() : EventInterval(-1), MaxEventPreparation(0),
	TimeoutTid(-1),	EventList(NULL), NumEvents(-1)
{
}

EventDaemon::~EventDaemon()
{
	if (TimeoutTid >= 0) {
		daemonCore->Cancel_Timer(TimeoutTid);
	}
	for (int i=0; i < NumEvents; i++) {
		delete EventList[i];
	}
	delete [] EventList;
}

int
EventDaemon::Config()
{
	char *tmp;

	for (int i=0; i < NumEvents; i++) {
		delete EventList[i];
	}
	delete [] EventList;

	tmp = param("EVENT_LIST");
	if (!tmp || tmp[0] == '\0') {
		dprintf(D_ALWAYS, "EVENT_LIST is empty or not defined.  "
				"EventDaemon will do nothing.\n");
		if (tmp) free(tmp);
		return 0;
	}
	StringList event_list(tmp);
	free(tmp);

	NumEvents = event_list.number();
	EventList = new (ScheduledEvent *)[NumEvents];

	event_list.rewind();
	char *item;
	for (int offset = 0; (item = event_list.next()) != NULL; offset++) {
		tmp = param(item);
		if (!tmp) {
			dprintf(D_ALWAYS, "Event %s undefined!\n", item);
			EventList[offset] = NULL;
		} else {
			dprintf(D_FULLDEBUG, "%s: %s\n", item, tmp);
			EventList[offset] = NewEvent(item, tmp);
		}
		free(tmp);
	}

	tmp = param("EVENTD_INTERVAL");
	if (tmp) {
		EventInterval = atoi(tmp);
		free(tmp);
		dprintf(D_FULLDEBUG, "Event interval set to %d seconds.\n",
				EventInterval);
	} else {
		EventInterval = 900;
	}

	tmp = param("EVENTD_MAX_PREPARATION");
	if (tmp) {
		MaxEventPreparation = atoi(tmp);
		free(tmp);
		dprintf(D_FULLDEBUG, "Max event preparation set to %d seconds.\n",
				MaxEventPreparation);
	} else {
		MaxEventPreparation = 0;
	}

	if (TimeoutTid >= 0) {
		daemonCore->Cancel_Timer(TimeoutTid);
	}

	TimeoutTid = daemonCore->Register_Timer(0, EventInterval,
											(Eventcpp)Timeout,
											"Timeout", this);

	return 0;
}

int
EventDaemon::Timeout()
{
	int TimeToNearestEvent = INT_MAX;
	for (int ev=0; ev < NumEvents; ev++) {
		if (EventList[ev] &&
			EventList[ev]->IsValid() && !EventList[ev]->IsActive()) {
			int TimeToEvent = EventList[ev]->TimeToEvent();
			if (TimeToNearestEvent > TimeToEvent) {
				TimeToNearestEvent = TimeToEvent;
			}
			if (MaxEventPreparation == 0 ||
				TimeToEvent <= MaxEventPreparation) {
				int TimeNeeded = EventList[ev]->TimeNeeded();
				dprintf(D_FULLDEBUG, "%s: TimeToEvent=%d, TimeNeeded=%d\n",
						EventList[ev]->Name(), TimeToEvent, TimeNeeded);
				if (TimeNeeded + EventInterval >= TimeToEvent) {
					dprintf(D_ALWAYS, "%s: Activating...\n",
							EventList[ev]->Name());
					EventList[ev]->ActivateEvent();
				}
			} else {
				dprintf(D_FULLDEBUG, "%s: TimeToEvent=%d\n",
						EventList[ev]->Name(), TimeToEvent);
			}
		}
	}
	if (TimeToNearestEvent > MaxEventPreparation) {
		dprintf(D_FULLDEBUG, "All events are a long way off.\n");
		dprintf(D_FULLDEBUG, "Next Timeout() will occur in %d seconds.\n",
				TimeToNearestEvent-MaxEventPreparation);
		daemonCore->Reset_Timer(TimeoutTid,
								TimeToNearestEvent-MaxEventPreparation,
								EventInterval);
	}
}

ScheduledEvent *
EventDaemon::NewEvent(const char name[], const char record[])
{
	if (strncmp(record, "SHUTDOWN", 8) == 0) {
		return new ScheduledShutdownEvent(name, record+8);
	}

	dprintf(D_ALWAYS, "Unknown event type in event %s.\n", name);
	return NULL;
}
<commit_msg>fix MaxEventPreparation bug which caused infinite loop when all events were active<commit_after>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

#include "condor_common.h"
#include "../condor_daemon_core.V6/condor_daemon_core.h"
#include "string_list.h"
#include "condor_config.h"

#include "eventd.h"

EventDaemon _EventD, *EventD = &_EventD;

EventDaemon::EventDaemon() : EventInterval(-1), MaxEventPreparation(0),
	TimeoutTid(-1),	EventList(NULL), NumEvents(-1)
{
}

EventDaemon::~EventDaemon()
{
	if (TimeoutTid >= 0) {
		daemonCore->Cancel_Timer(TimeoutTid);
	}
	for (int i=0; i < NumEvents; i++) {
		delete EventList[i];
	}
	delete [] EventList;
}

int
EventDaemon::Config()
{
	char *tmp;

	for (int i=0; i < NumEvents; i++) {
		delete EventList[i];
	}
	delete [] EventList;

	tmp = param("EVENT_LIST");
	if (!tmp || tmp[0] == '\0') {
		dprintf(D_ALWAYS, "EVENT_LIST is empty or not defined.  "
				"EventDaemon will do nothing.\n");
		if (tmp) free(tmp);
		return 0;
	}
	StringList event_list(tmp);
	free(tmp);

	NumEvents = event_list.number();
	EventList = new (ScheduledEvent *)[NumEvents];

	event_list.rewind();
	char *item;
	for (int offset = 0; (item = event_list.next()) != NULL; offset++) {
		tmp = param(item);
		if (!tmp) {
			dprintf(D_ALWAYS, "Event %s undefined!\n", item);
			EventList[offset] = NULL;
		} else {
			dprintf(D_FULLDEBUG, "%s: %s\n", item, tmp);
			EventList[offset] = NewEvent(item, tmp);
		}
		free(tmp);
	}

	tmp = param("EVENTD_INTERVAL");
	if (tmp) {
		EventInterval = atoi(tmp);
		free(tmp);
		dprintf(D_FULLDEBUG, "Event interval set to %d seconds.\n",
				EventInterval);
	} else {
		EventInterval = 900;
	}

	tmp = param("EVENTD_MAX_PREPARATION");
	if (tmp) {
		MaxEventPreparation = atoi(tmp);
		free(tmp);
		dprintf(D_FULLDEBUG, "Max event preparation set to %d seconds.\n",
				MaxEventPreparation);
	} else {
		MaxEventPreparation = 0;
	}

	if (TimeoutTid >= 0) {
		daemonCore->Cancel_Timer(TimeoutTid);
	}

	TimeoutTid = daemonCore->Register_Timer(0, EventInterval,
											(Eventcpp)Timeout,
											"Timeout", this);

	return 0;
}

int
EventDaemon::Timeout()
{
	int TimeToNearestEvent = -1;
	bool EventActive = false;
	for (int ev=0; ev < NumEvents; ev++) {
		if (EventList[ev] && EventList[ev]->IsValid()) {
			if (EventList[ev]->IsActive()) {
				EventActive = true;
			} else {
				int TimeToEvent = EventList[ev]->TimeToEvent();
				if (TimeToNearestEvent > TimeToEvent ||
					TimeToNearestEvent == -1) {
					TimeToNearestEvent = TimeToEvent;
				}
				if (MaxEventPreparation == 0 ||
					TimeToEvent <= MaxEventPreparation) {
					int TimeNeeded = EventList[ev]->TimeNeeded();
					dprintf(D_FULLDEBUG, "%s: TimeToEvent=%d, TimeNeeded=%d\n",
							EventList[ev]->Name(), TimeToEvent, TimeNeeded);
					if (TimeNeeded + EventInterval >= TimeToEvent) {
						dprintf(D_ALWAYS, "%s: Activating...\n",
								EventList[ev]->Name());
						EventList[ev]->ActivateEvent();
					}
				} else {
					dprintf(D_FULLDEBUG, "%s: TimeToEvent=%d\n",
							EventList[ev]->Name(), TimeToEvent);
				}
			}
		}
	}
	if (!EventActive && TimeToNearestEvent > MaxEventPreparation) {
		dprintf(D_FULLDEBUG, "All events are a long way off.\n");
		dprintf(D_FULLDEBUG, "Next Timeout() will occur in %d seconds.\n",
				TimeToNearestEvent-MaxEventPreparation);
		daemonCore->Reset_Timer(TimeoutTid,
								TimeToNearestEvent-MaxEventPreparation,
								EventInterval);
	}
}

ScheduledEvent *
EventDaemon::NewEvent(const char name[], const char record[])
{
	if (strncmp(record, "SHUTDOWN", 8) == 0) {
		return new ScheduledShutdownEvent(name, record+8);
	}

	dprintf(D_ALWAYS, "Unknown event type in event %s.\n", name);
	return NULL;
}
<|endoftext|>


<commit_before>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2015 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/ui/gl/blitter.h"

#include <string>

#include "xenia/base/assert.h"
#include "xenia/base/math.h"
#include "xenia/ui/gl/gl_context.h"

namespace xe {
namespace ui {
namespace gl {

Blitter::Blitter()
    : vertex_program_(0),
      color_fragment_program_(0),
      depth_fragment_program_(0),
      color_pipeline_(0),
      depth_pipeline_(0),
      vbo_(0),
      vao_(0),
      nearest_sampler_(0),
      linear_sampler_(0),
      scratch_framebuffer_(0) {}

Blitter::~Blitter() = default;

bool Blitter::Initialize() {
  const std::string header =
      R"(
#version 450
#extension GL_ARB_explicit_uniform_location : require
#extension GL_ARB_shading_language_420pack : require
precision highp float;
precision highp int;
layout(std140, column_major) uniform;
layout(std430, column_major) buffer;
)";
  const std::string vs_source = header +
                                R"(
layout(location = 0) uniform vec4 src_uv;
out gl_PerVertex {
  vec4 gl_Position;
  float gl_PointSize;
  float gl_ClipDistance[];
};
layout(location = 0) in vec2 vfetch_pos;
layout(location = 0) out vec2 vtx_uv;
void main() {
  gl_Position = vec4(vfetch_pos.xy * vec2(2.0, -2.0) -
                vec2(1.0, -1.0), 0.0, 1.0);
  vtx_uv = vfetch_pos.xy * src_uv.zw + src_uv.xy;
})";
  const std::string color_fs_source = header +
                                      R"(
layout(location = 1) uniform sampler2D src_texture;
layout(location = 2) uniform bool swap;
layout(location = 0) in vec2 vtx_uv;
layout(location = 0) out vec4 oC;
void main() {
  oC = texture(src_texture, vtx_uv);
  if (!swap) oC = oC.bgra;
})";
  const std::string depth_fs_source = header +
                                      R"(
layout(location = 1) uniform sampler2D src_texture;
layout(location = 0) in vec2 vtx_uv;
layout(location = 0) out vec4 oC;
void main() {
  gl_FragDepth = texture(src_texture, vtx_uv).r;
})";

  auto vs_source_str = vs_source.c_str();
  vertex_program_ = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &vs_source_str);
  auto color_fs_source_str = color_fs_source.c_str();
  color_fragment_program_ =
      glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &color_fs_source_str);
  auto depth_fs_source_str = depth_fs_source.c_str();
  depth_fragment_program_ =
      glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &depth_fs_source_str);
  glCreateProgramPipelines(1, &color_pipeline_);
  glUseProgramStages(color_pipeline_, GL_VERTEX_SHADER_BIT, vertex_program_);
  glUseProgramStages(color_pipeline_, GL_FRAGMENT_SHADER_BIT,
                     color_fragment_program_);
  glCreateProgramPipelines(1, &depth_pipeline_);
  glUseProgramStages(depth_pipeline_, GL_VERTEX_SHADER_BIT, vertex_program_);
  glUseProgramStages(depth_pipeline_, GL_FRAGMENT_SHADER_BIT,
                     depth_fragment_program_);

  glCreateBuffers(1, &vbo_);
  static const GLfloat vbo_data[] = {
      0, 0, 1, 0, 0, 1, 1, 1,
  };
  glNamedBufferStorage(vbo_, sizeof(vbo_data), vbo_data, 0);

  glCreateVertexArrays(1, &vao_);
  glEnableVertexArrayAttrib(vao_, 0);
  glVertexArrayAttribBinding(vao_, 0, 0);
  glVertexArrayAttribFormat(vao_, 0, 2, GL_FLOAT, GL_FALSE, 0);
  glVertexArrayVertexBuffer(vao_, 0, vbo_, 0, sizeof(GLfloat) * 2);

  glCreateSamplers(1, &nearest_sampler_);
  glSamplerParameteri(nearest_sampler_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glSamplerParameteri(nearest_sampler_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glSamplerParameteri(nearest_sampler_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glSamplerParameteri(nearest_sampler_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glCreateSamplers(1, &linear_sampler_);
  glSamplerParameteri(linear_sampler_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glSamplerParameteri(linear_sampler_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glSamplerParameteri(linear_sampler_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glSamplerParameteri(linear_sampler_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

  glCreateFramebuffers(1, &scratch_framebuffer_);

  return true;
}

void Blitter::Shutdown() {
  glDeleteFramebuffers(1, &scratch_framebuffer_);
  glDeleteProgram(vertex_program_);
  glDeleteProgram(color_fragment_program_);
  glDeleteProgram(depth_fragment_program_);
  glDeleteProgramPipelines(1, &color_pipeline_);
  glDeleteProgramPipelines(1, &depth_pipeline_);
  glDeleteBuffers(1, &vbo_);
  glDeleteVertexArrays(1, &vao_);
  glDeleteSamplers(1, &nearest_sampler_);
  glDeleteSamplers(1, &linear_sampler_);
}

struct SavedState {
  GLboolean scissor_test_enabled;
  GLboolean depth_test_enabled;
  GLboolean depth_mask_enabled;
  GLint depth_func;
  GLboolean stencil_test_enabled;
  GLboolean cull_face_enabled;
  GLint cull_face;
  GLint front_face;
  GLint polygon_mode;
  GLboolean color_mask_0_enabled[4];
  GLboolean blend_0_enabled;
  GLint draw_buffer;
  GLfloat viewport[4];
  GLint program_pipeline;
  GLint vertex_array;
  GLint texture_0;
  GLint sampler_0;

  void Save() {
    scissor_test_enabled = glIsEnabled(GL_SCISSOR_TEST);
    depth_test_enabled = glIsEnabled(GL_DEPTH_TEST);
    glGetBooleanv(GL_DEPTH_WRITEMASK, &depth_mask_enabled);
    glGetIntegerv(GL_DEPTH_FUNC, &depth_func);
    stencil_test_enabled = glIsEnabled(GL_STENCIL_TEST);
    cull_face_enabled = glIsEnabled(GL_CULL_FACE);
    glGetIntegerv(GL_CULL_FACE_MODE, &cull_face);
    glGetIntegerv(GL_FRONT_FACE, &front_face);
    glGetIntegerv(GL_POLYGON_MODE, &polygon_mode);
    glGetBooleani_v(GL_COLOR_WRITEMASK, 0,
                    reinterpret_cast<GLboolean*>(&color_mask_0_enabled));
    blend_0_enabled = glIsEnabledi(GL_BLEND, 0);
    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_buffer);
    glGetFloati_v(GL_VIEWPORT, 0, viewport);
    glGetIntegerv(GL_PROGRAM_PIPELINE_BINDING, &program_pipeline);
    glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vertex_array);
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture_0);
    glGetIntegerv(GL_SAMPLER_BINDING, &sampler_0);
  }

  void Restore() {
    scissor_test_enabled ? glEnable(GL_SCISSOR_TEST)
                         : glDisable(GL_SCISSOR_TEST);
    depth_test_enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
    glDepthMask(depth_mask_enabled);
    glDepthFunc(depth_func);
    stencil_test_enabled ? glEnable(GL_STENCIL_TEST)
                         : glDisable(GL_STENCIL_TEST);
    cull_face_enabled ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE);
    glCullFace(cull_face);
    glFrontFace(front_face);
    glPolygonMode(GL_FRONT_AND_BACK, polygon_mode);
    glColorMaski(0, color_mask_0_enabled[0], color_mask_0_enabled[1],
                 color_mask_0_enabled[2], color_mask_0_enabled[3]);
    blend_0_enabled ? glEnablei(GL_BLEND, 0) : glDisablei(GL_BLEND, 0);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_buffer);
    glViewportIndexedf(0, viewport[0], viewport[1], viewport[2], viewport[3]);
    glBindProgramPipeline(program_pipeline);
    glBindVertexArray(vertex_array);
    glBindTexture(GL_TEXTURE_2D, texture_0);
    glBindSampler(0, sampler_0);
  }
};

void Blitter::Draw(GLuint src_texture, Rect2D src_rect, Rect2D dest_rect,
                   GLenum filter) {
  glDisable(GL_SCISSOR_TEST);
  glDisable(GL_STENCIL_TEST);
  glDisablei(GL_BLEND, 0);
  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK);
  glFrontFace(GL_CW);
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  glBindVertexArray(vao_);
  glBindTextures(0, 1, &src_texture);
  switch (filter) {
    default:
    case GL_NEAREST:
      glBindSampler(0, nearest_sampler_);
      break;
    case GL_LINEAR:
      glBindSampler(0, linear_sampler_);
      break;
  }

  glViewportIndexedf(0, GLfloat(dest_rect.x), GLfloat(dest_rect.y),
                     GLfloat(dest_rect.width), GLfloat(dest_rect.height));

  // TODO(benvanik): avoid this?
  GLint src_texture_width;
  glGetTextureLevelParameteriv(src_texture, 0, GL_TEXTURE_WIDTH,
                               &src_texture_width);
  GLint src_texture_height;
  glGetTextureLevelParameteriv(src_texture, 0, GL_TEXTURE_HEIGHT,
                               &src_texture_height);
  glProgramUniform4f(vertex_program_, 0,
                     src_rect.x / static_cast<float>(src_texture_width),
                     src_rect.y / static_cast<float>(src_texture_height),
                     src_rect.width / static_cast<float>(src_texture_width),
                     src_rect.height / static_cast<float>(src_texture_height));

  // Useful for seeing the entire framebuffer/etc:
  // glProgramUniform4f(vertex_program_, 0, 0.0f, 0.0f, 1.0f, 1.0f);

  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

void Blitter::BlitTexture2D(GLuint src_texture, Rect2D src_rect,
                            Rect2D dest_rect, GLenum filter,
                            bool swap_channels) {
  SavedState state;
  state.Save();

  glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  glDisable(GL_DEPTH_TEST);
  glDepthMask(GL_FALSE);
  glStencilMask(0xFF);
  glBindProgramPipeline(color_pipeline_);

  glProgramUniform1i(color_fragment_program_, 2, swap_channels ? 1 : 0);

  Draw(src_texture, src_rect, dest_rect, filter);

  state.Restore();
}

void Blitter::CopyColorTexture2D(GLuint src_texture, Rect2D src_rect,
                                 GLuint dest_texture, Rect2D dest_rect,
                                 GLenum filter, bool swap_channels) {
  SavedState state;
  state.Save();

  glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  glDisable(GL_DEPTH_TEST);
  glDepthMask(GL_FALSE);
  glBindProgramPipeline(color_pipeline_);

  glProgramUniform1i(color_fragment_program_, 2, swap_channels ? 1 : 0);

  glNamedFramebufferTexture(scratch_framebuffer_, GL_COLOR_ATTACHMENT0,
                            dest_texture, 0);
  glNamedFramebufferDrawBuffer(scratch_framebuffer_, GL_COLOR_ATTACHMENT0);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, scratch_framebuffer_);
  Draw(src_texture, src_rect, dest_rect, filter);
  glNamedFramebufferDrawBuffer(scratch_framebuffer_, GL_NONE);
  glNamedFramebufferTexture(scratch_framebuffer_, GL_COLOR_ATTACHMENT0, GL_NONE,
                            0);

  state.Restore();
}

void Blitter::CopyDepthTexture(GLuint src_texture, Rect2D src_rect,
                               GLuint dest_texture, Rect2D dest_rect) {
  SavedState state;
  state.Save();

  glColorMaski(0, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_ALWAYS);
  glDepthMask(GL_TRUE);
  glBindProgramPipeline(depth_pipeline_);

  glNamedFramebufferTexture(scratch_framebuffer_, GL_DEPTH_STENCIL_ATTACHMENT,
                            dest_texture, 0);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, scratch_framebuffer_);
  Draw(src_texture, src_rect, dest_rect, GL_NEAREST);
  glNamedFramebufferTexture(scratch_framebuffer_, GL_DEPTH_STENCIL_ATTACHMENT,
                            GL_NONE, 0);

  state.Restore();
}

}  // namespace gl
}  // namespace ui
}  // namespace xe
<commit_msg>Blitter::Draw assert on zero src texture<commit_after>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2015 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/ui/gl/blitter.h"

#include <string>

#include "xenia/base/assert.h"
#include "xenia/base/math.h"
#include "xenia/ui/gl/gl_context.h"

namespace xe {
namespace ui {
namespace gl {

Blitter::Blitter()
    : vertex_program_(0),
      color_fragment_program_(0),
      depth_fragment_program_(0),
      color_pipeline_(0),
      depth_pipeline_(0),
      vbo_(0),
      vao_(0),
      nearest_sampler_(0),
      linear_sampler_(0),
      scratch_framebuffer_(0) {}

Blitter::~Blitter() = default;

bool Blitter::Initialize() {
  const std::string header =
      R"(
#version 450
#extension GL_ARB_explicit_uniform_location : require
#extension GL_ARB_shading_language_420pack : require
precision highp float;
precision highp int;
layout(std140, column_major) uniform;
layout(std430, column_major) buffer;
)";
  const std::string vs_source = header +
                                R"(
layout(location = 0) uniform vec4 src_uv;
out gl_PerVertex {
  vec4 gl_Position;
  float gl_PointSize;
  float gl_ClipDistance[];
};
layout(location = 0) in vec2 vfetch_pos;
layout(location = 0) out vec2 vtx_uv;
void main() {
  gl_Position = vec4(vfetch_pos.xy * vec2(2.0, -2.0) -
                vec2(1.0, -1.0), 0.0, 1.0);
  vtx_uv = vfetch_pos.xy * src_uv.zw + src_uv.xy;
})";
  const std::string color_fs_source = header +
                                      R"(
layout(location = 1) uniform sampler2D src_texture;
layout(location = 2) uniform bool swap;
layout(location = 0) in vec2 vtx_uv;
layout(location = 0) out vec4 oC;
void main() {
  oC = texture(src_texture, vtx_uv);
  if (!swap) oC = oC.bgra;
})";
  const std::string depth_fs_source = header +
                                      R"(
layout(location = 1) uniform sampler2D src_texture;
layout(location = 0) in vec2 vtx_uv;
layout(location = 0) out vec4 oC;
void main() {
  gl_FragDepth = texture(src_texture, vtx_uv).r;
})";

  auto vs_source_str = vs_source.c_str();
  vertex_program_ = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &vs_source_str);
  auto color_fs_source_str = color_fs_source.c_str();
  color_fragment_program_ =
      glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &color_fs_source_str);
  auto depth_fs_source_str = depth_fs_source.c_str();
  depth_fragment_program_ =
      glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &depth_fs_source_str);
  glCreateProgramPipelines(1, &color_pipeline_);
  glUseProgramStages(color_pipeline_, GL_VERTEX_SHADER_BIT, vertex_program_);
  glUseProgramStages(color_pipeline_, GL_FRAGMENT_SHADER_BIT,
                     color_fragment_program_);
  glCreateProgramPipelines(1, &depth_pipeline_);
  glUseProgramStages(depth_pipeline_, GL_VERTEX_SHADER_BIT, vertex_program_);
  glUseProgramStages(depth_pipeline_, GL_FRAGMENT_SHADER_BIT,
                     depth_fragment_program_);

  glCreateBuffers(1, &vbo_);
  static const GLfloat vbo_data[] = {
      0, 0, 1, 0, 0, 1, 1, 1,
  };
  glNamedBufferStorage(vbo_, sizeof(vbo_data), vbo_data, 0);

  glCreateVertexArrays(1, &vao_);
  glEnableVertexArrayAttrib(vao_, 0);
  glVertexArrayAttribBinding(vao_, 0, 0);
  glVertexArrayAttribFormat(vao_, 0, 2, GL_FLOAT, GL_FALSE, 0);
  glVertexArrayVertexBuffer(vao_, 0, vbo_, 0, sizeof(GLfloat) * 2);

  glCreateSamplers(1, &nearest_sampler_);
  glSamplerParameteri(nearest_sampler_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glSamplerParameteri(nearest_sampler_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glSamplerParameteri(nearest_sampler_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glSamplerParameteri(nearest_sampler_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glCreateSamplers(1, &linear_sampler_);
  glSamplerParameteri(linear_sampler_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glSamplerParameteri(linear_sampler_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glSamplerParameteri(linear_sampler_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glSamplerParameteri(linear_sampler_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

  glCreateFramebuffers(1, &scratch_framebuffer_);

  return true;
}

void Blitter::Shutdown() {
  glDeleteFramebuffers(1, &scratch_framebuffer_);
  glDeleteProgram(vertex_program_);
  glDeleteProgram(color_fragment_program_);
  glDeleteProgram(depth_fragment_program_);
  glDeleteProgramPipelines(1, &color_pipeline_);
  glDeleteProgramPipelines(1, &depth_pipeline_);
  glDeleteBuffers(1, &vbo_);
  glDeleteVertexArrays(1, &vao_);
  glDeleteSamplers(1, &nearest_sampler_);
  glDeleteSamplers(1, &linear_sampler_);
}

struct SavedState {
  GLboolean scissor_test_enabled;
  GLboolean depth_test_enabled;
  GLboolean depth_mask_enabled;
  GLint depth_func;
  GLboolean stencil_test_enabled;
  GLboolean cull_face_enabled;
  GLint cull_face;
  GLint front_face;
  GLint polygon_mode;
  GLboolean color_mask_0_enabled[4];
  GLboolean blend_0_enabled;
  GLint draw_buffer;
  GLfloat viewport[4];
  GLint program_pipeline;
  GLint vertex_array;
  GLint texture_0;
  GLint sampler_0;

  void Save() {
    scissor_test_enabled = glIsEnabled(GL_SCISSOR_TEST);
    depth_test_enabled = glIsEnabled(GL_DEPTH_TEST);
    glGetBooleanv(GL_DEPTH_WRITEMASK, &depth_mask_enabled);
    glGetIntegerv(GL_DEPTH_FUNC, &depth_func);
    stencil_test_enabled = glIsEnabled(GL_STENCIL_TEST);
    cull_face_enabled = glIsEnabled(GL_CULL_FACE);
    glGetIntegerv(GL_CULL_FACE_MODE, &cull_face);
    glGetIntegerv(GL_FRONT_FACE, &front_face);
    glGetIntegerv(GL_POLYGON_MODE, &polygon_mode);
    glGetBooleani_v(GL_COLOR_WRITEMASK, 0,
                    reinterpret_cast<GLboolean*>(&color_mask_0_enabled));
    blend_0_enabled = glIsEnabledi(GL_BLEND, 0);
    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_buffer);
    glGetFloati_v(GL_VIEWPORT, 0, viewport);
    glGetIntegerv(GL_PROGRAM_PIPELINE_BINDING, &program_pipeline);
    glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vertex_array);
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture_0);
    glGetIntegerv(GL_SAMPLER_BINDING, &sampler_0);
  }

  void Restore() {
    scissor_test_enabled ? glEnable(GL_SCISSOR_TEST)
                         : glDisable(GL_SCISSOR_TEST);
    depth_test_enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
    glDepthMask(depth_mask_enabled);
    glDepthFunc(depth_func);
    stencil_test_enabled ? glEnable(GL_STENCIL_TEST)
                         : glDisable(GL_STENCIL_TEST);
    cull_face_enabled ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE);
    glCullFace(cull_face);
    glFrontFace(front_face);
    glPolygonMode(GL_FRONT_AND_BACK, polygon_mode);
    glColorMaski(0, color_mask_0_enabled[0], color_mask_0_enabled[1],
                 color_mask_0_enabled[2], color_mask_0_enabled[3]);
    blend_0_enabled ? glEnablei(GL_BLEND, 0) : glDisablei(GL_BLEND, 0);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_buffer);
    glViewportIndexedf(0, viewport[0], viewport[1], viewport[2], viewport[3]);
    glBindProgramPipeline(program_pipeline);
    glBindVertexArray(vertex_array);
    glBindTexture(GL_TEXTURE_2D, texture_0);
    glBindSampler(0, sampler_0);
  }
};

void Blitter::Draw(GLuint src_texture, Rect2D src_rect, Rect2D dest_rect,
                   GLenum filter) {
  assert_not_zero(src_texture);

  glDisable(GL_SCISSOR_TEST);
  glDisable(GL_STENCIL_TEST);
  glDisablei(GL_BLEND, 0);
  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK);
  glFrontFace(GL_CW);
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  glBindVertexArray(vao_);
  glBindTextures(0, 1, &src_texture);
  switch (filter) {
    default:
    case GL_NEAREST:
      glBindSampler(0, nearest_sampler_);
      break;
    case GL_LINEAR:
      glBindSampler(0, linear_sampler_);
      break;
  }

  glViewportIndexedf(0, GLfloat(dest_rect.x), GLfloat(dest_rect.y),
                     GLfloat(dest_rect.width), GLfloat(dest_rect.height));

  // TODO(benvanik): avoid this?
  GLint src_texture_width;
  glGetTextureLevelParameteriv(src_texture, 0, GL_TEXTURE_WIDTH,
                               &src_texture_width);
  GLint src_texture_height;
  glGetTextureLevelParameteriv(src_texture, 0, GL_TEXTURE_HEIGHT,
                               &src_texture_height);
  glProgramUniform4f(vertex_program_, 0,
                     src_rect.x / static_cast<float>(src_texture_width),
                     src_rect.y / static_cast<float>(src_texture_height),
                     src_rect.width / static_cast<float>(src_texture_width),
                     src_rect.height / static_cast<float>(src_texture_height));

  // Useful for seeing the entire framebuffer/etc:
  // glProgramUniform4f(vertex_program_, 0, 0.0f, 0.0f, 1.0f, 1.0f);

  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

void Blitter::BlitTexture2D(GLuint src_texture, Rect2D src_rect,
                            Rect2D dest_rect, GLenum filter,
                            bool swap_channels) {
  SavedState state;
  state.Save();

  glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  glDisable(GL_DEPTH_TEST);
  glDepthMask(GL_FALSE);
  glStencilMask(0xFF);
  glBindProgramPipeline(color_pipeline_);

  glProgramUniform1i(color_fragment_program_, 2, swap_channels ? 1 : 0);

  Draw(src_texture, src_rect, dest_rect, filter);

  state.Restore();
}

void Blitter::CopyColorTexture2D(GLuint src_texture, Rect2D src_rect,
                                 GLuint dest_texture, Rect2D dest_rect,
                                 GLenum filter, bool swap_channels) {
  SavedState state;
  state.Save();

  glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  glDisable(GL_DEPTH_TEST);
  glDepthMask(GL_FALSE);
  glBindProgramPipeline(color_pipeline_);

  glProgramUniform1i(color_fragment_program_, 2, swap_channels ? 1 : 0);

  glNamedFramebufferTexture(scratch_framebuffer_, GL_COLOR_ATTACHMENT0,
                            dest_texture, 0);
  glNamedFramebufferDrawBuffer(scratch_framebuffer_, GL_COLOR_ATTACHMENT0);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, scratch_framebuffer_);
  Draw(src_texture, src_rect, dest_rect, filter);
  glNamedFramebufferDrawBuffer(scratch_framebuffer_, GL_NONE);
  glNamedFramebufferTexture(scratch_framebuffer_, GL_COLOR_ATTACHMENT0, GL_NONE,
                            0);

  state.Restore();
}

void Blitter::CopyDepthTexture(GLuint src_texture, Rect2D src_rect,
                               GLuint dest_texture, Rect2D dest_rect) {
  SavedState state;
  state.Save();

  glColorMaski(0, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_ALWAYS);
  glDepthMask(GL_TRUE);
  glBindProgramPipeline(depth_pipeline_);

  glNamedFramebufferTexture(scratch_framebuffer_, GL_DEPTH_STENCIL_ATTACHMENT,
                            dest_texture, 0);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, scratch_framebuffer_);
  Draw(src_texture, src_rect, dest_rect, GL_NEAREST);
  glNamedFramebufferTexture(scratch_framebuffer_, GL_DEPTH_STENCIL_ATTACHMENT,
                            GL_NONE, 0);

  state.Restore();
}

}  // namespace gl
}  // namespace ui
}  // namespace xe
<|endoftext|>