<commit_before>/* Various utility definitions for libpqxx.
 *
 * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/util instead.
 *
 * Copyright (c) 2000-2019, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this mistake,
 * or contact the author.
 */
#ifndef PQXX_H_UTIL
#define PQXX_H_UTIL

#include "pqxx/compiler-public.hxx"

#include <cstdio>
#include <cctype>
#include <iterator>
#include <memory>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <typeinfo>
#include <vector>

#include "pqxx/types.hxx"
#include "pqxx/version.hxx"

/// The home of all libpqxx classes, functions, templates, etc.
namespace pqxx {}

#include <pqxx/internal/libpq-forward.hxx>

namespace pqxx
{
/// Suppress compiler warning about an unused item.
template<typename T> inline void ignore_unused(T &&) {}

/** Check library version at link time.
 *
 * Ensures a failure when linking an application against a radically
 * different libpqxx version than the one against which it was compiled.
 *
 * Sometimes application builds fail in unclear ways because they compile
 * using headers from libpqxx version X, but then link against libpqxx
 * binary version Y.  A typical scenario would be one where you're building
 * against a libpqxx which you have built yourself, but a different version
 * is installed on the system.
 *
 * The check_library_version template is declared for any library version,
 * but only actually defined for the version of the libpqxx binary against
 * which the code is linked.
 *
 * If the library binary is a different version than the one declared in
 * these headers, then this call will fail to link: there will be no
 * definition for the function with these exact template parameter values.
 * There will be a definition, but the version in the parameter values will
 * be different.
 */
inline PQXX_PRIVATE void check_version()
{
  // There is no particular reason to do this here in @c connection, except
  // to ensure that every meaningful libpqxx client will execute it.  The call
  // must be in the execution path somewhere or the compiler won't try to link
  // it.  We can't use it to initialise a global or class-static variable,
  // because a smart compiler might resolve it at compile time.
  //
  // On the other hand, we don't want to make a useless function call too
  // often for performance reasons.  A local static variable is initialised
  // only on the definition's first execution.  Compilers will be well
  // optimised for this behaviour, so there's a minimal one-time cost.
  static const auto version_ok =
    internal::check_library_version<PQXX_VERSION_MAJOR, PQXX_VERSION_MINOR>();
  ignore_unused(version_ok);
}

/// Descriptor of library's thread-safety model.
/** This describes what the library knows about various risks to thread-safety.
 */
struct PQXX_LIBEXPORT thread_safety_model
{
  /// Is the underlying libpq build thread-safe?
  bool safe_libpq;

  /// Is Kerberos thread-safe?
  /** @warning Is currently always @c false.
   *
   * If your application uses Kerberos, all accesses to libpqxx or Kerberos must
   * be serialized.  Confine their use to a single thread, or protect it with a
   * global lock.
   */
  bool safe_kerberos;

  /// A human-readable description of any thread-safety issues.
  std::string description;
};

/// Describe thread safety available in this build.
PQXX_LIBEXPORT thread_safety_model describe_thread_safety();

/// The "null" oid.
constexpr oid oid_none = 0;
} // namespace pqxx

// Include this late, so strconv can also include ignore_unused from here.
#include "pqxx/strconv.hxx"

namespace pqxx
{
/**
 * @defgroup utility Utility functions
 */
//@{

/// Represent sequence of values as a string, joined by a given separator.
/**
 * Use this to turn e.g. the numbers 1, 2, and 3 into a string "1, 2, 3".
 *
 * @param sep separator string (to be placed between items)
 * @param begin beginning of items sequence
 * @param end end of items sequence
 * @param access functor defining how to dereference sequence elements
 */
template<typename ITER, typename ACCESS> inline std::string
separated_list(								//[t00]
	std::string_view sep,
	ITER begin,
	ITER end,
	ACCESS access)
{
  std::string result;
// TODO: Can we pre-compute optimal length for result?
  if (begin != end)
  {
    result = to_string(access(begin));
    for (++begin; begin != end; ++begin)
    {
      result += sep;
      result += to_string(access(begin));
    }
  }
  return result;
}

/// Render sequence as a string, using given separator between items.
template<typename ITER> inline std::string
separated_list(std::string_view sep, ITER begin, ITER end)		//[t00]
	{ return separated_list(sep, begin, end, [](ITER i){ return *i; }); }

/// Render items in a container as a string, using given separator.
template<typename CONTAINER> inline auto
separated_list(std::string_view sep, const CONTAINER &c)		//[t10]
	/*
	Always std::string; necessary because SFINAE doesn't work with the
	contents of function bodies, so the check for iterability has to be in
	the signature.
	*/
	-> typename std::enable_if<
		(
			not std::is_void<decltype(std::begin(c))>::value
			and not std::is_void<decltype(std::end(c))>::value
		),
		std::string
	>::type
{
  return separated_list(sep, std::begin(c), std::end(c));
}

/// Render items in a tuple as a string, using given separator.
template<
	typename TUPLE,
	std::size_t INDEX=0,
	typename ACCESS,
	typename std::enable_if<
		(INDEX == std::tuple_size<TUPLE>::value-1),
		int
	>::type=0
>
inline std::string
separated_list(
	std::string_view /* sep */,
	const TUPLE &t,
	const ACCESS& access
)
{
  return to_string(access(&std::get<INDEX>(t)));
}

template<
	typename TUPLE,
	std::size_t INDEX=0,
	typename ACCESS,
	typename std::enable_if<
		(INDEX < std::tuple_size<TUPLE>::value-1),
		int
	>::type=0
>
inline std::string
separated_list(std::string_view sep, const TUPLE &t, const ACCESS& access)
{
  std::string out{to_string(access(&std::get<INDEX>(t)))};
  out.append(sep);
  out.append(separated_list<TUPLE, INDEX+1>(sep, t, access));
  return out;
}

template<
	typename TUPLE,
	std::size_t INDEX=0,
	typename std::enable_if<
		(INDEX <= std::tuple_size<TUPLE>::value),
		int
	>::type=0
>
inline std::string
separated_list(std::string_view sep, const TUPLE &t)
{
  return separated_list(sep, t, [](const TUPLE &tup){return *tup;});
}
//@}
} // namespace pqxx

/// Private namespace for libpqxx's internal use; do not access.
/** This namespace hides definitions internal to libpqxx.  These are not
 * supposed to be used by client programs, and they may change at any time
 * without notice.
 *
 * Conversely, if you find something in this namespace tremendously useful, by
 * all means do lodge a request for its publication.
 *
 * @warning Here be dragons!
 */
namespace pqxx::internal
{
PQXX_LIBEXPORT void freepqmem(const void *) noexcept;
template<typename P> inline void freepqmem_templated(P *p) noexcept
{
  freepqmem(p);
}

PQXX_LIBEXPORT void freemallocmem(const void *) noexcept;
template<typename P> inline void freemallocmem_templated(P *p) noexcept
{
  freemallocmem(p);
}

/// Helper base class: object descriptions for error messages and such.
/**
 * Classes derived from namedclass have a class name (such as "transaction"),
 * an optional object name (such as "delete-old-logs"), and a description
 * generated from the two names (such as "transaction delete-old-logs").
 *
 * The class name is dynamic here, in order to support inheritance hierarchies
 * where the exact class name may not be known statically.
 *
 * In inheritance hierarchies, make namedclass a virtual base class so that
 * each class in the hierarchy can specify its own class name in its
 * constructors.
 */
class PQXX_LIBEXPORT namedclass
{
public:
  explicit namedclass(const std::string &Classname) :
    m_classname{Classname},
    m_name{}
  {
  }

  namedclass(const std::string &Classname, const std::string &Name) :
    m_classname{Classname},
    m_name{Name}
  {
  }

  /// Object name, or the empty string if no name was given.
  const std::string &name() const noexcept { return m_name; }		//[t01]

  /// Class name.
  const std::string &classname() const noexcept
	{ return m_classname; }

  /// Combination of class name and object name; or just class name.
  std::string description() const;

private:
  std::string m_classname, m_name;
};

PQXX_PRIVATE void check_unique_registration(
        const namedclass *new_ptr, const namedclass *old_ptr);
PQXX_PRIVATE void check_unique_unregistration(
        const namedclass *new_ptr, const namedclass *old_ptr);

/// Ensure proper opening/closing of GUEST objects related to a "host" object
/** Only a single GUEST may exist for a single host at any given time.  GUEST
 * must be derived from namedclass.
 */
template<typename GUEST>
class unique
{
public:
  constexpr unique() =default;
  constexpr unique(const unique &) =delete;
  constexpr unique(unique &&rhs) : m_guest(rhs.m_guest) { rhs.m_guest = nullptr; }
  constexpr unique &operator=(const unique &) =delete;
  constexpr unique &operator=(unique &&rhs)
  {
    m_guest = rhs.m_guest;
    rhs.m_guest = nullptr;
    return *this;
  }

  constexpr GUEST *get() const noexcept { return m_guest; }

  constexpr void register_guest(GUEST *G)
  {
    check_unique_registration(G, m_guest);
    m_guest = G;
  }

  constexpr void unregister_guest(GUEST *G)
  {
    check_unique_unregistration(G, m_guest);
    m_guest = nullptr;
  }

private:
  GUEST *m_guest = nullptr;
};

/// Sleep for the given number of seconds
/** May return early, e.g. when interrupted by a signal.  Completes instantly if
 * a zero or negative sleep time is requested.
 */
PQXX_LIBEXPORT void sleep_seconds(int);

} // namespace pqxx::internal

#endif
<commit_msg>Fix missing 'pre' header.<commit_after>/* Various utility definitions for libpqxx.
 *
 * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/util instead.
 *
 * Copyright (c) 2000-2019, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this mistake,
 * or contact the author.
 */
#ifndef PQXX_H_UTIL
#define PQXX_H_UTIL

#include "pqxx/compiler-public.hxx"
#include "pqxx/internal/compiler-internal-pre.hxx"

#include <cstdio>
#include <cctype>
#include <iterator>
#include <memory>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <typeinfo>
#include <vector>

#include "pqxx/types.hxx"
#include "pqxx/version.hxx"

/// The home of all libpqxx classes, functions, templates, etc.
namespace pqxx {}

#include <pqxx/internal/libpq-forward.hxx>

namespace pqxx
{
/// Suppress compiler warning about an unused item.
template<typename T> inline void ignore_unused(T &&) {}

/** Check library version at link time.
 *
 * Ensures a failure when linking an application against a radically
 * different libpqxx version than the one against which it was compiled.
 *
 * Sometimes application builds fail in unclear ways because they compile
 * using headers from libpqxx version X, but then link against libpqxx
 * binary version Y.  A typical scenario would be one where you're building
 * against a libpqxx which you have built yourself, but a different version
 * is installed on the system.
 *
 * The check_library_version template is declared for any library version,
 * but only actually defined for the version of the libpqxx binary against
 * which the code is linked.
 *
 * If the library binary is a different version than the one declared in
 * these headers, then this call will fail to link: there will be no
 * definition for the function with these exact template parameter values.
 * There will be a definition, but the version in the parameter values will
 * be different.
 */
inline PQXX_PRIVATE void check_version()
{
  // There is no particular reason to do this here in @c connection, except
  // to ensure that every meaningful libpqxx client will execute it.  The call
  // must be in the execution path somewhere or the compiler won't try to link
  // it.  We can't use it to initialise a global or class-static variable,
  // because a smart compiler might resolve it at compile time.
  //
  // On the other hand, we don't want to make a useless function call too
  // often for performance reasons.  A local static variable is initialised
  // only on the definition's first execution.  Compilers will be well
  // optimised for this behaviour, so there's a minimal one-time cost.
  static const auto version_ok =
    internal::check_library_version<PQXX_VERSION_MAJOR, PQXX_VERSION_MINOR>();
  ignore_unused(version_ok);
}

/// Descriptor of library's thread-safety model.
/** This describes what the library knows about various risks to thread-safety.
 */
struct PQXX_LIBEXPORT thread_safety_model
{
  /// Is the underlying libpq build thread-safe?
  bool safe_libpq;

  /// Is Kerberos thread-safe?
  /** @warning Is currently always @c false.
   *
   * If your application uses Kerberos, all accesses to libpqxx or Kerberos must
   * be serialized.  Confine their use to a single thread, or protect it with a
   * global lock.
   */
  bool safe_kerberos;

  /// A human-readable description of any thread-safety issues.
  std::string description;
};

/// Describe thread safety available in this build.
PQXX_LIBEXPORT thread_safety_model describe_thread_safety();

/// The "null" oid.
constexpr oid oid_none = 0;
} // namespace pqxx

// Include this late, so strconv can also include ignore_unused from here.
#include "pqxx/strconv.hxx"

namespace pqxx
{
/**
 * @defgroup utility Utility functions
 */
//@{

/// Represent sequence of values as a string, joined by a given separator.
/**
 * Use this to turn e.g. the numbers 1, 2, and 3 into a string "1, 2, 3".
 *
 * @param sep separator string (to be placed between items)
 * @param begin beginning of items sequence
 * @param end end of items sequence
 * @param access functor defining how to dereference sequence elements
 */
template<typename ITER, typename ACCESS> inline std::string
separated_list(								//[t00]
	std::string_view sep,
	ITER begin,
	ITER end,
	ACCESS access)
{
  std::string result;
// TODO: Can we pre-compute optimal length for result?
  if (begin != end)
  {
    result = to_string(access(begin));
    for (++begin; begin != end; ++begin)
    {
      result += sep;
      result += to_string(access(begin));
    }
  }
  return result;
}

/// Render sequence as a string, using given separator between items.
template<typename ITER> inline std::string
separated_list(std::string_view sep, ITER begin, ITER end)		//[t00]
	{ return separated_list(sep, begin, end, [](ITER i){ return *i; }); }

/// Render items in a container as a string, using given separator.
template<typename CONTAINER> inline auto
separated_list(std::string_view sep, const CONTAINER &c)		//[t10]
	/*
	Always std::string; necessary because SFINAE doesn't work with the
	contents of function bodies, so the check for iterability has to be in
	the signature.
	*/
	-> typename std::enable_if<
		(
			not std::is_void<decltype(std::begin(c))>::value
			and not std::is_void<decltype(std::end(c))>::value
		),
		std::string
	>::type
{
  return separated_list(sep, std::begin(c), std::end(c));
}

/// Render items in a tuple as a string, using given separator.
template<
	typename TUPLE,
	std::size_t INDEX=0,
	typename ACCESS,
	typename std::enable_if<
		(INDEX == std::tuple_size<TUPLE>::value-1),
		int
	>::type=0
>
inline std::string
separated_list(
	std::string_view /* sep */,
	const TUPLE &t,
	const ACCESS& access
)
{
  return to_string(access(&std::get<INDEX>(t)));
}

template<
	typename TUPLE,
	std::size_t INDEX=0,
	typename ACCESS,
	typename std::enable_if<
		(INDEX < std::tuple_size<TUPLE>::value-1),
		int
	>::type=0
>
inline std::string
separated_list(std::string_view sep, const TUPLE &t, const ACCESS& access)
{
  std::string out{to_string(access(&std::get<INDEX>(t)))};
  out.append(sep);
  out.append(separated_list<TUPLE, INDEX+1>(sep, t, access));
  return out;
}

template<
	typename TUPLE,
	std::size_t INDEX=0,
	typename std::enable_if<
		(INDEX <= std::tuple_size<TUPLE>::value),
		int
	>::type=0
>
inline std::string
separated_list(std::string_view sep, const TUPLE &t)
{
  return separated_list(sep, t, [](const TUPLE &tup){return *tup;});
}
//@}
} // namespace pqxx

/// Private namespace for libpqxx's internal use; do not access.
/** This namespace hides definitions internal to libpqxx.  These are not
 * supposed to be used by client programs, and they may change at any time
 * without notice.
 *
 * Conversely, if you find something in this namespace tremendously useful, by
 * all means do lodge a request for its publication.
 *
 * @warning Here be dragons!
 */
namespace pqxx::internal
{
PQXX_LIBEXPORT void freepqmem(const void *) noexcept;
template<typename P> inline void freepqmem_templated(P *p) noexcept
{
  freepqmem(p);
}

PQXX_LIBEXPORT void freemallocmem(const void *) noexcept;
template<typename P> inline void freemallocmem_templated(P *p) noexcept
{
  freemallocmem(p);
}

/// Helper base class: object descriptions for error messages and such.
/**
 * Classes derived from namedclass have a class name (such as "transaction"),
 * an optional object name (such as "delete-old-logs"), and a description
 * generated from the two names (such as "transaction delete-old-logs").
 *
 * The class name is dynamic here, in order to support inheritance hierarchies
 * where the exact class name may not be known statically.
 *
 * In inheritance hierarchies, make namedclass a virtual base class so that
 * each class in the hierarchy can specify its own class name in its
 * constructors.
 */
class PQXX_LIBEXPORT namedclass
{
public:
  explicit namedclass(const std::string &Classname) :
    m_classname{Classname},
    m_name{}
  {
  }

  namedclass(const std::string &Classname, const std::string &Name) :
    m_classname{Classname},
    m_name{Name}
  {
  }

  /// Object name, or the empty string if no name was given.
  const std::string &name() const noexcept { return m_name; }		//[t01]

  /// Class name.
  const std::string &classname() const noexcept
	{ return m_classname; }

  /// Combination of class name and object name; or just class name.
  std::string description() const;

private:
  std::string m_classname, m_name;
};

PQXX_PRIVATE void check_unique_registration(
        const namedclass *new_ptr, const namedclass *old_ptr);
PQXX_PRIVATE void check_unique_unregistration(
        const namedclass *new_ptr, const namedclass *old_ptr);

/// Ensure proper opening/closing of GUEST objects related to a "host" object
/** Only a single GUEST may exist for a single host at any given time.  GUEST
 * must be derived from namedclass.
 */
template<typename GUEST>
class unique
{
public:
  constexpr unique() =default;
  constexpr unique(const unique &) =delete;
  constexpr unique(unique &&rhs) : m_guest(rhs.m_guest) { rhs.m_guest = nullptr; }
  constexpr unique &operator=(const unique &) =delete;
  constexpr unique &operator=(unique &&rhs)
  {
    m_guest = rhs.m_guest;
    rhs.m_guest = nullptr;
    return *this;
  }

  constexpr GUEST *get() const noexcept { return m_guest; }

  constexpr void register_guest(GUEST *G)
  {
    check_unique_registration(G, m_guest);
    m_guest = G;
  }

  constexpr void unregister_guest(GUEST *G)
  {
    check_unique_unregistration(G, m_guest);
    m_guest = nullptr;
  }

private:
  GUEST *m_guest = nullptr;
};

/// Sleep for the given number of seconds
/** May return early, e.g. when interrupted by a signal.  Completes instantly if
 * a zero or negative sleep time is requested.
 */
PQXX_LIBEXPORT void sleep_seconds(int);

} // namespace pqxx::internal

#include "pqxx/internal/compiler-internal-post.hxx"
#endif
<|endoftext|>


<commit_before>/* Copyright 2009 SPARTA, Inc., dba Cobham Analytic Solutions
 *
 * This file is part of WATCHER.
 *
 *     WATCHER is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     WATCHER is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Watcher.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Connects to a Watcher server and writes the event stream to a KML file suitable for use
 * with Google Earth.
 *
 * @author michael.elkins@cobham.com
 */

#include <unistd.h>
#include <getopt.h>
#include <stdint.h>

#include <cassert>
#include <iostream>
#include <cstdlib>

#include "logger.h"
#include "initConfig.h"
#include "singletonConfig.h"
#include "libwatcher/messageStream.h"
#include "libwatcher/watcherGraph.h"

using namespace watcher;

void write_kml(const WatcherGraph& graph, const std::string& outputFile); // kml.cc

namespace {

//arguments to getopt_long()
const option OPTIONS[] = {
    { "config", required_argument, 0, 'c' },
    { "help", no_argument, 0, 'h' },
    { "output-file", required_argument, 0, 'o' },
    { "refresh", required_argument, 0, 'r' },
    { "seek", required_argument, 0, 'S' },
    { 0, 0, 0, 0 }
};

#define TOOL_NAME "earthWatcher"
const char *CONFIG_FILE = TOOL_NAME ".cfg";
const char *OUTPUT_FILE = "watcher.kml";
const char *PROPERTY_FILE = TOOL_NAME ".log.properties";
const unsigned int DEFAULT_REFRESH = 1; // SECONDS

void usage()
{
    const char *SEP = "\t\t";   // separator for argument/description columns

    std::cout << "usage: earthWatcher [ -h ] [ -c FILE ]\n"
        "  -c, --config FILE" << SEP << "specify a configuration file (default: " << CONFIG_FILE << ")\n"
        "  -h, --help\t" << SEP << "display this help message\n"
        "  -o, --output-file FILE" << SEP << "specifies the output KML file (default: " << OUTPUT_FILE << ")\n"
        "  -r, --refresh SECS" << SEP << "write the the output every SECS seconds (default: " << DEFAULT_REFRESH << ")\n"
        "  -S, --seek POS" << SEP << "start event playback at timestamp POS (default: -1)\n"
        << std::endl;
}

} // end namespace

DECLARE_GLOBAL_LOGGER(TOOL_NAME);

int main(int argc, char **argv)
{
    TRACE_ENTER();

    const char *output_file = 0;
    Timestamp start_timestamp = -1 ; // default to EOF (live playback)
    unsigned int refresh = DEFAULT_REFRESH;

    for (int i; (i = getopt_long(argc, argv, "hc:o:r:S:", OPTIONS, 0)) != -1; ) {
        switch (i) {
            case 'c':
                break; //handled by initConfig()

            case 'o': // output-file
                output_file = optarg;
                break;

            case 'r':
                refresh = atoi(optarg);
                break;

            case 'S':
                start_timestamp = atol(optarg);
                break;

            case 'h':
            default:
                usage();
                TRACE_EXIT_RET(EXIT_SUCCESS);
                return EXIT_SUCCESS;
                break;
        }
    }

    libconfig::Config& config = SingletonConfig::instance();
    SingletonConfig::lock();
    std::string configFilename; //filled by initConfig()
    if (! watcher::initConfig(config, argc, argv, configFilename))
        std::cout << "Configuration file not found. Creating new configuration file and using default runtime values." << std::endl;
    SingletonConfig::unlock();

    std::string logConf(PROPERTY_FILE);
    if (!config.lookupValue("logPropertiesFile", logConf))
    {
        std::cout << "Unable to find logPropertiesFile setting in the configuration file, using default: " << logConf
            << " and adding it to the configuration file." << std::endl;
        config.getRoot().add("logPropertiesFile", libconfig::Setting::TypeString) = logConf;
    }

    LOAD_LOG_PROPS(logConf);
    LOG_INFO("Logger initialized from file \"" << logConf << "\"");

    std::string serverName("127.0.0.1");
    if (!config.lookupValue("server", serverName))
    {
        LOG_INFO("'server' not found in the configuration file, using default: " << serverName
                << " and adding this to the configuration file.");
        config.getRoot().add("server", libconfig::Setting::TypeString) = serverName;
    }

    std::string service("watcherd");
    if (!config.lookupValue("service", service))
    {
        LOG_INFO("'service' not found in the configuration file, using default: " << service
                << " and adding this to the configuration file.");
        config.getRoot().add("service", libconfig::Setting::TypeString) = service;
    }

    std::string outputFile(OUTPUT_FILE);
    if (output_file)
        outputFile = output_file; //initialize from user supplied argument
    else if (!config.lookupValue("outputFile", outputFile)) {
        LOG_INFO("'outputFile' not found in the configuration file, using default: " << outputFile
                 << " and adding this to the configuration file.");
        config.getRoot().add("outputFile", libconfig::Setting::TypeString) = outputFile;
    }

    // open a message stream of live events for now
    MessageStreamPtr ms(MessageStream::createNewMessageStream(serverName, service, start_timestamp));
    if (!ms) {
        LOG_FATAL("Unable to create new message stream to server \"" << serverName << "\" using service (or port) \"" << service);
        TRACE_EXIT_RET(EXIT_FAILURE);
        return EXIT_FAILURE;
    }

    LOG_INFO("Starting event playback");
    ms->startStream();

    srandom(time(0));//we use random() to select the icon below
    WatcherGraph graph;

    LOG_INFO("Waiting for events ");
    unsigned int messageNumber = 0;
    MessagePtr mp;
    time_t last_output = 0; // counter to allow for writing the kml file on a fixed time interval
    bool changed = false;
    while (ms->getNextMessage(mp)) {
        std::cout << "Message #" << (++messageNumber) << ": " << *mp << std::endl;
        changed |= graph.updateGraph(mp);

        if (changed) {
            time_t now = time(0);
            if (now - last_output >= refresh) {
                LOG_DEBUG("writing kml file");
                last_output = now;
                write_kml(graph, outputFile);
            }
            changed = false; // reset flag
        }
    }

    // Save any configuration changes made during the run.
    LOG_INFO("Saving last known configuration to " << configFilename);
    SingletonConfig::lock();
    config.writeFile(configFilename.c_str());

    TRACE_EXIT_RET(0);
    return 0;
}
<commit_msg>call WatcherGraph::doMaintainence() prior to write_kml() to cull expired edges and keep the KML file small<commit_after>/* Copyright 2009 SPARTA, Inc., dba Cobham Analytic Solutions
 *
 * This file is part of WATCHER.
 *
 *     WATCHER is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     WATCHER is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Watcher.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Connects to a Watcher server and writes the event stream to a KML file suitable for use
 * with Google Earth.
 *
 * @author michael.elkins@cobham.com
 */

#include <unistd.h>
#include <getopt.h>
#include <stdint.h>

#include <cassert>
#include <iostream>
#include <cstdlib>

#include "logger.h"
#include "initConfig.h"
#include "singletonConfig.h"
#include "libwatcher/messageStream.h"
#include "libwatcher/watcherGraph.h"

using namespace watcher;

void write_kml(const WatcherGraph& graph, const std::string& outputFile); // kml.cc

namespace {

//arguments to getopt_long()
const option OPTIONS[] = {
    { "config", required_argument, 0, 'c' },
    { "help", no_argument, 0, 'h' },
    { "output-file", required_argument, 0, 'o' },
    { "refresh", required_argument, 0, 'r' },
    { "seek", required_argument, 0, 'S' },
    { 0, 0, 0, 0 }
};

#define TOOL_NAME "earthWatcher"
const char *CONFIG_FILE = TOOL_NAME ".cfg";
const char *OUTPUT_FILE = "watcher.kml";
const char *PROPERTY_FILE = TOOL_NAME ".log.properties";
const unsigned int DEFAULT_REFRESH = 1; // SECONDS

void usage()
{
    const char *SEP = "\t\t";   // separator for argument/description columns

    std::cout << "usage: earthWatcher [ -h ] [ -c FILE ]\n"
        "  -c, --config FILE" << SEP << "specify a configuration file (default: " << CONFIG_FILE << ")\n"
        "  -h, --help\t" << SEP << "display this help message\n"
        "  -o, --output-file FILE" << SEP << "specifies the output KML file (default: " << OUTPUT_FILE << ")\n"
        "  -r, --refresh SECS" << SEP << "write the the output every SECS seconds (default: " << DEFAULT_REFRESH << ")\n"
        "  -S, --seek POS" << SEP << "start event playback at timestamp POS (default: -1)\n"
        << std::endl;
}

} // end namespace

DECLARE_GLOBAL_LOGGER(TOOL_NAME);

int main(int argc, char **argv)
{
    TRACE_ENTER();

    const char *output_file = 0;
    Timestamp start_timestamp = -1 ; // default to EOF (live playback)
    unsigned int refresh = DEFAULT_REFRESH;

    for (int i; (i = getopt_long(argc, argv, "hc:o:r:S:", OPTIONS, 0)) != -1; ) {
        switch (i) {
            case 'c':
                break; //handled by initConfig()

            case 'o': // output-file
                output_file = optarg;
                break;

            case 'r':
                refresh = atoi(optarg);
                break;

            case 'S':
                start_timestamp = atol(optarg);
                break;

            case 'h':
            default:
                usage();
                TRACE_EXIT_RET(EXIT_SUCCESS);
                return EXIT_SUCCESS;
                break;
        }
    }

    libconfig::Config& config = SingletonConfig::instance();
    SingletonConfig::lock();
    std::string configFilename; //filled by initConfig()
    if (! watcher::initConfig(config, argc, argv, configFilename))
        std::cout << "Configuration file not found. Creating new configuration file and using default runtime values." << std::endl;
    SingletonConfig::unlock();

    std::string logConf(PROPERTY_FILE);
    if (!config.lookupValue("logPropertiesFile", logConf))
    {
        std::cout << "Unable to find logPropertiesFile setting in the configuration file, using default: " << logConf
            << " and adding it to the configuration file." << std::endl;
        config.getRoot().add("logPropertiesFile", libconfig::Setting::TypeString) = logConf;
    }

    LOAD_LOG_PROPS(logConf);
    LOG_INFO("Logger initialized from file \"" << logConf << "\"");

    std::string serverName("127.0.0.1");
    if (!config.lookupValue("server", serverName))
    {
        LOG_INFO("'server' not found in the configuration file, using default: " << serverName
                << " and adding this to the configuration file.");
        config.getRoot().add("server", libconfig::Setting::TypeString) = serverName;
    }

    std::string service("watcherd");
    if (!config.lookupValue("service", service))
    {
        LOG_INFO("'service' not found in the configuration file, using default: " << service
                << " and adding this to the configuration file.");
        config.getRoot().add("service", libconfig::Setting::TypeString) = service;
    }

    std::string outputFile(OUTPUT_FILE);
    if (output_file)
        outputFile = output_file; //initialize from user supplied argument
    else if (!config.lookupValue("outputFile", outputFile)) {
        LOG_INFO("'outputFile' not found in the configuration file, using default: " << outputFile
                 << " and adding this to the configuration file.");
        config.getRoot().add("outputFile", libconfig::Setting::TypeString) = outputFile;
    }

    // open a message stream of live events for now
    MessageStreamPtr ms(MessageStream::createNewMessageStream(serverName, service, start_timestamp));
    if (!ms) {
        LOG_FATAL("Unable to create new message stream to server \"" << serverName << "\" using service (or port) \"" << service);
        TRACE_EXIT_RET(EXIT_FAILURE);
        return EXIT_FAILURE;
    }

    LOG_INFO("Starting event playback");
    ms->startStream();

    srandom(time(0));//we use random() to select the icon below
    WatcherGraph graph;

    LOG_INFO("Waiting for events ");
    unsigned int messageNumber = 0;
    MessagePtr mp;
    time_t last_output = 0; // counter to allow for writing the kml file on a fixed time interval
    bool changed = false;
    while (ms->getNextMessage(mp)) {
        std::cout << "Message #" << (++messageNumber) << ": " << *mp << std::endl;
        changed |= graph.updateGraph(mp);

        if (changed) {
            time_t now = time(0);
            if (now - last_output >= refresh) {
                graph.doMaintanence(); // expire stale links
                LOG_DEBUG("writing kml file");
                last_output = now;
                write_kml(graph, outputFile);
            }
            changed = false; // reset flag
        }
    }

    // Save any configuration changes made during the run.
    LOG_INFO("Saving last known configuration to " << configFilename);
    SingletonConfig::lock();
    config.writeFile(configFilename.c_str());

    TRACE_EXIT_RET(0);
    return 0;
}
<|endoftext|>


<commit_before>#include <array>

#include <boost/asio.hpp>

#include <urdl/option_set.hpp>
#include <urdl/read_stream.hpp>

#include "../global.hpp"

namespace urlfetch {

struct request_t {
    std::string url;
    urdl::option_set options;
    long timeout;
};

struct response_t {
    std::string data;
};

template<class Stream = urdl::read_stream>
class task_t : public std::enable_shared_from_this<task_t<Stream>> {
public:
    typedef Stream stream_type;
    typedef boost::asio::io_service loop_type;
    typedef std::function<
        void(request_t&&, response_t&&, const boost::system::error_code&)
    > callback_type;

private:
    std::array<char, 4096> buffer;

    request_t request;
    response_t response;
    callback_type callback;
    stream_type stream_;

public:
    task_t(request_t request, callback_type callback, loop_type& loop) :
        request(std::move(request)),
        callback(std::move(callback)),
        stream_(loop)
    {}

    stream_type& stream() {
        return stream_;
    }

    void run() {
        //!@todo: do nothing if the stream is active.
        stream_.async_open(
            request.url,
            std::bind(
                &task_t::on_open,
                this->shared_from_this(),
                std::placeholders::_1
            )
        );
    }

private:
    void on_open(const boost::system::error_code& ec) {
        std::cout << "on_open" << std::endl;
        //!@todo:
//        if (ec) {
//            callback(url, "", ec);
//            return;
//        }

        post_read();
    }

    void on_read(const boost::system::error_code& ec, std::size_t length) {
        std::cout << "on_read" << std::endl;
        if (ec) {
            //!@todo: timer.cancel();
            if (ec.value() == boost::asio::error::eof) {
                callback(
                    std::move(request),
                    std::move(response),
                    boost::system::error_code()
                );
            } else {
                //!@todo:
//                callback(
//                    std::move(request),
//                    std::move(response),
//                    ec
//                );
            }

            return;
        }

        response.data.append(buffer.begin(), buffer.begin() + length);
        post_read();
    }

    void post_read() {
        stream_.async_read_some(
            boost::asio::buffer(buffer),
            std::bind(
                &task_t::on_read,
                this->shared_from_this(),
                std::placeholders::_1,
                std::placeholders::_2
            )
        );
    }
};

} // namespace urlfetch

namespace mock {

class stream_t {
public:
    stream_t(boost::asio::io_service&) {}

    MOCK_METHOD2(
        async_open,
        void(
            const urdl::url&,
            std::function<void(const boost::system::error_code&)>
        )
    );

    MOCK_METHOD2(
        async_read_some,
        void(
            boost::asio::mutable_buffer,
            std::function<void(const boost::system::error_code&, std::size_t)>
        )
    );
};

} // namespace mock

TEST(request_t, Class) {
    urlfetch::request_t request;
    UNUSED(request);
}

TEST(urlfetch_t, Class) {
    urlfetch::request_t request;
    urlfetch::task_t<mock::stream_t>::loop_type loop;
    urlfetch::task_t<mock::stream_t>::callback_type callback;
    urlfetch::task_t<mock::stream_t> task(request, callback, loop);
    UNUSED(task);
}

void post_open(boost::asio::io_service& loop,
               std::function<void(const boost::system::error_code&)> task,
               const boost::system::error_code& ec) {
    loop.post(std::bind(task, ec));
}

void post_read(boost::asio::io_service& loop,
               const boost::asio::mutable_buffer& buffer,
               std::function<
                   void(const boost::system::error_code&, std::size_t length)
               > task,
               const boost::system::error_code& ec,
               std::string data) {
    BOOST_ASSERT(data.size() <= 4096);

    std::size_t size = boost::asio::buffer_size(buffer);
    EXPECT_EQ(4096, size);

    unsigned char* p = boost::asio::buffer_cast<unsigned char*>(buffer);
    std::copy(data.begin(), data.end(), p);
    loop.post(std::bind(task, ec, data.size()));
}

namespace testing {

struct event_t {
    std::atomic<int>& counter;
};

struct successfull_get_t {
    std::atomic<int>& counter;

    void operator()(urlfetch::request_t&& request,
                    urlfetch::response_t&& response,
                    const boost::system::error_code& ec) {
        EXPECT_EQ("http://127.0.0.1:80", request.url);
        EXPECT_EQ("{}", response.data);
        EXPECT_EQ(0, ec.value());
        counter++;
    }
};

} // namespace testing

TEST(urlfetch_t, SuccessfullGet) {
    urlfetch::request_t request;
    request.url = "http://127.0.0.1:80";

    std::atomic<int> counter(0);
    testing::successfull_get_t event { counter };
    urlfetch::task_t<mock::stream_t>::loop_type loop;
    auto task = std::make_shared<
        urlfetch::task_t<mock::stream_t>
    >(request, event, loop);

    EXPECT_CALL(task->stream(), async_open(urdl::url("http://127.0.0.1:80"), _))
            .Times(1)
            .WillOnce(
                WithArg<1>(
                    Invoke(
                        std::bind(
                            &post_open,
                            std::ref(loop),
                            std::placeholders::_1,
                            boost::system::error_code()
                        )
                    )
                )
            );
    task->run();

    // First read event - just writing some bytes to the buffer.
    EXPECT_CALL(task->stream(), async_read_some(_, _))
            .Times(1)
            .WillOnce(
                Invoke(
                    std::bind(
                        &post_read,
                        std::ref(loop),
                        std::placeholders::_1,
                        std::placeholders::_2,
                        boost::system::error_code(),
                        "{}"
                    )
                )
            );

    // Poll `on_open` task.
    loop.run_one();

    // Second read event - pushing EOF "error" to notify, that no more bytes
    // will be received.
    EXPECT_CALL(task->stream(), async_read_some(_, _))
            .Times(1)
            .WillOnce(
                Invoke(
                    std::bind(
                        &post_read,
                        std::ref(loop),
                        std::placeholders::_1,
                        std::placeholders::_2,
                        boost::asio::error::make_error_code(
                            boost::asio::error::eof
                        ),
                        ""
                    )
                )
            );

    // Pool `on_read` first task.
    loop.run_one();

    // Poll `on_read` second task.
    loop.run_one();

    EXPECT_EQ(1, counter);
}

// Test-> failed
// Test-> timeout
// Test-> cancel

TEST(urlfetch_t, Manual) {
//    urlfetch::get(url, callback, loop, timeout=default);
//    urlfetch::post(url, body, callback, loop, timeout=default);
//    urlfetch::execute(request, callback, loop);
}
<commit_msg>[GCC 4.4] Oh, atomic.<commit_after>#include <array>

#include <boost/asio.hpp>

#include <urdl/option_set.hpp>
#include <urdl/read_stream.hpp>

#include <blackhole/utils/atomic.hpp>

#include "../global.hpp"

namespace urlfetch {

struct request_t {
    std::string url;
    urdl::option_set options;
    long timeout;
};

struct response_t {
    std::string data;
};

template<class Stream = urdl::read_stream>
class task_t : public std::enable_shared_from_this<task_t<Stream>> {
public:
    typedef Stream stream_type;
    typedef boost::asio::io_service loop_type;
    typedef std::function<
        void(request_t&&, response_t&&, const boost::system::error_code&)
    > callback_type;

private:
    std::array<char, 4096> buffer;

    request_t request;
    response_t response;
    callback_type callback;
    stream_type stream_;

public:
    task_t(request_t request, callback_type callback, loop_type& loop) :
        request(std::move(request)),
        callback(std::move(callback)),
        stream_(loop)
    {}

    stream_type& stream() {
        return stream_;
    }

    void run() {
        //!@todo: do nothing if the stream is active.
        stream_.async_open(
            request.url,
            std::bind(
                &task_t::on_open,
                this->shared_from_this(),
                std::placeholders::_1
            )
        );
    }

private:
    void on_open(const boost::system::error_code& ec) {
        std::cout << "on_open" << std::endl;
        //!@todo:
//        if (ec) {
//            callback(url, "", ec);
//            return;
//        }

        post_read();
    }

    void on_read(const boost::system::error_code& ec, std::size_t length) {
        std::cout << "on_read" << std::endl;
        if (ec) {
            //!@todo: timer.cancel();
            if (ec.value() == boost::asio::error::eof) {
                callback(
                    std::move(request),
                    std::move(response),
                    boost::system::error_code()
                );
            } else {
                //!@todo:
//                callback(
//                    std::move(request),
//                    std::move(response),
//                    ec
//                );
            }

            return;
        }

        response.data.append(buffer.begin(), buffer.begin() + length);
        post_read();
    }

    void post_read() {
        stream_.async_read_some(
            boost::asio::buffer(buffer),
            std::bind(
                &task_t::on_read,
                this->shared_from_this(),
                std::placeholders::_1,
                std::placeholders::_2
            )
        );
    }
};

} // namespace urlfetch

namespace mock {

class stream_t {
public:
    stream_t(boost::asio::io_service&) {}

    MOCK_METHOD2(
        async_open,
        void(
            const urdl::url&,
            std::function<void(const boost::system::error_code&)>
        )
    );

    MOCK_METHOD2(
        async_read_some,
        void(
            boost::asio::mutable_buffer,
            std::function<void(const boost::system::error_code&, std::size_t)>
        )
    );
};

} // namespace mock

TEST(request_t, Class) {
    urlfetch::request_t request;
    UNUSED(request);
}

TEST(urlfetch_t, Class) {
    urlfetch::request_t request;
    urlfetch::task_t<mock::stream_t>::loop_type loop;
    urlfetch::task_t<mock::stream_t>::callback_type callback;
    urlfetch::task_t<mock::stream_t> task(request, callback, loop);
    UNUSED(task);
}

void post_open(boost::asio::io_service& loop,
               std::function<void(const boost::system::error_code&)> task,
               const boost::system::error_code& ec) {
    loop.post(std::bind(task, ec));
}

void post_read(boost::asio::io_service& loop,
               const boost::asio::mutable_buffer& buffer,
               std::function<
                   void(const boost::system::error_code&, std::size_t length)
               > task,
               const boost::system::error_code& ec,
               std::string data) {
    BOOST_ASSERT(data.size() <= 4096);

    std::size_t size = boost::asio::buffer_size(buffer);
    EXPECT_EQ(4096, size);

    unsigned char* p = boost::asio::buffer_cast<unsigned char*>(buffer);
    std::copy(data.begin(), data.end(), p);
    loop.post(std::bind(task, ec, data.size()));
}

namespace testing {

struct event_t {
    std::atomic<int>& counter;
};

struct successfull_get_t {
    std::atomic<int>& counter;

    void operator()(urlfetch::request_t&& request,
                    urlfetch::response_t&& response,
                    const boost::system::error_code& ec) {
        EXPECT_EQ("http://127.0.0.1:80", request.url);
        EXPECT_EQ("{}", response.data);
        EXPECT_EQ(0, ec.value());
        counter++;
    }
};

} // namespace testing

TEST(urlfetch_t, SuccessfullGet) {
    urlfetch::request_t request;
    request.url = "http://127.0.0.1:80";

    std::atomic<int> counter(0);
    testing::successfull_get_t event { counter };
    urlfetch::task_t<mock::stream_t>::loop_type loop;
    auto task = std::make_shared<
        urlfetch::task_t<mock::stream_t>
    >(request, event, loop);

    EXPECT_CALL(task->stream(), async_open(urdl::url("http://127.0.0.1:80"), _))
            .Times(1)
            .WillOnce(
                WithArg<1>(
                    Invoke(
                        std::bind(
                            &post_open,
                            std::ref(loop),
                            std::placeholders::_1,
                            boost::system::error_code()
                        )
                    )
                )
            );
    task->run();

    // First read event - just writing some bytes to the buffer.
    EXPECT_CALL(task->stream(), async_read_some(_, _))
            .Times(1)
            .WillOnce(
                Invoke(
                    std::bind(
                        &post_read,
                        std::ref(loop),
                        std::placeholders::_1,
                        std::placeholders::_2,
                        boost::system::error_code(),
                        "{}"
                    )
                )
            );

    // Poll `on_open` task.
    loop.run_one();

    // Second read event - pushing EOF "error" to notify, that no more bytes
    // will be received.
    EXPECT_CALL(task->stream(), async_read_some(_, _))
            .Times(1)
            .WillOnce(
                Invoke(
                    std::bind(
                        &post_read,
                        std::ref(loop),
                        std::placeholders::_1,
                        std::placeholders::_2,
                        boost::asio::error::make_error_code(
                            boost::asio::error::eof
                        ),
                        ""
                    )
                )
            );

    // Pool `on_read` first task.
    loop.run_one();

    // Poll `on_read` second task.
    loop.run_one();

    EXPECT_EQ(1, counter);
}

// Test-> failed
// Test-> timeout
// Test-> cancel

TEST(urlfetch_t, Manual) {
//    urlfetch::get(url, callback, loop, timeout=default);
//    urlfetch::post(url, body, callback, loop, timeout=default);
//    urlfetch::execute(request, callback, loop);
}
<|endoftext|>


<commit_before>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnordmetric/environment.h>
#include <fnordmetric/metricdb/binaryformat.h>
#include <fnordmetric/metricdb/metric.h>
#include <fnordmetric/metricdb/samplewriter.h>
#include <fnordmetric/metricdb/tableref.h>
#include <fnordmetric/sstable/livesstable.h>
#include <fnordmetric/util/runtimeexception.h>
#include <fnordmetric/util/freeondestroy.h>
#include <fnordmetric/util/wallclock.h>
#include <string.h>

using namespace fnord;
namespace fnordmetric {
namespace metricdb {

Metric::Metric(
    const std::string& key,
    io::FileRepository* file_repo) :
    key_(key),
    file_repo_(file_repo),
    head_(nullptr),
    max_generation_(0) {
  if (env()->verbose()) {
    env()->logger()->printf(
        "DEBUG",
        "Create new metric: '%s'",
        key.c_str());
  }
}

Metric::Metric(
    const std::string& key,
    io::FileRepository* file_repo,
    std::vector<std::unique_ptr<TableRef>>&& tables) :
    key_(key),
    file_repo_(file_repo) {
  TableRef* head_table = nullptr;
  std::vector<uint64_t> generations;

  for (auto& table : tables) {
    if (head_table == nullptr ||
        table->generation() > head_table->generation()) {
      head_table = table.get();
    }
  }

  generations = head_table->parents();
  generations.emplace_back(head_table->generation());

  if (env()->verbose()) {
    env()->logger()->printf(
        "DEBUG",
        "Reopening metric: '%s' with %i table(s), generation: %" PRIu64,
        key.c_str(),
        (int) generations.size(),
        head_table->generation());
  }

  std::shared_ptr<MetricSnapshot> snapshot(new MetricSnapshot());
  for (const auto gen : generations) {
    for (auto& table : tables) {
      if (table.get() == nullptr) {
        continue;
      }

      if (table->generation() == gen) {
        table->importTokenIndex(&token_index_);
        snapshot->appendTable(std::move(table));
        table.reset(nullptr);
      }
    }
  }

  head_ = snapshot;
  max_generation_ = head_table->generation();

  for (auto& table : tables) {
    if (table.get() != nullptr) {
      env()->logger()->printf("INFO", "Orphaned table...");
    }
  }
}

const std::string& Metric::key() const {
  return key_;
}

std::shared_ptr<MetricSnapshot> Metric::getSnapshot() const {
  std::lock_guard<std::mutex> lock_holder(head_mutex_);
  return head_;
}

// Must hold append_mutex_ to call this!
std::shared_ptr<MetricSnapshot> Metric::getOrCreateSnapshot() {
  {
    std::lock_guard<std::mutex> lock_holder(head_mutex_);

    if (head_.get() != nullptr &&
        head_->isWritable() &&
        head_->tables().back()->isWritable() &&
        head_->tables().back()->bodySize() < (2 << 16)) {
      return head_;
    }
  }

  std::lock_guard<std::mutex> lock_holder(head_mutex_);
  auto new_snapshot = createSnapshot(true);
  head_ = new_snapshot;
  return head_;
}

void Metric::addSample(const Sample<double>& sample) {
  SampleWriter writer(&token_index_);
  writer.writeValue(sample.value);
  for (const auto& label : sample.labels) {
    writer.writeLabel(label.first, label.second);
  }

  std::lock_guard<std::mutex> lock_holder(append_mutex_);
  auto snapshot = getOrCreateSnapshot();
  auto& table = snapshot->tables().back();

  uint64_t now = fnord::util::WallClock::unixMicros();
  table->addSample(&writer, now);
}

// FIXPAUL misnomer...it creates a new snapshot + appends a new, clean table
std::shared_ptr<MetricSnapshot> Metric::createSnapshot(bool writable) {
  std::shared_ptr<MetricSnapshot> snapshot;
  std::vector<uint64_t> parents;

  if (head_.get() == nullptr) {
    snapshot.reset(new MetricSnapshot());
  } else {
    snapshot = head_->clone();
    for (const auto& tbl : snapshot->tables()) {
      parents.emplace_back(tbl->generation());
    }
  }

  snapshot->setWritable(writable);

  if (writable) {
    // open new file
    auto fileref = file_repo_->createFile();
    auto file = io::File::openFile(
        fileref.absolute_path,
        io::File::O_READ | io::File::O_WRITE | io::File::O_CREATE);

    snapshot->appendTable(TableRef::createTable(
        fileref.absolute_path,
        key_,
        std::move(file),
        ++max_generation_,
        parents));
  }

  return snapshot;
}

void Metric::scanSamples(
    const fnord::util::DateTime& time_begin,
    const fnord::util::DateTime& time_end,
    std::function<bool (MetricCursor*)> callback) {
  auto snapshot = getSnapshot();
  if (snapshot.get() == nullptr) {
    return;
  }

  MetricCursor cursor(snapshot, &token_index_);
  while (cursor.valid()) {
    callback(&cursor);

    if (!cursor.next()) {
      break;
    }
  }
}

void Metric::compact() {
  if (!compaction_mutex_.try_lock()) {
    return;
  }

  std::lock_guard<std::mutex>(compaction_mutex_, std::adopt_lock);

  if (env()->verbose()) {
    env()->logger()->printf(
        "DEBUG",
        "Running compaction for metric: '%s'",
        key_.c_str());
  }

  // create a new snapshot and make all tables immutable
  std::shared_ptr<MetricSnapshot> snapshot;
  {
    std::lock_guard<std::mutex> append_lock_holder(append_mutex_);
    std::lock_guard<std::mutex> head_lock_holder(head_mutex_);
    snapshot = createSnapshot(false);
  }

  auto old_tables = snapshot->tables();
  std::vector<std::shared_ptr<TableRef>> new_tables;

  // finalize unfinished sstables
  for (auto& table : old_tables) {
    if (table->isWritable()) {
      if (table->bodySize() == 0) {
        if (env()->verbose()) {
          env()->logger()->printf(
              "DEBUG",
              "SSTable '%s' (%s) is empty, deleting...",
              table->filename().c_str(),
              table->metricKey().c_str());
        }

        continue;
      } else {
        if (env()->verbose()) {
          env()->logger()->printf(
              "DEBUG",
              "Finalizing sstable '%s' (%s)",
              table->filename().c_str(),
              table->metricKey().c_str());
        }

        table->finalize(&token_index_);
        new_tables.emplace_back(new ReadonlyTableRef(*table));
      }
    } else {
      new_tables.emplace_back(table);
    }
  }
}

}
}
<commit_msg>commit compaction<commit_after>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnordmetric/environment.h>
#include <fnordmetric/metricdb/binaryformat.h>
#include <fnordmetric/metricdb/metric.h>
#include <fnordmetric/metricdb/samplewriter.h>
#include <fnordmetric/metricdb/tableref.h>
#include <fnordmetric/sstable/livesstable.h>
#include <fnordmetric/util/runtimeexception.h>
#include <fnordmetric/util/freeondestroy.h>
#include <fnordmetric/util/wallclock.h>
#include <string.h>

using namespace fnord;
namespace fnordmetric {
namespace metricdb {

Metric::Metric(
    const std::string& key,
    io::FileRepository* file_repo) :
    key_(key),
    file_repo_(file_repo),
    head_(nullptr),
    max_generation_(0) {
  if (env()->verbose()) {
    env()->logger()->printf(
        "DEBUG",
        "Create new metric: '%s'",
        key.c_str());
  }
}

Metric::Metric(
    const std::string& key,
    io::FileRepository* file_repo,
    std::vector<std::unique_ptr<TableRef>>&& tables) :
    key_(key),
    file_repo_(file_repo) {
  TableRef* head_table = nullptr;
  std::vector<uint64_t> generations;

  for (auto& table : tables) {
    if (head_table == nullptr ||
        table->generation() > head_table->generation()) {
      head_table = table.get();
    }
  }

  generations = head_table->parents();
  generations.emplace_back(head_table->generation());

  if (env()->verbose()) {
    env()->logger()->printf(
        "DEBUG",
        "Reopening metric: '%s' with %i table(s), generation: %" PRIu64,
        key.c_str(),
        (int) generations.size(),
        head_table->generation());
  }

  std::shared_ptr<MetricSnapshot> snapshot(new MetricSnapshot());
  for (const auto gen : generations) {
    for (auto& table : tables) {
      if (table.get() == nullptr) {
        continue;
      }

      if (table->generation() == gen) {
        table->importTokenIndex(&token_index_);
        snapshot->appendTable(std::move(table));
        table.reset(nullptr);
      }
    }
  }

  head_ = snapshot;
  max_generation_ = head_table->generation();

  for (auto& table : tables) {
    if (table.get() != nullptr) {
      env()->logger()->printf("INFO", "Orphaned table...");
    }
  }
}

const std::string& Metric::key() const {
  return key_;
}

std::shared_ptr<MetricSnapshot> Metric::getSnapshot() const {
  std::lock_guard<std::mutex> lock_holder(head_mutex_);
  return head_;
}

// Must hold append_mutex_ to call this!
std::shared_ptr<MetricSnapshot> Metric::getOrCreateSnapshot() {
  {
    std::lock_guard<std::mutex> lock_holder(head_mutex_);

    if (head_.get() != nullptr &&
        head_->isWritable() &&
        head_->tables().back()->isWritable() &&
        head_->tables().back()->bodySize() < (2 << 16)) {
      return head_;
    }
  }

  std::lock_guard<std::mutex> lock_holder(head_mutex_);
  auto new_snapshot = createSnapshot(true);
  head_ = new_snapshot;
  return head_;
}

void Metric::addSample(const Sample<double>& sample) {
  SampleWriter writer(&token_index_);
  writer.writeValue(sample.value);
  for (const auto& label : sample.labels) {
    writer.writeLabel(label.first, label.second);
  }

  std::lock_guard<std::mutex> lock_holder(append_mutex_);
  auto snapshot = getOrCreateSnapshot();
  auto& table = snapshot->tables().back();

  uint64_t now = fnord::util::WallClock::unixMicros();
  table->addSample(&writer, now);
}

// FIXPAUL misnomer...it creates a new snapshot + appends a new, clean table
std::shared_ptr<MetricSnapshot> Metric::createSnapshot(bool writable) {
  std::shared_ptr<MetricSnapshot> snapshot;
  std::vector<uint64_t> parents;

  if (head_.get() == nullptr) {
    snapshot.reset(new MetricSnapshot());
  } else {
    snapshot = head_->clone();
    for (const auto& tbl : snapshot->tables()) {
      parents.emplace_back(tbl->generation());
    }
  }

  snapshot->setWritable(writable);

  if (writable) {
    // open new file
    auto fileref = file_repo_->createFile();
    auto file = io::File::openFile(
        fileref.absolute_path,
        io::File::O_READ | io::File::O_WRITE | io::File::O_CREATE);

    snapshot->appendTable(TableRef::createTable(
        fileref.absolute_path,
        key_,
        std::move(file),
        ++max_generation_,
        parents));
  }

  return snapshot;
}

void Metric::scanSamples(
    const fnord::util::DateTime& time_begin,
    const fnord::util::DateTime& time_end,
    std::function<bool (MetricCursor*)> callback) {
  auto snapshot = getSnapshot();
  if (snapshot.get() == nullptr) {
    return;
  }

  MetricCursor cursor(snapshot, &token_index_);
  while (cursor.valid()) {
    callback(&cursor);

    if (!cursor.next()) {
      break;
    }
  }
}

void Metric::compact() {
  if (!compaction_mutex_.try_lock()) {
    return;
  }

  std::lock_guard<std::mutex>(compaction_mutex_, std::adopt_lock);

  if (env()->verbose()) {
    env()->logger()->printf(
        "DEBUG",
        "Running compaction for metric: '%s'",
        key_.c_str());
  }

  // create a new snapshot and make all tables immutable
  std::shared_ptr<MetricSnapshot> snapshot;
  {
    std::lock_guard<std::mutex> append_lock_holder(append_mutex_);
    std::lock_guard<std::mutex> head_lock_holder(head_mutex_);
    snapshot = createSnapshot(false);
  }

  auto old_tables = snapshot->tables();
  std::vector<std::shared_ptr<TableRef>> new_tables;

  // finalize unfinished sstables
  for (auto& table : old_tables) {
    if (table->isWritable()) {
      if (table->bodySize() == 0) {
        if (env()->verbose()) {
          env()->logger()->printf(
              "DEBUG",
              "SSTable '%s' (%s) is empty, deleting...",
              table->filename().c_str(),
              table->metricKey().c_str());
        }

        continue;
      } else {
        if (env()->verbose()) {
          env()->logger()->printf(
              "DEBUG",
              "Finalizing sstable '%s' (%s)",
              table->filename().c_str(),
              table->metricKey().c_str());
        }

        table->finalize(&token_index_);
        new_tables.emplace_back(new ReadonlyTableRef(*table));
      }
    } else {
      new_tables.emplace_back(table);
    }
  }

  // create a new snapshot and commit modifications
  {
    std::lock_guard<std::mutex> append_lock_holder(append_mutex_);
    std::lock_guard<std::mutex> head_lock_holder(head_mutex_);
    auto new_snapshot = new MetricSnapshot();
    new_snapshot->setWritable(head_->isWritable());

    for (const auto& table : new_tables) {
      new_snapshot->appendTable(table);
    }

    for (int i = old_tables.size(); i < head_->tables().size(); ++i) {
      new_snapshot->appendTable(head_->tables()[i]);
    }

    head_.reset(new_snapshot);
  }
}

}
}
<|endoftext|>


<commit_before>#include "servermanage.h"

#include "utility.h"
#include "NetworkEvent.hpp"
#include "univ/Base64.h"
#include <jsoncpp/json/json.h>
using namespace Sloong::Events;

CResult Sloong::CServerManage::Initialize( int template_id )
{
	m_listFuncHandler["AddTemplate"]=std::bind(&CServerManage::AddTemplateHandler, this, std::placeholders::_1,std::placeholders::_2);
	m_listFuncHandler["DeleteTemplate"]=std::bind(&CServerManage::DeleteTemplateHandler, this, std::placeholders::_1,std::placeholders::_2);
	m_listFuncHandler["SetTemplate"]=std::bind(&CServerManage::SetTemplateHandler, this, std::placeholders::_1,std::placeholders::_2);
	m_listFuncHandler["QueryTemplate"]=std::bind(&CServerManage::QueryTemplateHandler, this, std::placeholders::_1,std::placeholders::_2);

	auto res = m_pAllConfig->Initialize("/data/configuation.db");
	if (res.IsFialed()) return res;

	// Initialize template list
	auto list = m_pAllConfig->GetTemplateList();
	for (auto item : list)
	{
		TemplateItem addItem(item);
		m_oTemplateList[addItem.ID] = addItem;
	}

	res = m_pAllConfig->GetTemplate(template_id);
	if (res.IsFialed())
		return CResult::Succeed();
	else
		return res;
}

int Sloong::CServerManage::SearchNeedCreateTemplate()
{
	for (auto item : m_oTemplateList)
	{
		if (item.second.Replicas == 0)
			continue;

		if (item.second.Created.size() == item.second.Replicas)
			continue;

		if (item.second.Created.size() == 0)
			return item.first;
	}

	for (auto item : m_oTemplateList)
	{
		if (item.second.Replicas == 0)
			continue;

		if (item.second.Created.size() == item.second.Replicas)
			continue;

		if (item.second.Created.size() < item.second.Replicas)
			return item.first;
	}
	return -1;
}

bool Sloong::CServerManage::RegisterServerHandler(Functions func, string sender, CDataTransPackage* pack)
{
	auto index = SearchNeedCreateTemplate();
	if (index == -1)
	{
		pack->ResponsePackage(ResultType::Retry,"Retry");
		return true;
	}

	if( sender.length() == 0 )
	{
		sender = CUtility::GenUUID();
	}

	auto sender_info = m_oServerList.try_get(sender);
	if( sender_info == nullptr)
	{
		ServerItem item;
		item.Address = pack->GetSocketIP();
		item.Port = pack->GetSocketPort();
		item.UUID = sender;
		m_oServerList[sender] = item;
		m_pLog->Verbos(CUniversal::Format("Module[%s:%d] regist to system. Allocating uuid [%s].", item.Address, item.Port, item.UUID));
		sender_info = m_oServerList.try_get(sender);
	}

	if( sender_info == nullptr)
	{
		pack->ResponsePackage(ResultType::Error,"Add server info to ServerList fialed.");
		return true;
	}

	auto tpl = m_oTemplateList[index];
	sender_info->TemplateName = tpl.Name;
	sender_info->TemplateID = tpl.ID;
	m_pLog->Verbos(CUniversal::Format("Allocating module[%s] Type to [%s]", sender_info->UUID, sender_info->TemplateName));
	pack->ResponsePackage(sender_info->UUID,sender_info->TemplateID==-1?"": CBase64::Decode(m_oTemplateList[sender_info->TemplateID].Configuation));

	return true;
}

bool Sloong::CServerManage::ProcessHandler(Functions func, string sender, CDataTransPackage* pack)
{
	Json::Reader reader;
	Json::Value root;
	auto jreq = pack->GetRecvMessage();
	if (!reader.parse(jreq, root))
	{
		pack->ResponsePackage(ResultType::Error, CUniversal::Format("Parser json[% s] error.", jreq));
		return true;
	}
	if (root["Function"].isNull())
	{
		pack->ResponsePackage(ResultType::Error, "Request no set [Function] node.");
		return true;
	}
	auto function = root["Function"].asString();
	if(!m_listFuncHandler.exist(function))
	{
		pack->ResponsePackage(ResultType::Error, CUniversal::Format("Function [%s] no handler.",function));
		return true;
	}

	return m_listFuncHandler[function](sender,pack);
}

bool Sloong::CServerManage::AddTemplateHandler(const Json::Value& jRequest,CDataTransPackage* pack)
{
	if(jRequest["Name"].isNull()||jRequest["Replicas"].isNull()||jRequest["Configuation"].isNull())
	{
		pack->ResponsePackage(ResultType::Error, "The required parameter is null.");
		return true;
	}

	TemplateItem item;
	item.Name = jRequest["Name"].asString();
	item.Note = jRequest["Note"].asString();
	item.Replicas = jRequest["Replicas"].asInt();
	item.Configuation = jRequest["Configuation"].asString();
	int id = 0;
	auto res = m_pAllConfig->AddTemplate(item.ToTemplateInfo(),&id);
	if( res.IsFialed() )
	{
		pack->ResponsePackage(ResultType::Error, res.Message());
		return true;
	}
	item.ID = id;
	m_oTemplateList[id] = item;
	pack->ResponsePackage(ResultType::Succeed,CUniversal::ntos(id));
	return true;
}

bool Sloong::CServerManage::DeleteTemplateHandler(const Json::Value& jRequest,CDataTransPackage* pack)
{
	if(jRequest["ID"].isNull())
	{
		pack->ResponsePackage(ResultType::Error, "The required parameter is null.");
		return true;
	}

	int id = jRequest["ID"].asInt();
	if( !m_oTemplateList.exist(id))
	{
		pack->ResponsePackage(ResultType::Error, CUniversal::Format("The template id [%d] is no exist.",id));
		return true;
	}

	auto res = m_pAllConfig->DeleteTemplate(id);
	if( res.IsFialed() )
	{
		pack->ResponsePackage(ResultType::Error, res.Message());
		return true;
	}
	m_oTemplateList.erase(id);
	pack->ResponsePackage(CResult::Succeed());
	return true;
}

bool Sloong::CServerManage::SetTemplateHandler(const Json::Value& jRequest,CDataTransPackage* pack)
{
	if (jRequest["ID"].isNull() || !m_oTemplateList.exist(jRequest["ID"].asInt()))
	{
		pack->ResponsePackage(ResultType::Error, "Check the templeate ID error, please check.");
		return true;
	}

	auto info = m_oTemplateList[jRequest["ID"].asInt()];
	if (!jRequest["Name"].isNull())
		info.Name = jRequest["Name"].asString();

	if (!jRequest["Note"].isNull())
		info.Note = jRequest["Note"].asString();

	if (!jRequest["Replcas"].isNull())
		info.Replicas = jRequest["Replcas"].asInt();

	if (!jRequest["Configuation"].isNull())
		info.Configuation = jRequest["Configuation"].asString();

	auto res = m_pAllConfig->SetTemplate(info.ID, info.ToTemplateInfo());
	if (res.IsFialed())
	{
		pack->ResponsePackage(ResultType::Error, res.Message());
		return true;
	}

	pack->ResponsePackage(ResultType::Succeed);
	m_oTemplateList[info.ID] =  info;
	return true;
}

bool Sloong::CServerManage::QueryTemplateHandler(const Json::Value& jRequest,CDataTransPackage* pack)
{
	Json::Value list;
	if( !jRequest["ID"].isNull() )
	{
		int id = jRequest["ID"].asInt();
		if(!m_oTemplateList.exist(id))
		{
			pack->ResponsePackage(ResultType::Error, CUniversal::Format("The template id [%d] is no exist.",id));
			return true;
		}

		list.append(m_oTemplateList[id].ToJson());
	}
	else
	{
		for (auto& i : m_oTemplateList)
		{
			list.append(i.second.ToJson());
		}
	}

	Json::Value root;
	root["TemplateList"] = list;
	pack->ResponsePackage(root.toStyledString(),"");

	return true;
}

bool Sloong::CServerManage::GetServerListHandler(const Json::Value& jRequest,CDataTransPackage* pack)
{
	Json::Value list;
	for (auto& i : m_oServerList)
	{
		Json::Value item;
		item["UUID"] = i.first;
		item["TemplateID"] = i.second.TemplateID;
		list.append(item);
	}
	Json::Value root;
	root["ServerList"] = list;
	pack->ResponsePackage(root.toStyledString(), "");
	return true;
}
<commit_msg>FIx the json request used value problem.<commit_after>#include "servermanage.h"

#include "utility.h"
#include "NetworkEvent.hpp"
#include "univ/Base64.h"
#include <jsoncpp/json/json.h>
using namespace Sloong::Events;

CResult Sloong::CServerManage::Initialize( int template_id )
{
	m_listFuncHandler["AddTemplate"]=std::bind(&CServerManage::AddTemplateHandler, this, std::placeholders::_1,std::placeholders::_2);
	m_listFuncHandler["DeleteTemplate"]=std::bind(&CServerManage::DeleteTemplateHandler, this, std::placeholders::_1,std::placeholders::_2);
	m_listFuncHandler["SetTemplate"]=std::bind(&CServerManage::SetTemplateHandler, this, std::placeholders::_1,std::placeholders::_2);
	m_listFuncHandler["QueryTemplate"]=std::bind(&CServerManage::QueryTemplateHandler, this, std::placeholders::_1,std::placeholders::_2);

	auto res = m_pAllConfig->Initialize("/data/configuation.db");
	if (res.IsFialed()) return res;

	// Initialize template list
	auto list = m_pAllConfig->GetTemplateList();
	for (auto item : list)
	{
		TemplateItem addItem(item);
		m_oTemplateList[addItem.ID] = addItem;
	}

	res = m_pAllConfig->GetTemplate(template_id);
	if (res.IsFialed())
		return CResult::Succeed();
	else
		return res;
}

int Sloong::CServerManage::SearchNeedCreateTemplate()
{
	for (auto item : m_oTemplateList)
	{
		if (item.second.Replicas == 0)
			continue;

		if (item.second.Created.size() == item.second.Replicas)
			continue;

		if (item.second.Created.size() == 0)
			return item.first;
	}

	for (auto item : m_oTemplateList)
	{
		if (item.second.Replicas == 0)
			continue;

		if (item.second.Created.size() == item.second.Replicas)
			continue;

		if (item.second.Created.size() < item.second.Replicas)
			return item.first;
	}
	return -1;
}

bool Sloong::CServerManage::RegisterServerHandler(Functions func, string sender, CDataTransPackage* pack)
{
	auto index = SearchNeedCreateTemplate();
	if (index == -1)
	{
		pack->ResponsePackage(ResultType::Retry,"Retry");
		return true;
	}

	if( sender.length() == 0 )
	{
		sender = CUtility::GenUUID();
	}

	auto sender_info = m_oServerList.try_get(sender);
	if( sender_info == nullptr)
	{
		ServerItem item;
		item.Address = pack->GetSocketIP();
		item.Port = pack->GetSocketPort();
		item.UUID = sender;
		m_oServerList[sender] = item;
		m_pLog->Verbos(CUniversal::Format("Module[%s:%d] regist to system. Allocating uuid [%s].", item.Address, item.Port, item.UUID));
		sender_info = m_oServerList.try_get(sender);
	}

	if( sender_info == nullptr)
	{
		pack->ResponsePackage(ResultType::Error,"Add server info to ServerList fialed.");
		return true;
	}

	auto tpl = m_oTemplateList[index];
	sender_info->TemplateName = tpl.Name;
	sender_info->TemplateID = tpl.ID;
	m_pLog->Verbos(CUniversal::Format("Allocating module[%s] Type to [%s]", sender_info->UUID, sender_info->TemplateName));
	pack->ResponsePackage(sender_info->UUID,sender_info->TemplateID==-1?"": CBase64::Decode(m_oTemplateList[sender_info->TemplateID].Configuation));

	return true;
}

bool Sloong::CServerManage::ProcessHandler(Functions func, string sender, CDataTransPackage* pack)
{
	Json::Reader reader;
	Json::Value root;
	auto jreq = pack->GetRecvMessage();
	if (!reader.parse(jreq, root))
	{
		pack->ResponsePackage(ResultType::Error, CUniversal::Format("Parser json[% s] error.", jreq));
		return true;
	}
	if (root["Function"].isNull())
	{
		pack->ResponsePackage(ResultType::Error, "Request no set [Function] node.");
		return true;
	}
	auto function = root["Function"].asString();
	if(!m_listFuncHandler.exist(function))
	{
		pack->ResponsePackage(ResultType::Error, CUniversal::Format("Function [%s] no handler.",function));
		return true;
	}

	return m_listFuncHandler[function](sender,pack);
}

bool Sloong::CServerManage::AddTemplateHandler(const Json::Value& jRequest,CDataTransPackage* pack)
{
	auto jReq = jRequest["Template"];
	if(jReq["Name"].isNull()||jReq["Replicas"].isNull()||jReq["Configuation"].isNull())
	{
		pack->ResponsePackage(ResultType::Error, "The required parameter is null.");
		return true;
	}

	TemplateItem item;
	item.Name = jReq["Name"].asString();
	item.Note = jReq["Note"].asString();
	item.Replicas = jReq["Replicas"].asInt();
	item.Configuation = jReq["Configuation"].asString();
	int id = 0;
	auto res = m_pAllConfig->AddTemplate(item.ToTemplateInfo(),&id);
	if( res.IsFialed() )
	{
		pack->ResponsePackage(ResultType::Error, res.Message());
		return true;
	}
	item.ID = id;
	m_oTemplateList[id] = item;
	pack->ResponsePackage(ResultType::Succeed,CUniversal::ntos(id));
	return true;
}

bool Sloong::CServerManage::DeleteTemplateHandler(const Json::Value& jRequest,CDataTransPackage* pack)
{
	if(jRequest["ID"].isNull())
	{
		pack->ResponsePackage(ResultType::Error, "The required parameter is null.");
		return true;
	}

	int id = jRequest["ID"].asInt();
	if( !m_oTemplateList.exist(id))
	{
		pack->ResponsePackage(ResultType::Error, CUniversal::Format("The template id [%d] is no exist.",id));
		return true;
	}

	auto res = m_pAllConfig->DeleteTemplate(id);
	if( res.IsFialed() )
	{
		pack->ResponsePackage(ResultType::Error, res.Message());
		return true;
	}
	m_oTemplateList.erase(id);
	pack->ResponsePackage(CResult::Succeed());
	return true;
}

bool Sloong::CServerManage::SetTemplateHandler(const Json::Value& jRequest,CDataTransPackage* pack)
{
	auto jReq = jRequest["Template"];
	if (jReq["ID"].isNull() || !m_oTemplateList.exist(jReq["ID"].asInt()))
	{
		pack->ResponsePackage(ResultType::Error, "Check the templeate ID error, please check.");
		return true;
	}

	auto info = m_oTemplateList[jReq["ID"].asInt()];
	if (!jReq["Name"].isNull())
		info.Name = jReq["Name"].asString();

	if (!jReq["Note"].isNull())
		info.Note = jReq["Note"].asString();

	if (!jReq["Replcas"].isNull())
		info.Replicas = jReq["Replcas"].asInt();

	if (!jReq["Configuation"].isNull())
		info.Configuation = jReq["Configuation"].asString();

	auto res = m_pAllConfig->SetTemplate(info.ID, info.ToTemplateInfo());
	if (res.IsFialed())
	{
		pack->ResponsePackage(ResultType::Error, res.Message());
		return true;
	}

	pack->ResponsePackage(ResultType::Succeed);
	m_oTemplateList[info.ID] =  info;
	return true;
}

bool Sloong::CServerManage::QueryTemplateHandler(const Json::Value& jRequest,CDataTransPackage* pack)
{
	Json::Value list;
	if( !jRequest["ID"].isNull() )
	{
		int id = jRequest["ID"].asInt();
		if(!m_oTemplateList.exist(id))
		{
			pack->ResponsePackage(ResultType::Error, CUniversal::Format("The template id [%d] is no exist.",id));
			return true;
		}

		list.append(m_oTemplateList[id].ToJson());
	}
	else
	{
		for (auto& i : m_oTemplateList)
		{
			list.append(i.second.ToJson());
		}
	}

	Json::Value root;
	root["TemplateList"] = list;
	pack->ResponsePackage(root.toStyledString(),"");

	return true;
}

bool Sloong::CServerManage::GetServerListHandler(const Json::Value& jRequest,CDataTransPackage* pack)
{
	Json::Value list;
	for (auto& i : m_oServerList)
	{
		Json::Value item;
		item["UUID"] = i.first;
		item["TemplateID"] = i.second.TemplateID;
		list.append(item);
	}
	Json::Value root;
	root["ServerList"] = list;
	pack->ResponsePackage(root.toStyledString(), "");
	return true;
}
<|endoftext|>


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

#include <string>
#include <vector>

#include <mesos/module.hpp>

#include <stout/json.hpp>
#include <stout/numify.hpp>
#include <stout/os.hpp>
#include <stout/strings.hpp>
#include <stout/stringify.hpp>
#include <stout/version.hpp>

#include "manager.hpp"

using std::list;
using std::string;
using std::vector;
using process::Owned;

namespace mesos {
namespace internal {

pthread_mutex_t ModuleManager::mutex = PTHREAD_MUTEX_INITIALIZER;
hashmap<const string, string> ModuleManager::kindToVersion;
hashmap<const string, ModuleBase*> ModuleManager::moduleBases;
list<Owned<DynamicLibrary> > ModuleManager::dynamicLibraries;

void ModuleManager::initialize()
{
  // ATTENTION: Every time a Mesos developer breaks compatibility with
  // a module kind type, this table needs to be updated.
  // Specifically, the version value in the entry corresponding to the
  // kind needs to be set to the Mesos version that affects the
  // current change.  Typically that should be the version currently
  // under development.

  kindToVersion["TestModule"] = MESOS_VERSION;

  // What happens then when Mesos is built with a certain version,
  // 'kindToVersion' states a certain other minimum version, and a
  // module library is built against "module.hpp" belonging to yet
  // another Mesos version?
  //
  // Mesos can admit modules built against earlier versions of itself
  // by stating so explicitly in 'kindToVersion'.  If a modules is
  // built with a Mesos version greater than or equal to the one
  // stated in 'kindToVersion', it passes this verification step.
  // Otherwise it is rejected when attempting to load it.
  //
  // Here are some examples:
  //
  // Mesos   kindToVersion    library    modules loadable?
  // 0.18.0      0.18.0       0.18.0          YES
  // 0.29.0      0.18.0       0.18.0          YES
  // 0.29.0      0.18.0       0.21.0          YES
  // 0.18.0      0.18.0       0.29.0          NO
  // 0.29.0      0.21.0       0.18.0          NO
  // 0.29.0      0.29.0       0.18.0          NO

  // ATTENTION: This mechanism only protects the interfaces of
  // modules, not how they maintain functional compatibility with
  // Mesos and among each other.  This is covered by their own
  // "isCompatible" call.
}

// For testing only.  Unload all dlopen()'d module libraries and
// clear the list of module manifests.
void ModuleManager::unloadAll()
{
  kindToVersion.clear();
  moduleBases.clear();
  dynamicLibraries.clear();
}

// TODO(karya): Show library author info for failed library/module.
Try<Nothing> ModuleManager::verifyModule(
    const string& moduleName,
    const ModuleBase* moduleBase)
{
  CHECK_NOTNULL(moduleBase);
  if (moduleBase->mesosVersion == NULL ||
      moduleBase->moduleApiVersion == NULL ||
      moduleBase->authorName == NULL ||
      moduleBase->authorEmail == NULL ||
      moduleBase->description == NULL ||
      moduleBase->kind == NULL) {
    return Error("Error loading module '" + moduleName + "'; missing fields");
  }

  // Verify module API version.
  if (stringify(moduleBase->moduleApiVersion) != MESOS_MODULE_API_VERSION) {
    return Error(
        "Module API version mismatch. Mesos has: " MESOS_MODULE_API_VERSION ", "
        "library requires: " + stringify(moduleBase->moduleApiVersion));
  }

  if (!kindToVersion.contains(moduleBase->kind)) {
    return Error("Unknown module kind: " + stringify(moduleBase->kind));
  }

  Try<Version> mesosVersion = Version::parse(MESOS_VERSION);
  CHECK(!mesosVersion.isError());

  Try<Version> minimumVersion = Version::parse(kindToVersion[moduleBase->kind]);
  CHECK(!minimumVersion.isError());

  Try<Version> moduleMesosVersion = Version::parse(moduleBase->mesosVersion);
  if (moduleMesosVersion.isError()) {
    return Error(moduleMesosVersion.error());
  }

  if (moduleMesosVersion.get() < minimumVersion.get()) {
    return Error(
        "Minimum supported mesos version for '" + stringify(moduleBase->kind) +
        "' is " + stringify(minimumVersion.get()) + ", but module is compiled "
        "with version " + stringify(moduleMesosVersion.get()));
  }

  if (moduleBase->compatible == NULL) {
    if (moduleMesosVersion.get() != mesosVersion.get()) {
      return Error(
          "Mesos has version " + stringify(mesosVersion.get()) +
          ", but module is compiled with version " +
          stringify(moduleMesosVersion.get()));
    }
    return Nothing();
  }

  if (moduleMesosVersion.get() > mesosVersion.get()) {
    return Error(
        "Mesos has version " + stringify(mesosVersion.get()) +
        ", but module is compiled with version " +
        stringify(moduleMesosVersion.get()));
  }

  bool result = moduleBase->compatible();
  if (!result) {
    return Error("Module " + moduleName + "has determined to be incompatible");
  }

  return Nothing();
}

Try<Nothing> ModuleManager::load(const Modules& modules)
{
  Lock lock(&mutex);
  initialize();

  foreach (const Modules::Library& library, modules.libraries()) {
    if (!library.has_path()) {
      return Error("Library path not provided");
    }

    Owned<DynamicLibrary> dynamicLibrary(new DynamicLibrary());
    Try<Nothing> result = dynamicLibrary->open(library.path());
    if (!result.isSome()) {
      return Error("Error opening library: '" + library.path() + "'");
    }

    // Currently we never delete the DynamicLibrary instance nor do we
    // expose a way to delete it so for now we just put it in a list.
    // TODO(karya): If we add the functionality to "unload" a module
    // library, we should make this pointer addressable by something
    // like the module name.
    dynamicLibraries.push_back(dynamicLibrary);

    // Load module manifests.
    foreach (const string& moduleName, library.modules()) {
      Try<void*> symbol =  dynamicLibrary->loadSymbol(moduleName);
      if (symbol.isError()) {
        return Error(
	    "Error loading module '" + moduleName + "': " + symbol.error());
      }
      ModuleBase* moduleBase = (ModuleBase*) symbol.get();
      Try<Nothing> result = verifyModule(moduleName, moduleBase);
      if (result.isError()) {
	return Error(
	    "Error verifying module '" + moduleName + "': " + result.error());
      }
      moduleBases[moduleName] = (ModuleBase*) symbol.get();
    }
  }

  return Nothing();
}

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

#include <string>
#include <vector>

#include <mesos/module.hpp>

#include <stout/json.hpp>
#include <stout/numify.hpp>
#include <stout/os.hpp>
#include <stout/strings.hpp>
#include <stout/stringify.hpp>
#include <stout/version.hpp>

#include "manager.hpp"

using std::list;
using std::string;
using std::vector;
using process::Owned;

namespace mesos {
namespace internal {

pthread_mutex_t ModuleManager::mutex = PTHREAD_MUTEX_INITIALIZER;
hashmap<const string, string> ModuleManager::kindToVersion;
hashmap<const string, ModuleBase*> ModuleManager::moduleBases;
list<Owned<DynamicLibrary> > ModuleManager::dynamicLibraries;

void ModuleManager::initialize()
{
  // ATTENTION: Every time a Mesos developer breaks compatibility with
  // a module kind type, this table needs to be updated.
  // Specifically, the version value in the entry corresponding to the
  // kind needs to be set to the Mesos version that affects the
  // current change.  Typically that should be the version currently
  // under development.

  kindToVersion["TestModule"] = MESOS_VERSION;

  // What happens then when Mesos is built with a certain version,
  // 'kindToVersion' states a certain other minimum version, and a
  // module library is built against "module.hpp" belonging to yet
  // another Mesos version?
  //
  // Mesos can admit modules built against earlier versions of itself
  // by stating so explicitly in 'kindToVersion'.  If a modules is
  // built with a Mesos version greater than or equal to the one
  // stated in 'kindToVersion', it passes this verification step.
  // Otherwise it is rejected when attempting to load it.
  //
  // Here are some examples:
  //
  // Mesos   kindToVersion    library    modules loadable?
  // 0.18.0      0.18.0       0.18.0          YES
  // 0.29.0      0.18.0       0.18.0          YES
  // 0.29.0      0.18.0       0.21.0          YES
  // 0.18.0      0.18.0       0.29.0          NO
  // 0.29.0      0.21.0       0.18.0          NO
  // 0.29.0      0.29.0       0.18.0          NO

  // ATTENTION: This mechanism only protects the interfaces of
  // modules, not how they maintain functional compatibility with
  // Mesos and among each other.  This is covered by their own
  // "isCompatible" call.
}

// For testing only.  Unload all dlopen()'d module libraries and
// clear the list of module manifests.
void ModuleManager::unloadAll()
{
  kindToVersion.clear();
  moduleBases.clear();
  dynamicLibraries.clear();
}

// TODO(karya): Show library author info for failed library/module.
Try<Nothing> ModuleManager::verifyModule(
    const string& moduleName,
    const ModuleBase* moduleBase)
{
  CHECK_NOTNULL(moduleBase);
  if (moduleBase->mesosVersion == NULL ||
      moduleBase->moduleApiVersion == NULL ||
      moduleBase->authorName == NULL ||
      moduleBase->authorEmail == NULL ||
      moduleBase->description == NULL ||
      moduleBase->kind == NULL) {
    return Error("Error loading module '" + moduleName + "'; missing fields");
  }

  // Verify module API version.
  if (stringify(moduleBase->moduleApiVersion) != MESOS_MODULE_API_VERSION) {
    return Error(
        "Module API version mismatch. Mesos has: " MESOS_MODULE_API_VERSION ", "
        "library requires: " + stringify(moduleBase->moduleApiVersion));
  }

  if (!kindToVersion.contains(moduleBase->kind)) {
    return Error("Unknown module kind: " + stringify(moduleBase->kind));
  }

  Try<Version> mesosVersion = Version::parse(MESOS_VERSION);
  CHECK(!mesosVersion.isError());

  Try<Version> minimumVersion = Version::parse(kindToVersion[moduleBase->kind]);
  CHECK(!minimumVersion.isError());

  Try<Version> moduleMesosVersion = Version::parse(moduleBase->mesosVersion);
  if (moduleMesosVersion.isError()) {
    return Error(moduleMesosVersion.error());
  }

  if (moduleMesosVersion.get() < minimumVersion.get()) {
    return Error(
        "Minimum supported mesos version for '" + stringify(moduleBase->kind) +
        "' is " + stringify(minimumVersion.get()) + ", but module is compiled "
        "with version " + stringify(moduleMesosVersion.get()));
  }

  if (moduleBase->compatible == NULL) {
    if (moduleMesosVersion.get() != mesosVersion.get()) {
      return Error(
          "Mesos has version " + stringify(mesosVersion.get()) +
          ", but module is compiled with version " +
          stringify(moduleMesosVersion.get()));
    }
    return Nothing();
  }

  if (moduleMesosVersion.get() > mesosVersion.get()) {
    return Error(
        "Mesos has version " + stringify(mesosVersion.get()) +
        ", but module is compiled with version " +
        stringify(moduleMesosVersion.get()));
  }

  bool result = moduleBase->compatible();
  if (!result) {
    return Error("Module " + moduleName + "has determined to be incompatible");
  }

  return Nothing();
}

Try<Nothing> ModuleManager::load(const Modules& modules)
{
  Lock lock(&mutex);
  initialize();

  foreach (const Modules::Library& library, modules.libraries()) {
    if (!library.has_path()) {
      return Error("Library path not provided");
    }

    Owned<DynamicLibrary> dynamicLibrary(new DynamicLibrary());
    Try<Nothing> result = dynamicLibrary->open(library.path());
    if (!result.isSome()) {
      return Error("Error opening library: '" + library.path() + "'");
    }

    // Currently we never delete the DynamicLibrary instance nor do we
    // expose a way to delete it so for now we just put it in a list.
    // TODO(karya): If we add the functionality to "unload" a module
    // library, we should make this pointer addressable by something
    // like the module name.
    dynamicLibraries.push_back(dynamicLibrary);

    // Load module manifests.
    foreach (const string& moduleName, library.modules()) {
      Try<void*> symbol =  dynamicLibrary->loadSymbol(moduleName);
      if (symbol.isError()) {
        return Error(
            "Error loading module '" + moduleName + "': " + symbol.error());
      }
      ModuleBase* moduleBase = (ModuleBase*) symbol.get();
      Try<Nothing> result = verifyModule(moduleName, moduleBase);
      if (result.isError()) {
        return Error(
            "Error verifying module '" + moduleName + "': " + result.error());
      }
      moduleBases[moduleName] = (ModuleBase*) symbol.get();
    }
  }

  return Nothing();
}

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


<commit_before>#include "opcuasecondary.h"

#include <opcuabridge/opcuabridgeclient.h>
#include "secondaryconfig.h"

#include <logging.h>
#include <ostreereposync.h>
#include <utils.h>

#include <algorithm>
#include <iterator>

#include <boost/filesystem.hpp>
#include <boost/iostreams/device/mapped_file.hpp>

namespace fs = boost::filesystem;
namespace io = boost::iostreams;

namespace Uptane {

OpcuaSecondary::OpcuaSecondary(const SecondaryConfig& sconfig) : SecondaryInterface(sconfig) {}

OpcuaSecondary::~OpcuaSecondary() {}

std::string OpcuaSecondary::getSerial() {
  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  return client.recvConfiguration().getSerial();
}
std::string OpcuaSecondary::getHwId() {
  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  return client.recvConfiguration().getHwId();
}
std::pair<KeyType, std::string> OpcuaSecondary::getPublicKey() {
  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  return std::make_pair(client.recvConfiguration().getPublicKeyType(), client.recvConfiguration().getPublicKey());
}

Json::Value OpcuaSecondary::getManifest() {
  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  auto original_manifest = client.recvOriginalManifest().getBlock();
  return Utils::parseJSON(std::string(original_manifest.begin(), original_manifest.end()));
}

bool OpcuaSecondary::putMetadata(const MetaPack& meta_pack) {
  std::vector<opcuabridge::MetadataFile> metadatafiles;
  {
    opcuabridge::MetadataFile mf;
    mf.setMetadata(meta_pack.director_root.original());
    metadatafiles.push_back(mf);
  }
  {
    opcuabridge::MetadataFile mf;
    mf.setMetadata(meta_pack.director_targets.original());
    metadatafiles.push_back(mf);
  }
  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  return client.sendMetadataFiles(metadatafiles);
}

bool OpcuaSecondary::sendFirmware(const std::string& data) {
  Json::Value data_json = Utils::parseJSON(data);

  const fs::path source_repo_dir_path(ostree_repo_sync::GetOstreeRepoPath(data_json["sysroot_path"].asString()));

  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  if (!client) return false;

  bool retval = true;
  if (ostree_repo_sync::ArchiveModeRepo(source_repo_dir_path)) {
    retval = client.syncDirectoryFiles(source_repo_dir_path);
  } else {
    TemporaryDirectory temp_dir("opcuabridge-ostree-sync-working-repo");
    const fs::path working_repo_dir_path = temp_dir.Path();

    if (!ostree_repo_sync::LocalPullRepo(source_repo_dir_path, working_repo_dir_path,
                                         data_json["ref_hash"].asString())) {
      LOG_ERROR << "OSTree repo sync failed: unable to local pull from " << source_repo_dir_path.native();
      return false;
    }
    retval = client.syncDirectoryFiles(working_repo_dir_path);
  }
  return retval;
}

int OpcuaSecondary::getRootVersion(bool director) {
  LOG_ERROR << "OpcuaSecondary::getRootVersion is not implemented yet";
  return 0;
}

bool OpcuaSecondary::putRoot(Uptane::Root root, bool director) {
  LOG_ERROR << "OpcuaSecondary::putRoot is not implemented yet";
  return false;
}

}  // namespace Uptane
<commit_msg>Fix unused argument warnings<commit_after>#include "opcuasecondary.h"

#include <opcuabridge/opcuabridgeclient.h>
#include "secondaryconfig.h"

#include <logging.h>
#include <ostreereposync.h>
#include <utils.h>

#include <algorithm>
#include <iterator>

#include <boost/filesystem.hpp>
#include <boost/iostreams/device/mapped_file.hpp>

namespace fs = boost::filesystem;
namespace io = boost::iostreams;

namespace Uptane {

OpcuaSecondary::OpcuaSecondary(const SecondaryConfig& sconfig) : SecondaryInterface(sconfig) {}

OpcuaSecondary::~OpcuaSecondary() {}

std::string OpcuaSecondary::getSerial() {
  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  return client.recvConfiguration().getSerial();
}
std::string OpcuaSecondary::getHwId() {
  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  return client.recvConfiguration().getHwId();
}
std::pair<KeyType, std::string> OpcuaSecondary::getPublicKey() {
  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  return std::make_pair(client.recvConfiguration().getPublicKeyType(), client.recvConfiguration().getPublicKey());
}

Json::Value OpcuaSecondary::getManifest() {
  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  auto original_manifest = client.recvOriginalManifest().getBlock();
  return Utils::parseJSON(std::string(original_manifest.begin(), original_manifest.end()));
}

bool OpcuaSecondary::putMetadata(const MetaPack& meta_pack) {
  std::vector<opcuabridge::MetadataFile> metadatafiles;
  {
    opcuabridge::MetadataFile mf;
    mf.setMetadata(meta_pack.director_root.original());
    metadatafiles.push_back(mf);
  }
  {
    opcuabridge::MetadataFile mf;
    mf.setMetadata(meta_pack.director_targets.original());
    metadatafiles.push_back(mf);
  }
  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  return client.sendMetadataFiles(metadatafiles);
}

bool OpcuaSecondary::sendFirmware(const std::string& data) {
  Json::Value data_json = Utils::parseJSON(data);

  const fs::path source_repo_dir_path(ostree_repo_sync::GetOstreeRepoPath(data_json["sysroot_path"].asString()));

  opcuabridge::Client client{opcuabridge::SelectEndPoint(SecondaryInterface::sconfig)};
  if (!client) return false;

  bool retval = true;
  if (ostree_repo_sync::ArchiveModeRepo(source_repo_dir_path)) {
    retval = client.syncDirectoryFiles(source_repo_dir_path);
  } else {
    TemporaryDirectory temp_dir("opcuabridge-ostree-sync-working-repo");
    const fs::path working_repo_dir_path = temp_dir.Path();

    if (!ostree_repo_sync::LocalPullRepo(source_repo_dir_path, working_repo_dir_path,
                                         data_json["ref_hash"].asString())) {
      LOG_ERROR << "OSTree repo sync failed: unable to local pull from " << source_repo_dir_path.native();
      return false;
    }
    retval = client.syncDirectoryFiles(working_repo_dir_path);
  }
  return retval;
}

int OpcuaSecondary::getRootVersion(bool /* director */) {
  LOG_ERROR << "OpcuaSecondary::getRootVersion is not implemented yet";
  return 0;
}

bool OpcuaSecondary::putRoot(Uptane::Root /* root */, bool /* director */) {
  LOG_ERROR << "OpcuaSecondary::putRoot is not implemented yet";
  return false;
}

}  // namespace Uptane
<|endoftext|>