<commit_before>/*
 *
 * Copyright 2018 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <grpc/support/port_platform.h>

#include "src/core/lib/security/security_connector/ssl/ssl_security_connector.h"

#include <stdbool.h>

#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>

#include "src/core/ext/transport/chttp2/alpn/alpn.h"
#include "src/core/lib/channel/handshaker.h"
#include "src/core/lib/gpr/host_port.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/security/context/security_context.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/security/credentials/ssl/ssl_credentials.h"
#include "src/core/lib/security/security_connector/load_system_roots.h"
#include "src/core/lib/security/security_connector/ssl_utils.h"
#include "src/core/lib/security/transport/security_handshaker.h"
#include "src/core/tsi/ssl_transport_security.h"
#include "src/core/tsi/transport_security.h"

namespace {

class grpc_ssl_channel_security_connector final
    : public grpc_channel_security_connector {
 public:
  grpc_ssl_channel_security_connector(
      grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
      grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
      const grpc_ssl_config* config, const char* target_name,
      const char* overridden_target_name)
      : grpc_channel_security_connector(GRPC_SSL_URL_SCHEME,
                                        std::move(channel_creds),
                                        std::move(request_metadata_creds)),
        overridden_target_name_(overridden_target_name == nullptr
                                    ? nullptr
                                    : gpr_strdup(overridden_target_name)),
        verify_options_(&config->verify_options) {
    char* port;
    gpr_split_host_port(target_name, &target_name_, &port);
    gpr_free(port);
  }

  ~grpc_ssl_channel_security_connector() override {
    tsi_ssl_client_handshaker_factory_unref(client_handshaker_factory_);
    if (target_name_ != nullptr) gpr_free(target_name_);
    if (overridden_target_name_ != nullptr) gpr_free(overridden_target_name_);
  }

  grpc_security_status InitializeHandshakerFactory(
      const grpc_ssl_config* config, tsi_ssl_session_cache* ssl_session_cache) {
    return grpc_ssl_tsi_client_handshaker_factory_init(
        config->pem_key_cert_pair, config->pem_root_certs, ssl_session_cache,
        &client_handshaker_factory_);
  }

  void add_handshakers(grpc_pollset_set* interested_parties,
                       grpc_core::HandshakeManager* handshake_mgr) override {
    // Instantiate TSI handshaker.
    tsi_handshaker* tsi_hs = nullptr;
    tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker(
        client_handshaker_factory_,
        overridden_target_name_ != nullptr ? overridden_target_name_
                                           : target_name_,
        &tsi_hs);
    if (result != TSI_OK) {
      gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
              tsi_result_to_string(result));
      return;
    }
    // Create handshakers.
    handshake_mgr->Add(grpc_core::SecurityHandshakerCreate(tsi_hs, this));
  }

  void check_peer(tsi_peer peer, grpc_endpoint* ep,
                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
                  grpc_closure* on_peer_checked) override {
    const char* target_name = overridden_target_name_ != nullptr
                                  ? overridden_target_name_
                                  : target_name_;
    grpc_error* error = grpc_ssl_check_alpn(&peer);
    if (error == GRPC_ERROR_NONE) {
      error = grpc_ssl_check_peer_name(target_name, &peer);
      if (error == GRPC_ERROR_NONE) {
        if (verify_options_->verify_peer_callback != nullptr) {
          const tsi_peer_property* p =
              tsi_peer_get_property_by_name(&peer, TSI_X509_PEM_CERT_PROPERTY);
          if (p == nullptr) {
            error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
                "Cannot check peer: missing pem cert property.");
          } else {
            char* peer_pem =
                static_cast<char*>(gpr_malloc(p->value.length + 1));
            memcpy(peer_pem, p->value.data, p->value.length);
            peer_pem[p->value.length] = '\0';
            int callback_status = verify_options_->verify_peer_callback(
                target_name, peer_pem,
                verify_options_->verify_peer_callback_userdata);
            gpr_free(peer_pem);
            if (callback_status) {
              char* msg;
              gpr_asprintf(&msg, "Verify peer callback returned a failure (%d)",
                           callback_status);
              error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
              gpr_free(msg);
            }
          }
        }
        *auth_context = grpc_ssl_peer_to_auth_context(&peer);
      }
    }
    GRPC_CLOSURE_SCHED(on_peer_checked, error);
    tsi_peer_destruct(&peer);
  }

  int cmp(const grpc_security_connector* other_sc) const override {
    auto* other =
        reinterpret_cast<const grpc_ssl_channel_security_connector*>(other_sc);
    int c = channel_security_connector_cmp(other);
    if (c != 0) return c;
    return grpc_ssl_cmp_target_name(target_name_, other->target_name_,
                                    overridden_target_name_,
                                    other->overridden_target_name_);
  }

  bool check_call_host(const char* host, grpc_auth_context* auth_context,
                       grpc_closure* on_call_host_checked,
                       grpc_error** error) override {
    return grpc_ssl_check_call_host(host, target_name_, overridden_target_name_,
                                    auth_context, on_call_host_checked, error);
  }

  void cancel_check_call_host(grpc_closure* on_call_host_checked,
                              grpc_error* error) override {
    GRPC_ERROR_UNREF(error);
  }

 private:
  tsi_ssl_client_handshaker_factory* client_handshaker_factory_;
  char* target_name_;
  char* overridden_target_name_;
  const verify_peer_options* verify_options_;
};

class grpc_ssl_server_security_connector
    : public grpc_server_security_connector {
 public:
  grpc_ssl_server_security_connector(
      grpc_core::RefCountedPtr<grpc_server_credentials> server_creds)
      : grpc_server_security_connector(GRPC_SSL_URL_SCHEME,
                                       std::move(server_creds)) {}

  ~grpc_ssl_server_security_connector() override {
    tsi_ssl_server_handshaker_factory_unref(server_handshaker_factory_);
  }

  bool has_cert_config_fetcher() const {
    return static_cast<const grpc_ssl_server_credentials*>(server_creds())
        ->has_cert_config_fetcher();
  }

  const tsi_ssl_server_handshaker_factory* server_handshaker_factory() const {
    return server_handshaker_factory_;
  }

  grpc_security_status InitializeHandshakerFactory() {
    grpc_security_status retval = GRPC_SECURITY_OK;
    if (has_cert_config_fetcher()) {
      // Load initial credentials from certificate_config_fetcher:
      if (!try_fetch_ssl_server_credentials()) {
        gpr_log(GPR_ERROR,
                "Failed loading SSL server credentials from fetcher.");
        retval = GRPC_SECURITY_ERROR;
      }
    } else {
      auto* server_credentials =
          static_cast<const grpc_ssl_server_credentials*>(server_creds());
      retval = grpc_ssl_tsi_server_handshaker_factory_init(
          server_credentials->config().pem_key_cert_pairs,
          server_credentials->config().num_key_cert_pairs,
          server_credentials->config().pem_root_certs,
          server_credentials->config().client_certificate_request,
          &server_handshaker_factory_);
    }
    return retval;
  }

  void add_handshakers(grpc_pollset_set* interested_parties,
                       grpc_core::HandshakeManager* handshake_mgr) override {
    // Instantiate TSI handshaker.
    try_fetch_ssl_server_credentials();
    tsi_handshaker* tsi_hs = nullptr;
    tsi_result result = tsi_ssl_server_handshaker_factory_create_handshaker(
        server_handshaker_factory_, &tsi_hs);
    if (result != TSI_OK) {
      gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
              tsi_result_to_string(result));
      return;
    }
    // Create handshakers.
    handshake_mgr->Add(grpc_core::SecurityHandshakerCreate(tsi_hs, this));
  }

  void check_peer(tsi_peer peer, grpc_endpoint* ep,
                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
                  grpc_closure* on_peer_checked) override {
    grpc_error* error = grpc_ssl_check_alpn(&peer);
    *auth_context = grpc_ssl_peer_to_auth_context(&peer);
    tsi_peer_destruct(&peer);
    GRPC_CLOSURE_SCHED(on_peer_checked, error);
  }

  int cmp(const grpc_security_connector* other) const override {
    return server_security_connector_cmp(
        static_cast<const grpc_server_security_connector*>(other));
  }

 private:
  /* Attempts to fetch the server certificate config if a callback is available.
   * Current certificate config will continue to be used if the callback returns
   * an error. Returns true if new credentials were sucessfully loaded. */
  bool try_fetch_ssl_server_credentials() {
    grpc_ssl_server_certificate_config* certificate_config = nullptr;
    bool status;
    if (!has_cert_config_fetcher()) return false;
    grpc_ssl_server_credentials* server_creds =
        static_cast<grpc_ssl_server_credentials*>(this->mutable_server_creds());
    grpc_ssl_certificate_config_reload_status cb_result =
        server_creds->FetchCertConfig(&certificate_config);
    if (cb_result == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED) {
      gpr_log(GPR_DEBUG, "No change in SSL server credentials.");
      status = false;
    } else if (cb_result == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW) {
      status = try_replace_server_handshaker_factory(certificate_config);
    } else {
      // Log error, continue using previously-loaded credentials.
      gpr_log(GPR_ERROR,
              "Failed fetching new server credentials, continuing to "
              "use previously-loaded credentials.");
      status = false;
    }
    if (certificate_config != nullptr) {
      grpc_ssl_server_certificate_config_destroy(certificate_config);
    }
    return status;
  }

  /* Attempts to replace the server_handshaker_factory with a new factory using
   * the provided grpc_ssl_server_certificate_config. Should new factory
   * creation fail, the existing factory will not be replaced. Returns true on
   * success (new factory created). */
  bool try_replace_server_handshaker_factory(
      const grpc_ssl_server_certificate_config* config) {
    if (config == nullptr) {
      gpr_log(GPR_ERROR,
              "Server certificate config callback returned invalid (NULL) "
              "config.");
      return false;
    }
    tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs =
        grpc_convert_grpc_to_tsi_cert_pairs(config->pem_key_cert_pairs,
                                            config->num_key_cert_pairs);
    const grpc_ssl_server_credentials* server_credentials =
        static_cast<const grpc_ssl_server_credentials*>(this->server_creds());
    tsi_ssl_server_handshaker_factory* new_handshaker_factory = nullptr;
    grpc_security_status retval = grpc_ssl_tsi_server_handshaker_factory_init(
        pem_key_cert_pairs, config->num_key_cert_pairs, config->pem_root_certs,
        server_credentials->config().client_certificate_request,
        &new_handshaker_factory);
    gpr_free(pem_key_cert_pairs);
    if (retval != GRPC_SECURITY_OK) {
      return false;
    }
    set_server_handshaker_factory(new_handshaker_factory);
    return true;
  }

  void set_server_handshaker_factory(
      tsi_ssl_server_handshaker_factory* new_factory) {
    if (server_handshaker_factory_) {
      tsi_ssl_server_handshaker_factory_unref(server_handshaker_factory_);
    }
    server_handshaker_factory_ = new_factory;
  }

  tsi_ssl_server_handshaker_factory* server_handshaker_factory_ = nullptr;
};
}  // namespace

grpc_core::RefCountedPtr<grpc_channel_security_connector>
grpc_ssl_channel_security_connector_create(
    grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
    const grpc_ssl_config* config, const char* target_name,
    const char* overridden_target_name,
    tsi_ssl_session_cache* ssl_session_cache) {
  if (config == nullptr || target_name == nullptr) {
    gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name.");
    return nullptr;
  }
  grpc_core::RefCountedPtr<grpc_ssl_channel_security_connector> c =
      grpc_core::MakeRefCounted<grpc_ssl_channel_security_connector>(
          std::move(channel_creds), std::move(request_metadata_creds), config,
          target_name, overridden_target_name);
  const grpc_security_status result =
      c->InitializeHandshakerFactory(config, ssl_session_cache);
  if (result != GRPC_SECURITY_OK) {
    return nullptr;
  }
  return c;
}

grpc_core::RefCountedPtr<grpc_server_security_connector>
grpc_ssl_server_security_connector_create(
    grpc_core::RefCountedPtr<grpc_server_credentials> server_credentials) {
  GPR_ASSERT(server_credentials != nullptr);
  grpc_core::RefCountedPtr<grpc_ssl_server_security_connector> c =
      grpc_core::MakeRefCounted<grpc_ssl_server_security_connector>(
          std::move(server_credentials));
  const grpc_security_status retval = c->InitializeHandshakerFactory();
  if (retval != GRPC_SECURITY_OK) {
    return nullptr;
  }
  return c;
}
<commit_msg>fail-fast if no pem root certs are available.<commit_after>/*
 *
 * Copyright 2018 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <grpc/support/port_platform.h>

#include "src/core/lib/security/security_connector/ssl/ssl_security_connector.h"

#include <stdbool.h>

#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>

#include "src/core/ext/transport/chttp2/alpn/alpn.h"
#include "src/core/lib/channel/handshaker.h"
#include "src/core/lib/gpr/host_port.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/security/context/security_context.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/security/credentials/ssl/ssl_credentials.h"
#include "src/core/lib/security/security_connector/load_system_roots.h"
#include "src/core/lib/security/security_connector/ssl_utils.h"
#include "src/core/lib/security/transport/security_handshaker.h"
#include "src/core/tsi/ssl_transport_security.h"
#include "src/core/tsi/transport_security.h"

namespace {

class grpc_ssl_channel_security_connector final
    : public grpc_channel_security_connector {
 public:
  grpc_ssl_channel_security_connector(
      grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
      grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
      const grpc_ssl_config* config, const char* target_name,
      const char* overridden_target_name)
      : grpc_channel_security_connector(GRPC_SSL_URL_SCHEME,
                                        std::move(channel_creds),
                                        std::move(request_metadata_creds)),
        overridden_target_name_(overridden_target_name == nullptr
                                    ? nullptr
                                    : gpr_strdup(overridden_target_name)),
        verify_options_(&config->verify_options) {
    char* port;
    gpr_split_host_port(target_name, &target_name_, &port);
    gpr_free(port);
  }

  ~grpc_ssl_channel_security_connector() override {
    tsi_ssl_client_handshaker_factory_unref(client_handshaker_factory_);
    if (target_name_ != nullptr) gpr_free(target_name_);
    if (overridden_target_name_ != nullptr) gpr_free(overridden_target_name_);
  }

  grpc_security_status InitializeHandshakerFactory(
      const grpc_ssl_config* config, tsi_ssl_session_cache* ssl_session_cache) {
    return grpc_ssl_tsi_client_handshaker_factory_init(
        config->pem_key_cert_pair, config->pem_root_certs, ssl_session_cache,
        &client_handshaker_factory_);
  }

  void add_handshakers(grpc_pollset_set* interested_parties,
                       grpc_core::HandshakeManager* handshake_mgr) override {
    // Instantiate TSI handshaker.
    tsi_handshaker* tsi_hs = nullptr;
    tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker(
        client_handshaker_factory_,
        overridden_target_name_ != nullptr ? overridden_target_name_
                                           : target_name_,
        &tsi_hs);
    if (result != TSI_OK) {
      gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
              tsi_result_to_string(result));
      return;
    }
    // Create handshakers.
    handshake_mgr->Add(grpc_core::SecurityHandshakerCreate(tsi_hs, this));
  }

  void check_peer(tsi_peer peer, grpc_endpoint* ep,
                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
                  grpc_closure* on_peer_checked) override {
    const char* target_name = overridden_target_name_ != nullptr
                                  ? overridden_target_name_
                                  : target_name_;
    grpc_error* error = grpc_ssl_check_alpn(&peer);
    if (error == GRPC_ERROR_NONE) {
      error = grpc_ssl_check_peer_name(target_name, &peer);
      if (error == GRPC_ERROR_NONE) {
        if (verify_options_->verify_peer_callback != nullptr) {
          const tsi_peer_property* p =
              tsi_peer_get_property_by_name(&peer, TSI_X509_PEM_CERT_PROPERTY);
          if (p == nullptr) {
            error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
                "Cannot check peer: missing pem cert property.");
          } else {
            char* peer_pem =
                static_cast<char*>(gpr_malloc(p->value.length + 1));
            memcpy(peer_pem, p->value.data, p->value.length);
            peer_pem[p->value.length] = '\0';
            int callback_status = verify_options_->verify_peer_callback(
                target_name, peer_pem,
                verify_options_->verify_peer_callback_userdata);
            gpr_free(peer_pem);
            if (callback_status) {
              char* msg;
              gpr_asprintf(&msg, "Verify peer callback returned a failure (%d)",
                           callback_status);
              error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
              gpr_free(msg);
            }
          }
        }
        *auth_context = grpc_ssl_peer_to_auth_context(&peer);
      }
    }
    GRPC_CLOSURE_SCHED(on_peer_checked, error);
    tsi_peer_destruct(&peer);
  }

  int cmp(const grpc_security_connector* other_sc) const override {
    auto* other =
        reinterpret_cast<const grpc_ssl_channel_security_connector*>(other_sc);
    int c = channel_security_connector_cmp(other);
    if (c != 0) return c;
    return grpc_ssl_cmp_target_name(target_name_, other->target_name_,
                                    overridden_target_name_,
                                    other->overridden_target_name_);
  }

  bool check_call_host(const char* host, grpc_auth_context* auth_context,
                       grpc_closure* on_call_host_checked,
                       grpc_error** error) override {
    return grpc_ssl_check_call_host(host, target_name_, overridden_target_name_,
                                    auth_context, on_call_host_checked, error);
  }

  void cancel_check_call_host(grpc_closure* on_call_host_checked,
                              grpc_error* error) override {
    GRPC_ERROR_UNREF(error);
  }

 private:
  tsi_ssl_client_handshaker_factory* client_handshaker_factory_;
  char* target_name_;
  char* overridden_target_name_;
  const verify_peer_options* verify_options_;
};

class grpc_ssl_server_security_connector
    : public grpc_server_security_connector {
 public:
  grpc_ssl_server_security_connector(
      grpc_core::RefCountedPtr<grpc_server_credentials> server_creds)
      : grpc_server_security_connector(GRPC_SSL_URL_SCHEME,
                                       std::move(server_creds)) {}

  ~grpc_ssl_server_security_connector() override {
    tsi_ssl_server_handshaker_factory_unref(server_handshaker_factory_);
  }

  bool has_cert_config_fetcher() const {
    return static_cast<const grpc_ssl_server_credentials*>(server_creds())
        ->has_cert_config_fetcher();
  }

  const tsi_ssl_server_handshaker_factory* server_handshaker_factory() const {
    return server_handshaker_factory_;
  }

  grpc_security_status InitializeHandshakerFactory() {
    grpc_security_status retval = GRPC_SECURITY_OK;
    if (has_cert_config_fetcher()) {
      // Load initial credentials from certificate_config_fetcher:
      if (!try_fetch_ssl_server_credentials()) {
        gpr_log(GPR_ERROR,
                "Failed loading SSL server credentials from fetcher.");
        retval = GRPC_SECURITY_ERROR;
      }
    } else {
      auto* server_credentials =
          static_cast<const grpc_ssl_server_credentials*>(server_creds());
      retval = grpc_ssl_tsi_server_handshaker_factory_init(
          server_credentials->config().pem_key_cert_pairs,
          server_credentials->config().num_key_cert_pairs,
          server_credentials->config().pem_root_certs,
          server_credentials->config().client_certificate_request,
          &server_handshaker_factory_);
    }
    return retval;
  }

  void add_handshakers(grpc_pollset_set* interested_parties,
                       grpc_core::HandshakeManager* handshake_mgr) override {
    // Instantiate TSI handshaker.
    try_fetch_ssl_server_credentials();
    tsi_handshaker* tsi_hs = nullptr;
    tsi_result result = tsi_ssl_server_handshaker_factory_create_handshaker(
        server_handshaker_factory_, &tsi_hs);
    if (result != TSI_OK) {
      gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
              tsi_result_to_string(result));
      return;
    }
    // Create handshakers.
    handshake_mgr->Add(grpc_core::SecurityHandshakerCreate(tsi_hs, this));
  }

  void check_peer(tsi_peer peer, grpc_endpoint* ep,
                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
                  grpc_closure* on_peer_checked) override {
    grpc_error* error = grpc_ssl_check_alpn(&peer);
    *auth_context = grpc_ssl_peer_to_auth_context(&peer);
    tsi_peer_destruct(&peer);
    GRPC_CLOSURE_SCHED(on_peer_checked, error);
  }

  int cmp(const grpc_security_connector* other) const override {
    return server_security_connector_cmp(
        static_cast<const grpc_server_security_connector*>(other));
  }

 private:
  /* Attempts to fetch the server certificate config if a callback is available.
   * Current certificate config will continue to be used if the callback returns
   * an error. Returns true if new credentials were sucessfully loaded. */
  bool try_fetch_ssl_server_credentials() {
    grpc_ssl_server_certificate_config* certificate_config = nullptr;
    bool status;
    if (!has_cert_config_fetcher()) return false;
    grpc_ssl_server_credentials* server_creds =
        static_cast<grpc_ssl_server_credentials*>(this->mutable_server_creds());
    grpc_ssl_certificate_config_reload_status cb_result =
        server_creds->FetchCertConfig(&certificate_config);
    if (cb_result == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED) {
      gpr_log(GPR_DEBUG, "No change in SSL server credentials.");
      status = false;
    } else if (cb_result == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW) {
      status = try_replace_server_handshaker_factory(certificate_config);
    } else {
      // Log error, continue using previously-loaded credentials.
      gpr_log(GPR_ERROR,
              "Failed fetching new server credentials, continuing to "
              "use previously-loaded credentials.");
      status = false;
    }
    if (certificate_config != nullptr) {
      grpc_ssl_server_certificate_config_destroy(certificate_config);
    }
    return status;
  }

  /* Attempts to replace the server_handshaker_factory with a new factory using
   * the provided grpc_ssl_server_certificate_config. Should new factory
   * creation fail, the existing factory will not be replaced. Returns true on
   * success (new factory created). */
  bool try_replace_server_handshaker_factory(
      const grpc_ssl_server_certificate_config* config) {
    if (config == nullptr) {
      gpr_log(GPR_ERROR,
              "Server certificate config callback returned invalid (NULL) "
              "config.");
      return false;
    }
    tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs =
        grpc_convert_grpc_to_tsi_cert_pairs(config->pem_key_cert_pairs,
                                            config->num_key_cert_pairs);
    const grpc_ssl_server_credentials* server_credentials =
        static_cast<const grpc_ssl_server_credentials*>(this->server_creds());
    tsi_ssl_server_handshaker_factory* new_handshaker_factory = nullptr;
    grpc_security_status retval = grpc_ssl_tsi_server_handshaker_factory_init(
        pem_key_cert_pairs, config->num_key_cert_pairs, config->pem_root_certs,
        server_credentials->config().client_certificate_request,
        &new_handshaker_factory);
    gpr_free(pem_key_cert_pairs);
    if (retval != GRPC_SECURITY_OK) {
      return false;
    }
    set_server_handshaker_factory(new_handshaker_factory);
    return true;
  }

  void set_server_handshaker_factory(
      tsi_ssl_server_handshaker_factory* new_factory) {
    if (server_handshaker_factory_) {
      tsi_ssl_server_handshaker_factory_unref(server_handshaker_factory_);
    }
    server_handshaker_factory_ = new_factory;
  }

  tsi_ssl_server_handshaker_factory* server_handshaker_factory_ = nullptr;
};
}  // namespace

grpc_core::RefCountedPtr<grpc_channel_security_connector>
grpc_ssl_channel_security_connector_create(
    grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
    const grpc_ssl_config* config, const char* target_name,
    const char* overridden_target_name,
    tsi_ssl_session_cache* ssl_session_cache) {
  if (config == nullptr || target_name == nullptr) {
    gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name.");
    return nullptr;
  }
  if (config->pem_root_certs == nullptr &&
      grpc_core::DefaultSslRootStore::GetPemRootCerts() == nullptr) {
    gpr_log(GPR_ERROR, "Could not get pem root certs.");
    return nullptr;
  }
  grpc_core::RefCountedPtr<grpc_ssl_channel_security_connector> c =
      grpc_core::MakeRefCounted<grpc_ssl_channel_security_connector>(
          std::move(channel_creds), std::move(request_metadata_creds), config,
          target_name, overridden_target_name);
  const grpc_security_status result =
      c->InitializeHandshakerFactory(config, ssl_session_cache);
  if (result != GRPC_SECURITY_OK) {
    return nullptr;
  }
  return c;
}

grpc_core::RefCountedPtr<grpc_server_security_connector>
grpc_ssl_server_security_connector_create(
    grpc_core::RefCountedPtr<grpc_server_credentials> server_credentials) {
  GPR_ASSERT(server_credentials != nullptr);
  grpc_core::RefCountedPtr<grpc_ssl_server_security_connector> c =
      grpc_core::MakeRefCounted<grpc_ssl_server_security_connector>(
          std::move(server_credentials));
  const grpc_security_status retval = c->InitializeHandshakerFactory();
  if (retval != GRPC_SECURITY_OK) {
    return nullptr;
  }
  return c;
}
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/nvdimm_utils.H $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2018,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
<commit_msg>Adds STR entry and exit functions to support NVDIMM<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/nvdimm_utils.H $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2018,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file nvdimm_utils.H
/// @brief Subroutines to support nvdimm backup/restore process
///
// *HWP HWP Owner: Tsung Yeung <tyeung@us.ibm.com>
// *HWP HWP Backup: Stephen Glancy <sglancy@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 3
// *HWP Consumed by: FSP:HB

#include <fapi2.H>
#include <generic/memory/lib/utils/find.H>

namespace mss
{

namespace nvdimm
{

///
/// @brief Helper for self_refresh_exit().
/// @tparam T the target type associated with this subroutine
/// @param[in] i_target the target associated with this subroutine
/// @return FAPI2_RC_SUCCESS iff setup was successful
/// @note Uses memdiag to read the port to force CKE back to high.
/// Stolen from mss_lab_memdiags.C
///
template< fapi2::TargetType T >
fapi2::ReturnCode self_refresh_exit_helper( const fapi2::Target<T>& i_target );

///
/// @brief Disable refresh and put target into self-refresh
/// @tparam T the target type associated with this subroutine
/// @param[in] i_target the target associated with this subroutine
/// @return FAPI2_RC_SUCCESS iff setup was successful
///
template< fapi2::TargetType T >
fapi2::ReturnCode self_refresh_entry( const fapi2::Target<T>& i_target );

///
/// @brief Take the target out of self-refresh and restart refresh
/// @tparam T the target type associated with this subroutine
/// @param[in] i_target the target associated with this subroutine
/// @return FAPI2_RC_SUCCESS iff setup was successful
///
template< fapi2::TargetType T >
fapi2::ReturnCode self_refresh_exit( const fapi2::Target<T>& i_target );

///
/// @brief Latch write vref at per-dram basis
/// @tparam T the target type associated with this subroutine
/// @param[in] i_target the target associated with this subroutine
/// @return FAPI2_RC_SUCCESS iff setup was successful
///

template< fapi2::TargetType T >
fapi2::ReturnCode pda_vref_latch( const fapi2::Target<T>& i_target );

///
/// @brief Full post-restore transition for NVDIMM
/// @tparam T the target type associated with this subroutine
/// @param[in] i_target the target associated with this subroutine
/// @return FAPI2_RC_SUCCESS iff setup was successful
///

template< fapi2::TargetType T >
fapi2::ReturnCode post_restore_transition( const fapi2::Target<T>& i_target );

}//ns nvdimm

}//ns mss
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtLocation module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qdeclarativeresultmodelbase_p.h"

QDeclarativeResultModelBase::QDeclarativeResultModelBase(QObject *parent)
:   QDeclarativeSearchModelBase(parent)
{
    QHash<int, QByteArray> roles = roleNames();
    roles.insert(DistanceRole, "distance");
    roles.insert(PlaceRole, "place");
    setRoleNames(roles);
}

QDeclarativeGeoServiceProvider* QDeclarativeResultModelBase::favoritesPlugin() const
{
    return m_favoritesPlugin;
}

void QDeclarativeResultModelBase::setFavoritesPlugin(QDeclarativeGeoServiceProvider *plugin)
{

    if (m_favoritesPlugin == plugin)
        return;

    m_favoritesPlugin = plugin;
    emit favoritesPluginChanged();
}

QVariantMap QDeclarativeResultModelBase::favoritesMatchParameters() const
{
    return m_matchParameters;
}

void QDeclarativeResultModelBase::setFavoritesMatchParameters(const QVariantMap &parameters)
{
    if (m_matchParameters == parameters)
        return;

    m_matchParameters = parameters;
    emit favoritesMatchParametersChanged();
}

void QDeclarativeResultModelBase::clearData()
{
    qDeleteAll(m_places);
    m_places.clear();
    m_results.clear();
}

int QDeclarativeResultModelBase::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    return m_results.count();
}

QVariant QDeclarativeResultModelBase::data(int index, const QString &role) const
{
    QModelIndex modelIndex = createIndex(index, 0);
    return data(modelIndex, roleNames().key(role.toLatin1()));
}

QVariant QDeclarativeResultModelBase::data(const QModelIndex &index, int role) const
{
    if (index.row() > m_results.count())
        return QVariant();

    const QPlaceSearchResult &result = m_results.at(index.row());

    switch (role) {
    case Qt::DisplayRole:
        if (result.type() == QPlaceSearchResult::PlaceResult)
            return m_places.at(index.row())->name();
        else
            return QVariant();
    case DistanceRole:
        return result.distance();
    case PlaceRole:
        return QVariant::fromValue(static_cast<QObject *>(m_places.at(index.row())));
    default:
        return QVariant();
    }
}

void QDeclarativeResultModelBase::queryFinished()
{
    if (!m_reply)
        return;
    QPlaceReply *reply = m_reply;
    m_reply = 0;
    if (reply->error() != QPlaceReply::NoError) {
        m_resultsBuffer.clear();
        updateLayout();
        setStatus(Error, reply->errorString());
        reply->deleteLater();
    }

    if (reply->type() == QPlaceReply::SearchReply) {
        QPlaceSearchReply *searchReply = qobject_cast<QPlaceSearchReply *>(reply);
        Q_ASSERT(searchReply);

        m_resultsBuffer = searchReply->results();
        reply->deleteLater();

        if (!m_favoritesPlugin) {
            updateLayout();
            setStatus(Ready);
        } else {
            QGeoServiceProvider *serviceProvider = m_favoritesPlugin->sharedGeoServiceProvider();
            if (!serviceProvider) {
                updateLayout();
                setStatus(Error, QLatin1String("Favorites plugin returns a null QGeoServiceProvider instance"));
            }

            QPlaceManager *favoritesManager = serviceProvider->placeManager();
            if (!favoritesManager) {
                updateLayout();
                setStatus(Error, QLatin1String("Favorites plugin returns a null QPlaceManager"));
            }

            QPlaceMatchRequest request;
            if (m_matchParameters.isEmpty()) {
                if (!m_plugin) {
                    reply->deleteLater();
                    setStatus(Error, QLatin1String("Plugin not assigned"));
                    return;
                }

                QVariantMap params;
                params.insert(QPlaceMatchRequest::AlternativeId, QString::fromLatin1("x_id_") + m_plugin->name());
                request.setParameters(params);
            } else {
                request.setParameters(m_matchParameters);
            }

            request.setResults(m_resultsBuffer);
            m_reply = favoritesManager->matchingPlaces(request);
            connect(m_reply, SIGNAL(finished()), this, SLOT(queryFinished()));
        }
    } else if (reply->type() == QPlaceReply::MatchReply){
        QPlaceMatchReply *matchReply = qobject_cast<QPlaceMatchReply *>(reply);
        Q_ASSERT(matchReply);
        updateLayout(matchReply->places());
        setStatus(Ready);
        reply->deleteLater();
    } else {
        setStatus(Error, QLatin1String("Unknown reply type"));
        reply->deleteLater();
    }
}

//Note: m_results buffer should be correctly populated before
//calling this function
void QDeclarativeResultModelBase::updateLayout(const QList<QPlace> &favoritePlaces)
{
    int oldRowCount = rowCount();

    beginResetModel();
    clearData();
    m_results = m_resultsBuffer;
    m_resultsBuffer.clear();

    for (int i=0 ;i < m_results.count(); ++i) {
        QDeclarativePlace *place = new QDeclarativePlace(m_results.at(i).place(),plugin(), this);
        m_places.append(place);

        if ((favoritePlaces.count() == m_results.count()) && favoritePlaces.at(i) != QPlace())
            m_places[i]->setFavorite(new QDeclarativePlace(favoritePlaces.at(i), m_favoritesPlugin, m_places[i]));
    }

    endResetModel();
    if (m_results.count() != oldRowCount)
        emit rowCountChanged();
}

<commit_msg>Fix crash due to uninitialised data member.<commit_after>/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtLocation module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qdeclarativeresultmodelbase_p.h"

QDeclarativeResultModelBase::QDeclarativeResultModelBase(QObject *parent)
:   QDeclarativeSearchModelBase(parent), m_favoritesPlugin(0)
{
    QHash<int, QByteArray> roles = roleNames();
    roles.insert(DistanceRole, "distance");
    roles.insert(PlaceRole, "place");
    setRoleNames(roles);
}

QDeclarativeGeoServiceProvider* QDeclarativeResultModelBase::favoritesPlugin() const
{
    return m_favoritesPlugin;
}

void QDeclarativeResultModelBase::setFavoritesPlugin(QDeclarativeGeoServiceProvider *plugin)
{

    if (m_favoritesPlugin == plugin)
        return;

    m_favoritesPlugin = plugin;
    emit favoritesPluginChanged();
}

QVariantMap QDeclarativeResultModelBase::favoritesMatchParameters() const
{
    return m_matchParameters;
}

void QDeclarativeResultModelBase::setFavoritesMatchParameters(const QVariantMap &parameters)
{
    if (m_matchParameters == parameters)
        return;

    m_matchParameters = parameters;
    emit favoritesMatchParametersChanged();
}

void QDeclarativeResultModelBase::clearData()
{
    qDeleteAll(m_places);
    m_places.clear();
    m_results.clear();
}

int QDeclarativeResultModelBase::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    return m_results.count();
}

QVariant QDeclarativeResultModelBase::data(int index, const QString &role) const
{
    QModelIndex modelIndex = createIndex(index, 0);
    return data(modelIndex, roleNames().key(role.toLatin1()));
}

QVariant QDeclarativeResultModelBase::data(const QModelIndex &index, int role) const
{
    if (index.row() > m_results.count())
        return QVariant();

    const QPlaceSearchResult &result = m_results.at(index.row());

    switch (role) {
    case Qt::DisplayRole:
        if (result.type() == QPlaceSearchResult::PlaceResult)
            return m_places.at(index.row())->name();
        else
            return QVariant();
    case DistanceRole:
        return result.distance();
    case PlaceRole:
        return QVariant::fromValue(static_cast<QObject *>(m_places.at(index.row())));
    default:
        return QVariant();
    }
}

void QDeclarativeResultModelBase::queryFinished()
{
    if (!m_reply)
        return;
    QPlaceReply *reply = m_reply;
    m_reply = 0;
    if (reply->error() != QPlaceReply::NoError) {
        m_resultsBuffer.clear();
        updateLayout();
        setStatus(Error, reply->errorString());
        reply->deleteLater();
    }

    if (reply->type() == QPlaceReply::SearchReply) {
        QPlaceSearchReply *searchReply = qobject_cast<QPlaceSearchReply *>(reply);
        Q_ASSERT(searchReply);

        m_resultsBuffer = searchReply->results();
        reply->deleteLater();

        if (!m_favoritesPlugin) {
            updateLayout();
            setStatus(Ready);
        } else {
            QGeoServiceProvider *serviceProvider = m_favoritesPlugin->sharedGeoServiceProvider();
            if (!serviceProvider) {
                updateLayout();
                setStatus(Error, QLatin1String("Favorites plugin returns a null QGeoServiceProvider instance"));
            }

            QPlaceManager *favoritesManager = serviceProvider->placeManager();
            if (!favoritesManager) {
                updateLayout();
                setStatus(Error, QLatin1String("Favorites plugin returns a null QPlaceManager"));
            }

            QPlaceMatchRequest request;
            if (m_matchParameters.isEmpty()) {
                if (!m_plugin) {
                    reply->deleteLater();
                    setStatus(Error, QLatin1String("Plugin not assigned"));
                    return;
                }

                QVariantMap params;
                params.insert(QPlaceMatchRequest::AlternativeId, QString::fromLatin1("x_id_") + m_plugin->name());
                request.setParameters(params);
            } else {
                request.setParameters(m_matchParameters);
            }

            request.setResults(m_resultsBuffer);
            m_reply = favoritesManager->matchingPlaces(request);
            connect(m_reply, SIGNAL(finished()), this, SLOT(queryFinished()));
        }
    } else if (reply->type() == QPlaceReply::MatchReply){
        QPlaceMatchReply *matchReply = qobject_cast<QPlaceMatchReply *>(reply);
        Q_ASSERT(matchReply);
        updateLayout(matchReply->places());
        setStatus(Ready);
        reply->deleteLater();
    } else {
        setStatus(Error, QLatin1String("Unknown reply type"));
        reply->deleteLater();
    }
}

//Note: m_results buffer should be correctly populated before
//calling this function
void QDeclarativeResultModelBase::updateLayout(const QList<QPlace> &favoritePlaces)
{
    int oldRowCount = rowCount();

    beginResetModel();
    clearData();
    m_results = m_resultsBuffer;
    m_resultsBuffer.clear();

    for (int i=0 ;i < m_results.count(); ++i) {
        QDeclarativePlace *place = new QDeclarativePlace(m_results.at(i).place(),plugin(), this);
        m_places.append(place);

        if ((favoritePlaces.count() == m_results.count()) && favoritePlaces.at(i) != QPlace())
            m_places[i]->setFavorite(new QDeclarativePlace(favoritePlaces.at(i), m_favoritesPlugin, m_places[i]));
    }

    endResetModel();
    if (m_results.count() != oldRowCount)
        emit rowCountChanged();
}

<|endoftext|>


<commit_before>/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Symbian Foundation License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.symbianfoundation.org/legal/sfl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/

#include "testconfiguration.h"
#include <stdio.h>

// XML configuration elements
const char setupElement[] = "setup";
const char contactElement[] = "contact";
const char testElement[] = "test";
const char logElement[] = "log";

// XML configuration attriputes
const char nameAttr[] = "name";
const char saveAttr[] = "save";
const char typeAttr[] = "type";
const char loggingAttr[] = "logging";

// XML configuration values
const char valueYes[] = "yes";
const char valueNo[] = "no";
const char valuePerformanceTest[] = "0";
const char valueNormalTest[] = "1";

// -----------------------------------------------------------------------------
// TestConfiguration::TestConfiguration
// -----------------------------------------------------------------------------
//
TestConfiguration::TestConfiguration()
: mSaveContact(true),
  mPerformanceTest(true),
  mLogToFile(false)
{
}

// -----------------------------------------------------------------------------
// TestConfiguration::TestConfiguration
// -----------------------------------------------------------------------------
//
TestConfiguration::~TestConfiguration()
{
}

// -----------------------------------------------------------------------------
// TestResultXmlParser::parse
// -----------------------------------------------------------------------------
//
int TestConfiguration::parse(const QString& fileName)
{
    QFile file(fileName);
    QXmlInputSource inputSource(&file);
    QXmlSimpleReader reader;
    reader.setContentHandler(this);
    return reader.parse(inputSource);
}

// -----------------------------------------------------------------------------
// TestConfiguration::startElement
// -----------------------------------------------------------------------------
//
bool TestConfiguration::startElement(
    const QString& /*namespaceURI*/,
    const QString& /*localName*/,
    const QString& qName,
    const QXmlAttributes& atts)
{
    if( qName == setupElement) {
        mTestName = atts.value(nameAttr);
    }
    else if (qName == contactElement) {
        mSaveContact = atts.value(saveAttr).contains(valueYes,
                                                     Qt::CaseInsensitive);
    }
    else if (qName == testElement) {
        mPerformanceTest = atts.value(typeAttr).contains(valuePerformanceTest,
                                                         Qt::CaseInsensitive);
    }
    else if (qName == logElement) {
        mLogToFile = atts.value(loggingAttr).contains(valueYes,
                                                      Qt::CaseInsensitive);
    }
    return true;
}

// End of File.
<commit_msg>Fix for arm compilation warnings<commit_after>/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Symbian Foundation License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.symbianfoundation.org/legal/sfl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/

#include "testconfiguration.h"
#include <stdio.h>

// XML configuration elements
const char setupElement[] = "setup";
const char contactElement[] = "contact";
const char testElement[] = "test";
const char logElement[] = "log";

// XML configuration attriputes
const char nameAttr[] = "name";
const char saveAttr[] = "save";
const char typeAttr[] = "type";
const char loggingAttr[] = "logging";

// XML configuration values
const char valueYes[] = "yes";
const char valuePerformanceTest[] = "0";

// -----------------------------------------------------------------------------
// TestConfiguration::TestConfiguration
// -----------------------------------------------------------------------------
//
TestConfiguration::TestConfiguration()
: mSaveContact(true),
  mPerformanceTest(true),
  mLogToFile(false)
{
}

// -----------------------------------------------------------------------------
// TestConfiguration::TestConfiguration
// -----------------------------------------------------------------------------
//
TestConfiguration::~TestConfiguration()
{
}

// -----------------------------------------------------------------------------
// TestResultXmlParser::parse
// -----------------------------------------------------------------------------
//
int TestConfiguration::parse(const QString& fileName)
{
    QFile file(fileName);
    QXmlInputSource inputSource(&file);
    QXmlSimpleReader reader;
    reader.setContentHandler(this);
    return reader.parse(inputSource);
}

// -----------------------------------------------------------------------------
// TestConfiguration::startElement
// -----------------------------------------------------------------------------
//
bool TestConfiguration::startElement(
    const QString& /*namespaceURI*/,
    const QString& /*localName*/,
    const QString& qName,
    const QXmlAttributes& atts)
{
    if( qName == setupElement) {
        mTestName = atts.value(nameAttr);
    }
    else if (qName == contactElement) {
        mSaveContact = atts.value(saveAttr).contains(valueYes,
                                                     Qt::CaseInsensitive);
    }
    else if (qName == testElement) {
        mPerformanceTest = atts.value(typeAttr).contains(valuePerformanceTest,
                                                         Qt::CaseInsensitive);
    }
    else if (qName == logElement) {
        mLogToFile = atts.value(loggingAttr).contains(valueYes,
                                                      Qt::CaseInsensitive);
    }
    return true;
}

// End of File.
<|endoftext|>


<commit_before>#include <sys/errno.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <unistd.h>

#include <common/buffer.h>
#include <common/limits.h>

#include <event/action.h>
#include <event/callback.h>
#include <event/event_system.h>

#include <io/io_system.h>

#define	IO_READ_BUFFER_SIZE	65536

IOSystem::Handle::Handle(int fd, Channel *owner)
: log_("/io/system/handle"),
  fd_(fd),
  owner_(owner),
  close_callback_(NULL),
  close_action_(NULL),
  read_amount_(0),
  read_buffer_(),
  read_callback_(NULL),
  read_action_(NULL),
  write_buffer_(),
  write_callback_(NULL),
  write_action_(NULL)
{ }

IOSystem::Handle::~Handle()
{
	ASSERT(fd_ == -1);

	ASSERT(close_action_ == NULL);
	ASSERT(close_callback_ == NULL);

	ASSERT(read_action_ == NULL);
	ASSERT(read_callback_ == NULL);

	ASSERT(write_action_ == NULL);
	ASSERT(write_callback_ == NULL);
}

void
IOSystem::Handle::close_callback(void)
{
	close_action_->cancel();
	close_action_ = NULL;

	ASSERT(fd_ != -1);
	int rv = ::close(fd_);
	if (rv == -1) {
		switch (errno) {
		case EAGAIN:
			close_action_ = close_schedule();
			break;
		default:
			close_callback_->event(Event(Event::Error, errno));
			Action *a = EventSystem::instance()->schedule(close_callback_);
			close_action_ = a;
			close_callback_ = NULL;
			break;
		}
		return;
	}
	fd_ = -1;
	close_callback_->event(Event(Event::Done, 0));
	Action *a = EventSystem::instance()->schedule(close_callback_);
	close_action_ = a;
	close_callback_ = NULL;
}

void
IOSystem::Handle::close_cancel(void)
{
	ASSERT(close_action_ != NULL);
	close_action_->cancel();
	close_action_ = NULL;

	if (close_callback_ != NULL) {
		delete close_callback_;
		close_callback_ = NULL;
	}
}

Action *
IOSystem::Handle::close_schedule(void)
{
	ASSERT(close_action_ == NULL);
	Callback *cb = callback(this, &IOSystem::Handle::close_callback);
	Action *a = EventSystem::instance()->schedule(cb);
	return (a);
}

void
IOSystem::Handle::read_callback(Event e)
{
	read_action_->cancel();
	read_action_ = NULL;

	switch (e.type_) {
	case Event::EOS:
	case Event::Done:
		break;
	case Event::Error: {
		DEBUG(log_) << "Poll returned error: " << e;
		read_callback_->event(e);
		Action *a = EventSystem::instance()->schedule(read_callback_);
		read_action_ = a;
		read_callback_ = NULL;
		return;
	}
	default:
		HALT(log_) << "Unexpected event: " << e;
	}

	size_t rlen;

	if (read_amount_ == 0)
		rlen = IO_READ_BUFFER_SIZE;
	else {
		rlen = read_amount_ - read_buffer_.length();
		ASSERT(rlen != 0);
		if (rlen > IO_READ_BUFFER_SIZE)
			rlen = IO_READ_BUFFER_SIZE;
	}

	/*
	 * A bit of discussion is warranted on this:
	 *
	 * In tack, IOV_MAX BufferSegments are allocated and read in to with
	 * readv(2), and then the lengths are adjusted and the ones that are
	 * empty are freed.  It's also possible to set the expected lengths
	 * first (and only allocate
	 * 	roundup(rlen, BUFFER_SEGMENT_SIZE) / BUFFER_SEGMENT_SIZE
	 * BufferSegments, though really IOV_MAX (or some chosen number) seems
	 * a bit better since most of our reads right now are read_amount_==0)
	 * and put them into a Buffer and trim the leftovers, which is a bit
	 * nicer.
	 *
	 * Since our read_amount_ is usually 0, though, we're kind of at the
	 * mercy chance (well, not really) as to how much data we will read,
	 * which means a sizable amount of thrashing of memory; allocating and
	 * freeing BufferSegments.
	 *
	 * By comparison, stack space is cheap in userland and allocating 64K
	 * of it here is pretty painless.  Reading to it is fast and then
	 * copying only what we need into BufferSegments isn't very costly.
	 * Indeed, since readv can't sparsely-populate each data pointer, it
	 * has to do some data shuffling, already.  Benchmarking would be a
	 * good idea, but it seems like there are arguments both ways.  Of
	 * course, tack is very fast and this code path hasn't been thrashed
	 * half so much.  When tack is adjusted to use the IO system and Pipes
	 * in the future, if performance degradation is noticeable, perhaps
	 * it will worth it to switch.  For now, absence any idea of the real
	 * read sizes in the wild, doing nothing and not thrashing memory is
	 * decidedly more appealing.
	 */
	uint8_t data[IO_READ_BUFFER_SIZE];
	ssize_t len = ::read(fd_, data, sizeof data);
	if (len == -1) {
		switch (errno) {
		case EAGAIN:
			read_action_ = read_schedule();
			break;
		default:
			read_callback_->event(Event(Event::Error, errno, read_buffer_));
			Action *a = EventSystem::instance()->schedule(read_callback_);
			read_action_ = a;
			read_callback_ = NULL;
			read_buffer_.clear();
			read_amount_ = 0;
			break;
		}
		return;
	}

	/*
	 * XXX
	 * If we get an EOS from EventPoll, should we just terminate after that
	 * one read?  How can we tell how much data is still available to be
	 * read?  Will we get it all in one read?  Eventually, one would expect
	 * to get a read with a length of 0, or an error, but will kevent even
	 * continue to fire off read events?
	 */
	if (len == 0) {
		read_callback_->event(Event(Event::EOS, 0, read_buffer_));
		Action *a = EventSystem::instance()->schedule(read_callback_);
		read_action_ = a;
		read_callback_ = NULL;
		read_buffer_.clear();
		read_amount_ = 0;
		return;
	}

	read_buffer_.append(data, len);
	read_action_ = read_schedule();
}

void
IOSystem::Handle::read_cancel(void)
{
	ASSERT(read_action_ != NULL);
	read_action_->cancel();
	read_action_ = NULL;

	if (read_callback_ != NULL) {
		delete read_callback_;
		read_callback_ = NULL;
	}
}

Action *
IOSystem::Handle::read_schedule(void)
{
	ASSERT(read_action_ == NULL);

	if (!read_buffer_.empty() && read_buffer_.length() >= read_amount_) {
		if (read_amount_ == 0)
			read_amount_ = read_buffer_.length();
		read_callback_->event(Event(Event::Done, 0, Buffer(read_buffer_, read_amount_)));
		Action *a = EventSystem::instance()->schedule(read_callback_);
		read_callback_ = NULL;
		read_buffer_.skip(read_amount_);
		read_amount_ = 0;
		return (a);
	}

	EventCallback *cb = callback(this, &IOSystem::Handle::read_callback);
	Action *a = EventSystem::instance()->poll(EventPoll::Readable, fd_, cb);
	return (a);
}

void
IOSystem::Handle::write_callback(Event e)
{
	write_action_->cancel();
	write_action_ = NULL;

	switch (e.type_) {
	case Event::Done:
		break;
	case Event::Error: {
		DEBUG(log_) << "Poll returned error: " << e;
		write_callback_->event(e);
		Action *a = EventSystem::instance()->schedule(write_callback_);
		write_action_ = a;
		write_callback_ = NULL;
		return;
	}
	default:
		HALT(log_) << "Unexpected event: " << e;
	}

	/* XXX This doesn't handle UDP nicely.  Right?  */
	/* XXX If a UDP packet is > IOV_MAX segments, this will break it.  */
	struct iovec iov[IOV_MAX];
	size_t iovcnt = write_buffer_.fill_iovec(iov, IOV_MAX);

	ssize_t len = ::writev(fd_, iov, iovcnt);
	if (len == -1) {
		switch (errno) {
		case EAGAIN:
			write_action_ = write_schedule();
			break;
		default:
			write_callback_->event(Event(Event::Error, errno));
			Action *a = EventSystem::instance()->schedule(write_callback_);
			write_action_ = a;
			write_callback_ = NULL;
			break;
		}
		return;
	}

	write_buffer_.skip(len);

	if (write_buffer_.empty()) {
		write_callback_->event(Event(Event::Done, 0));
		Action *a = EventSystem::instance()->schedule(write_callback_);
		write_action_ = a;
		write_callback_ = NULL;
		return;
	}
	write_action_ = write_schedule();
}

void
IOSystem::Handle::write_cancel(void)
{
	ASSERT(write_action_ != NULL);
	write_action_->cancel();
	write_action_ = NULL;

	if (write_callback_ != NULL) {
		delete write_callback_;
		write_callback_ = NULL;
	}
}

Action *
IOSystem::Handle::write_schedule(void)
{
	ASSERT(write_action_ == NULL);
	EventCallback *cb = callback(this, &IOSystem::Handle::write_callback);
	Action *a = EventSystem::instance()->poll(EventPoll::Writable, fd_, cb);
	return (a);
}

IOSystem::IOSystem(void)
: log_("/io/system"),
  handle_map_()
{
	/*
	 * Prepare system to handle IO.
	 */
	INFO(log_) << "Starting IO system.";

	/*
	 * Disable SIGPIPE.
	 *
	 * Because errors are returned asynchronously and may occur at any time,
	 * there may be a pending write to a file descriptor which has
	 * previously thrown an error.  There are changes that could be made to
	 * the scheduler to work around this, but they are not desirable.
	 */
	if (::signal(SIGPIPE, SIG_IGN) == SIG_ERR)
		HALT(log_) << "Could not disable SIGPIPE.";

	/*
	 * Up the file descriptor limit.
	 *
	 * Probably this should be configurable, but there's no harm on modern
	 * systems and for the performance-critical applications using the IO
	 * system, more file descriptors is better.
	 */
	struct rlimit rlim;
	int rv = ::getrlimit(RLIMIT_NOFILE, &rlim);
	if (rv == 0) {
		if (rlim.rlim_cur < rlim.rlim_max) {
			rlim.rlim_cur = rlim.rlim_max;

			rv = ::setrlimit(RLIMIT_NOFILE, &rlim);
			if (rv == -1) {
				INFO(log_) << "Unable to increase file descriptor limit.";
			}
		}
	} else {
		INFO(log_) << "Unable to get file descriptor limit.";
	}
}

IOSystem::~IOSystem()
{
	ASSERT(handle_map_.empty());
}

void
IOSystem::attach(int fd, Channel *owner)
{
	ASSERT(handle_map_.find(handle_key_t(fd, owner)) == handle_map_.end());
	handle_map_[handle_key_t(fd, owner)] = new IOSystem::Handle(fd, owner);
}

void
IOSystem::detach(int fd, Channel *owner)
{
	handle_map_t::iterator it;
	IOSystem::Handle *h;

	it = handle_map_.find(handle_key_t(fd, owner));
	ASSERT(it != handle_map_.end());

	h = it->second;
	ASSERT(h != NULL);
	ASSERT(h->owner_ == owner);

	handle_map_.erase(it);
	delete h;
}

Action *
IOSystem::close(int fd, Channel *owner, EventCallback *cb)
{
	IOSystem::Handle *h;

	h = handle_map_[handle_key_t(fd, owner)];
	ASSERT(h != NULL);

	ASSERT(h->close_callback_ == NULL);
	ASSERT(h->close_action_ == NULL);

	ASSERT(h->read_callback_ == NULL);
	ASSERT(h->read_action_ == NULL);

	ASSERT(h->write_callback_ == NULL);
	ASSERT(h->write_action_ == NULL);

	ASSERT(h->fd_ != -1);

	h->close_callback_ = cb;
	h->close_action_ = h->close_schedule();
	return (cancellation(h, &IOSystem::Handle::close_cancel));
}

Action *
IOSystem::read(int fd, Channel *owner, size_t amount, EventCallback *cb)
{
	IOSystem::Handle *h;

	h = handle_map_[handle_key_t(fd, owner)];
	ASSERT(h != NULL);

	ASSERT(h->read_callback_ == NULL);
	ASSERT(h->read_action_ == NULL);

	h->read_amount_ = amount;
	h->read_callback_ = cb;
	h->read_action_ = h->read_schedule();
	return (cancellation(h, &IOSystem::Handle::read_cancel));
}

Action *
IOSystem::write(int fd, Channel *owner, Buffer *buffer, EventCallback *cb)
{
	IOSystem::Handle *h;

	h = handle_map_[handle_key_t(fd, owner)];
	ASSERT(h != NULL);

	ASSERT(h->write_callback_ == NULL);
	ASSERT(h->write_action_ == NULL);
	ASSERT(h->write_buffer_.empty());

	ASSERT(!buffer->empty());
	h->write_buffer_.append(buffer);
	buffer->clear();

	h->write_callback_ = cb;
	h->write_action_ = h->write_schedule();
	return (cancellation(h, &IOSystem::Handle::write_cancel));
}
<commit_msg>Reword a comment.<commit_after>#include <sys/errno.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <unistd.h>

#include <common/buffer.h>
#include <common/limits.h>

#include <event/action.h>
#include <event/callback.h>
#include <event/event_system.h>

#include <io/io_system.h>

#define	IO_READ_BUFFER_SIZE	65536

IOSystem::Handle::Handle(int fd, Channel *owner)
: log_("/io/system/handle"),
  fd_(fd),
  owner_(owner),
  close_callback_(NULL),
  close_action_(NULL),
  read_amount_(0),
  read_buffer_(),
  read_callback_(NULL),
  read_action_(NULL),
  write_buffer_(),
  write_callback_(NULL),
  write_action_(NULL)
{ }

IOSystem::Handle::~Handle()
{
	ASSERT(fd_ == -1);

	ASSERT(close_action_ == NULL);
	ASSERT(close_callback_ == NULL);

	ASSERT(read_action_ == NULL);
	ASSERT(read_callback_ == NULL);

	ASSERT(write_action_ == NULL);
	ASSERT(write_callback_ == NULL);
}

void
IOSystem::Handle::close_callback(void)
{
	close_action_->cancel();
	close_action_ = NULL;

	ASSERT(fd_ != -1);
	int rv = ::close(fd_);
	if (rv == -1) {
		switch (errno) {
		case EAGAIN:
			close_action_ = close_schedule();
			break;
		default:
			close_callback_->event(Event(Event::Error, errno));
			Action *a = EventSystem::instance()->schedule(close_callback_);
			close_action_ = a;
			close_callback_ = NULL;
			break;
		}
		return;
	}
	fd_ = -1;
	close_callback_->event(Event(Event::Done, 0));
	Action *a = EventSystem::instance()->schedule(close_callback_);
	close_action_ = a;
	close_callback_ = NULL;
}

void
IOSystem::Handle::close_cancel(void)
{
	ASSERT(close_action_ != NULL);
	close_action_->cancel();
	close_action_ = NULL;

	if (close_callback_ != NULL) {
		delete close_callback_;
		close_callback_ = NULL;
	}
}

Action *
IOSystem::Handle::close_schedule(void)
{
	ASSERT(close_action_ == NULL);
	Callback *cb = callback(this, &IOSystem::Handle::close_callback);
	Action *a = EventSystem::instance()->schedule(cb);
	return (a);
}

void
IOSystem::Handle::read_callback(Event e)
{
	read_action_->cancel();
	read_action_ = NULL;

	switch (e.type_) {
	case Event::EOS:
	case Event::Done:
		break;
	case Event::Error: {
		DEBUG(log_) << "Poll returned error: " << e;
		read_callback_->event(e);
		Action *a = EventSystem::instance()->schedule(read_callback_);
		read_action_ = a;
		read_callback_ = NULL;
		return;
	}
	default:
		HALT(log_) << "Unexpected event: " << e;
	}

	size_t rlen;

	if (read_amount_ == 0)
		rlen = IO_READ_BUFFER_SIZE;
	else {
		rlen = read_amount_ - read_buffer_.length();
		ASSERT(rlen != 0);
		if (rlen > IO_READ_BUFFER_SIZE)
			rlen = IO_READ_BUFFER_SIZE;
	}

	/*
	 * A bit of discussion is warranted on this:
	 *
	 * In tack, IOV_MAX BufferSegments are allocated and read in to with
	 * readv(2), and then the lengths are adjusted and the ones that are
	 * empty are freed.  It's also possible to set the expected lengths
	 * first (and only allocate
	 * 	roundup(rlen, BUFFER_SEGMENT_SIZE) / BUFFER_SEGMENT_SIZE
	 * BufferSegments, though really IOV_MAX (or some chosen number) seems
	 * a bit better since most of our reads right now are read_amount_==0)
	 * and put them into a Buffer and trim the leftovers, which is a bit
	 * nicer.
	 *
	 * Since our read_amount_ is usually 0, though, we're kind of at the
	 * mercy chance (well, not really) as to how much data we will read,
	 * which means a sizable amount of thrashing of memory; allocating and
	 * freeing BufferSegments.
	 *
	 * By comparison, stack space is cheap in userland and allocating 64K
	 * of it here is pretty painless.  Reading to it is fast and then
	 * copying only what we need into BufferSegments isn't very costly.
	 * Indeed, since readv can't sparsely-populate each data pointer, it
	 * has to do some data shuffling, already.  Benchmarking would be a
	 * good idea, but it seems like there are arguments both ways.  Of
	 * course, tack is very fast and this code path hasn't been thrashed
	 * half so much.  When tack is adjusted to use the IO system and Pipes
	 * in the future, if performance degradation is noticeable, perhaps
	 * it will worth it to switch.  For now, absence any idea of the real
	 * read sizes in the wild, doing nothing and not thrashing memory is
	 * decidedly more appealing.
	 */
	uint8_t data[IO_READ_BUFFER_SIZE];
	ssize_t len = ::read(fd_, data, sizeof data);
	if (len == -1) {
		switch (errno) {
		case EAGAIN:
			read_action_ = read_schedule();
			break;
		default:
			read_callback_->event(Event(Event::Error, errno, read_buffer_));
			Action *a = EventSystem::instance()->schedule(read_callback_);
			read_action_ = a;
			read_callback_ = NULL;
			read_buffer_.clear();
			read_amount_ = 0;
			break;
		}
		return;
	}

	/*
	 * XXX
	 * If we get a short read from readv and detected EOS from EventPoll is
	 * that good enough, instead?  We can keep reading until we get a 0, sure,
	 * but if things other than network conditions influence whether reads
	 * would block (and whether non-blocking reads return), there could be
	 * more data waiting, and so we shouldn't just use a short read as an
	 * indicator?
	 */
	if (len == 0) {
		read_callback_->event(Event(Event::EOS, 0, read_buffer_));
		Action *a = EventSystem::instance()->schedule(read_callback_);
		read_action_ = a;
		read_callback_ = NULL;
		read_buffer_.clear();
		read_amount_ = 0;
		return;
	}

	read_buffer_.append(data, len);
	read_action_ = read_schedule();
}

void
IOSystem::Handle::read_cancel(void)
{
	ASSERT(read_action_ != NULL);
	read_action_->cancel();
	read_action_ = NULL;

	if (read_callback_ != NULL) {
		delete read_callback_;
		read_callback_ = NULL;
	}
}

Action *
IOSystem::Handle::read_schedule(void)
{
	ASSERT(read_action_ == NULL);

	if (!read_buffer_.empty() && read_buffer_.length() >= read_amount_) {
		if (read_amount_ == 0)
			read_amount_ = read_buffer_.length();
		read_callback_->event(Event(Event::Done, 0, Buffer(read_buffer_, read_amount_)));
		Action *a = EventSystem::instance()->schedule(read_callback_);
		read_callback_ = NULL;
		read_buffer_.skip(read_amount_);
		read_amount_ = 0;
		return (a);
	}

	EventCallback *cb = callback(this, &IOSystem::Handle::read_callback);
	Action *a = EventSystem::instance()->poll(EventPoll::Readable, fd_, cb);
	return (a);
}

void
IOSystem::Handle::write_callback(Event e)
{
	write_action_->cancel();
	write_action_ = NULL;

	switch (e.type_) {
	case Event::Done:
		break;
	case Event::Error: {
		DEBUG(log_) << "Poll returned error: " << e;
		write_callback_->event(e);
		Action *a = EventSystem::instance()->schedule(write_callback_);
		write_action_ = a;
		write_callback_ = NULL;
		return;
	}
	default:
		HALT(log_) << "Unexpected event: " << e;
	}

	/* XXX This doesn't handle UDP nicely.  Right?  */
	/* XXX If a UDP packet is > IOV_MAX segments, this will break it.  */
	struct iovec iov[IOV_MAX];
	size_t iovcnt = write_buffer_.fill_iovec(iov, IOV_MAX);

	ssize_t len = ::writev(fd_, iov, iovcnt);
	if (len == -1) {
		switch (errno) {
		case EAGAIN:
			write_action_ = write_schedule();
			break;
		default:
			write_callback_->event(Event(Event::Error, errno));
			Action *a = EventSystem::instance()->schedule(write_callback_);
			write_action_ = a;
			write_callback_ = NULL;
			break;
		}
		return;
	}

	write_buffer_.skip(len);

	if (write_buffer_.empty()) {
		write_callback_->event(Event(Event::Done, 0));
		Action *a = EventSystem::instance()->schedule(write_callback_);
		write_action_ = a;
		write_callback_ = NULL;
		return;
	}
	write_action_ = write_schedule();
}

void
IOSystem::Handle::write_cancel(void)
{
	ASSERT(write_action_ != NULL);
	write_action_->cancel();
	write_action_ = NULL;

	if (write_callback_ != NULL) {
		delete write_callback_;
		write_callback_ = NULL;
	}
}

Action *
IOSystem::Handle::write_schedule(void)
{
	ASSERT(write_action_ == NULL);
	EventCallback *cb = callback(this, &IOSystem::Handle::write_callback);
	Action *a = EventSystem::instance()->poll(EventPoll::Writable, fd_, cb);
	return (a);
}

IOSystem::IOSystem(void)
: log_("/io/system"),
  handle_map_()
{
	/*
	 * Prepare system to handle IO.
	 */
	INFO(log_) << "Starting IO system.";

	/*
	 * Disable SIGPIPE.
	 *
	 * Because errors are returned asynchronously and may occur at any time,
	 * there may be a pending write to a file descriptor which has
	 * previously thrown an error.  There are changes that could be made to
	 * the scheduler to work around this, but they are not desirable.
	 */
	if (::signal(SIGPIPE, SIG_IGN) == SIG_ERR)
		HALT(log_) << "Could not disable SIGPIPE.";

	/*
	 * Up the file descriptor limit.
	 *
	 * Probably this should be configurable, but there's no harm on modern
	 * systems and for the performance-critical applications using the IO
	 * system, more file descriptors is better.
	 */
	struct rlimit rlim;
	int rv = ::getrlimit(RLIMIT_NOFILE, &rlim);
	if (rv == 0) {
		if (rlim.rlim_cur < rlim.rlim_max) {
			rlim.rlim_cur = rlim.rlim_max;

			rv = ::setrlimit(RLIMIT_NOFILE, &rlim);
			if (rv == -1) {
				INFO(log_) << "Unable to increase file descriptor limit.";
			}
		}
	} else {
		INFO(log_) << "Unable to get file descriptor limit.";
	}
}

IOSystem::~IOSystem()
{
	ASSERT(handle_map_.empty());
}

void
IOSystem::attach(int fd, Channel *owner)
{
	ASSERT(handle_map_.find(handle_key_t(fd, owner)) == handle_map_.end());
	handle_map_[handle_key_t(fd, owner)] = new IOSystem::Handle(fd, owner);
}

void
IOSystem::detach(int fd, Channel *owner)
{
	handle_map_t::iterator it;
	IOSystem::Handle *h;

	it = handle_map_.find(handle_key_t(fd, owner));
	ASSERT(it != handle_map_.end());

	h = it->second;
	ASSERT(h != NULL);
	ASSERT(h->owner_ == owner);

	handle_map_.erase(it);
	delete h;
}

Action *
IOSystem::close(int fd, Channel *owner, EventCallback *cb)
{
	IOSystem::Handle *h;

	h = handle_map_[handle_key_t(fd, owner)];
	ASSERT(h != NULL);

	ASSERT(h->close_callback_ == NULL);
	ASSERT(h->close_action_ == NULL);

	ASSERT(h->read_callback_ == NULL);
	ASSERT(h->read_action_ == NULL);

	ASSERT(h->write_callback_ == NULL);
	ASSERT(h->write_action_ == NULL);

	ASSERT(h->fd_ != -1);

	h->close_callback_ = cb;
	h->close_action_ = h->close_schedule();
	return (cancellation(h, &IOSystem::Handle::close_cancel));
}

Action *
IOSystem::read(int fd, Channel *owner, size_t amount, EventCallback *cb)
{
	IOSystem::Handle *h;

	h = handle_map_[handle_key_t(fd, owner)];
	ASSERT(h != NULL);

	ASSERT(h->read_callback_ == NULL);
	ASSERT(h->read_action_ == NULL);

	h->read_amount_ = amount;
	h->read_callback_ = cb;
	h->read_action_ = h->read_schedule();
	return (cancellation(h, &IOSystem::Handle::read_cancel));
}

Action *
IOSystem::write(int fd, Channel *owner, Buffer *buffer, EventCallback *cb)
{
	IOSystem::Handle *h;

	h = handle_map_[handle_key_t(fd, owner)];
	ASSERT(h != NULL);

	ASSERT(h->write_callback_ == NULL);
	ASSERT(h->write_action_ == NULL);
	ASSERT(h->write_buffer_.empty());

	ASSERT(!buffer->empty());
	h->write_buffer_.append(buffer);
	buffer->clear();

	h->write_callback_ = cb;
	h->write_action_ = h->write_schedule();
	return (cancellation(h, &IOSystem::Handle::write_cancel));
}
<|endoftext|>


<commit_before>#include "layers/icp.h"

#include "pcl/registration/registration.h"
#include "pcl/registration/icp.h"

#include <Eigen/Dense>            ///< to enable OpenCV's eigen2cv()
#include <opencv2/core/eigen.hpp> ///< for eigen2cv(), preceeded be #include <Eigen/Dense>

#include "core/exception.h"
#include "core/layerconfig.h"
#include "core/pcl_utils.h"
#include "core/signal.h"
#include "core/stl.h"
#include "layers/layerfactory.h"
#include "ts/ts.h"
#include "ts/layer_assertions.h"

using namespace std;
using namespace cv;
using namespace pcl;
using namespace sem;

namespace {

#ifdef __WITH_PCL // test normally

const string NAME_INPUT_POINT_CLOUD_SRC     = "src";    ///< key to source cloud
const string NAME_INPUT_POINT_CLOUD_TARGET  = "target"; ///< key to target cloud

const string NAME_OUTPUT_SCORE              = "score";  ///< key to fitness score
const string NAME_OUTPUT_CONVERGENCE        = "c";      ///< key to convergence result
const string NAME_OUTPUT_TRANSFORMATION     = "transf"; ///< key to optional final tansformation

class ICPInitTest : public ::testing::Test
{
protected:
    virtual void SetUp()
    {
        cfg_ = LayerConfig();
        cfg_.Params(params_);
        io_names_ = LayerIONames();

        io_names_.Input(ICP::KEY_INPUT_POINT_CLOUD_SRC   , NAME_INPUT_POINT_CLOUD_SRC);
        io_names_.Input(ICP::KEY_INPUT_POINT_CLOUD_TARGET, NAME_INPUT_POINT_CLOUD_TARGET);
        io_names_.Output(ICP::KEY_OUTPUT_CONVERGENCE     , NAME_OUTPUT_CONVERGENCE);
        io_names_.Output(ICP::KEY_OUTPUT_SCORE           , NAME_OUTPUT_SCORE);
        io_names_.Output(ICP::KEY_OUTPUT_TRANSFORMATION  , NAME_OUTPUT_TRANSFORMATION);
    }

    virtual void TearDown()
    {
        params_.clear();
    }

    // members
    PTree params_;
    LayerConfig cfg_;
    LayerIONames io_names_;
};

TEST_F(ICPInitTest, Constructor)
{
    EXPECT_NO_THROW(ICP to);
    EXPECT_NO_THROW(ICP to(cfg_));
}

TEST_F(ICPInitTest, MissingParams)
{
    cfg_.Params(PTree());
    EXPECT_NO_THROW(ICP to(cfg_));

    ICP to;
    EXPECT_NO_THROW(to.Reset(cfg_));
    EXPECT_NO_THROW(to.Reconfigure(cfg_));
}

TEST_F(ICPInitTest, MissingRequiredIONames)
{
    shared_ptr<base_Layer> to_ptr(new ICP()); // pointer to test object

    EXPECT_THROW(to_ptr->IONames(LayerIONames()), ExceptionKeyError);

    map<string, pair<bool, string> > io_pairs; // false for input, true for output
    io_pairs[ICP::KEY_INPUT_POINT_CLOUD_SRC    ] = make_pair(0, NAME_INPUT_POINT_CLOUD_SRC);
    io_pairs[ICP::KEY_INPUT_POINT_CLOUD_TARGET ] = make_pair(0, NAME_INPUT_POINT_CLOUD_TARGET);
    io_pairs[ICP::KEY_OUTPUT_CONVERGENCE       ] = make_pair(1, NAME_OUTPUT_CONVERGENCE);
    io_pairs[ICP::KEY_OUTPUT_SCORE             ] = make_pair(1, NAME_OUTPUT_SCORE);
    io_pairs[ICP::KEY_OUTPUT_TRANSFORMATION    ] = make_pair(1, NAME_OUTPUT_TRANSFORMATION);

    ValidateRequiredIONames(io_pairs, to_ptr);
}

TEST_F(ICPInitTest, CreateWithFactory)
{
    shared_ptr<base_Layer> to_ptr = LayerFactory::CreateShared("ICP", cfg_, io_names_);

    EXPECT_TRUE(bool(to_ptr));
}

/**
 * @brief Test the live methods assuming a sucessful initialization
 */
class ICPTest : public ICPInitTest
{
protected:
    virtual void SetUp()
    {
        ICPInitTest::SetUp();

        to_ = LayerFactory::CreateShared("ICP", cfg_, io_names_);

        cloud_in_.reset(new PointCloudXYZ);

        // Fill in the CloudIn data
        cloud_in_->width    = 5;
        cloud_in_->height   = 1;
        cloud_in_->is_dense = false;
        cloud_in_->points.resize(cloud_in_->width * cloud_in_->height);
        for(size_t i=0; i < cloud_in_->points.size(); ++i) {

            cloud_in_->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
            cloud_in_->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
            cloud_in_->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f);
        }

        // target.x = in.x + constant
        cloud_target_.reset(new PointCloudXYZ);
        *cloud_target_ = *cloud_in_;
        for(size_t i=0; i < cloud_in_->points.size (); ++i) {

            cloud_target_->points[i].x += 0.7f;
        }

        sig_.Append(NAME_INPUT_POINT_CLOUD_SRC, PointCloud2Mat(cloud_in_));
        sig_.Append(NAME_INPUT_POINT_CLOUD_TARGET, PointCloud2Mat(cloud_target_));
    }

    virtual void TearDown()
    {
        ICPInitTest::TearDown();
    }

    shared_ptr<base_Layer> to_; ///< pointer to test object
    Signal sig_;
    PointCloudXYZ::Ptr cloud_in_;
    PointCloudXYZ::Ptr cloud_target_;
};

TEST_F(ICPTest, ActivateAndResponse) {

    to_->Activate(sig_);

    EXPECT_FALSE(sig_.Exists(NAME_OUTPUT_CONVERGENCE));
    EXPECT_FALSE(sig_.Exists(NAME_OUTPUT_SCORE));
    EXPECT_FALSE(sig_.Exists(NAME_OUTPUT_TRANSFORMATION));

    to_->Response(sig_);

    EXPECT_TRUE(sig_.Exists(NAME_OUTPUT_CONVERGENCE));
    EXPECT_TRUE(sig_.Exists(NAME_OUTPUT_SCORE));
    EXPECT_TRUE(sig_.Exists(NAME_OUTPUT_TRANSFORMATION));

    // check response
    // convergence:
    ASSERT_TRUE(icp.hasConverged()) << "this test needs to converge";
    EXPECT_MAT_DIMS_EQ(sig_.MostRecent(NAME_OUTPUT_CONVERGENCE), Size2i(1, 1)) << "Expecting Mat of single element for convergence.";
    EXPECT_EQ(sig_.MostRecent(NAME_OUTPUT_CONVERGENCE).at<float>(0)==1.f, icp.hasConverged()) << "Mismatched convergence.";

    // fitness score
    EXPECT_MAT_DIMS_EQ(sig_.MostRecent(NAME_OUTPUT_SCORE), Size2i(1, 1)) << "Expecting Mat of single element for fitness score.";
    EXPECT_FLOAT_EQ(sig_.MostRecent(NAME_OUTPUT_SCORE).at<float>(0), static_cast<float>(icp.getFitnessScore())) << "Mismatched fitness score.";

    // final transformation matrix estimated by registration method
    Mat1f transformation_expected;
    eigen2cv(icp.getFinalTransformation(), transformation_expected);

    Mat1f transformation_actual = sig_.MostRecent(NAME_OUTPUT_TRANSFORMATION);
    EXPECT_MAT_EQ(transformation_expected, transformation_actual) << "Mismatch in final transformation";

    // more detailed check of transformation matrix
    EXPECT_EQ(transformation_actual.rows, transformation_actual.cols) << "Expecting square matrix";
    for(int i=0; i<transformation_actual.rows; i++) {

        EXPECT_FLOAT_EQ(1.f, transformation_actual(i, i)) << "Expecting diagonal of 1's";
    }
}

#else // __WITH_PCL
    #warning "Skipping building ICP layer unittests"
#endif // __WITH_PCL

} // annonymous namespace for test fixtures
<commit_msg>test ICP activation in a scenario where it would not converge<commit_after>#include "layers/icp.h"

#include "pcl/registration/registration.h"
#include "pcl/registration/icp.h"

#include <Eigen/Dense>            ///< to enable OpenCV's eigen2cv()
#include <opencv2/core/eigen.hpp> ///< for eigen2cv(), preceeded be #include <Eigen/Dense>

#include "core/exception.h"
#include "core/layerconfig.h"
#include "core/pcl_utils.h"
#include "core/signal.h"
#include "core/stl.h"
#include "layers/layerfactory.h"
#include "ts/ts.h"
#include "ts/layer_assertions.h"

using namespace std;
using namespace cv;
using namespace pcl;
using namespace sem;

namespace {

#ifdef __WITH_PCL // test normally

const string NAME_INPUT_POINT_CLOUD_SRC     = "src";    ///< key to source cloud
const string NAME_INPUT_POINT_CLOUD_TARGET  = "target"; ///< key to target cloud

const string NAME_OUTPUT_SCORE              = "score";  ///< key to fitness score
const string NAME_OUTPUT_CONVERGENCE        = "c";      ///< key to convergence result
const string NAME_OUTPUT_TRANSFORMATION     = "transf"; ///< key to optional final tansformation

class ICPInitTest : public ::testing::Test
{
protected:
    virtual void SetUp()
    {
        cfg_ = LayerConfig();
        cfg_.Params(params_);
        io_names_ = LayerIONames();

        io_names_.Input(ICP::KEY_INPUT_POINT_CLOUD_SRC   , NAME_INPUT_POINT_CLOUD_SRC);
        io_names_.Input(ICP::KEY_INPUT_POINT_CLOUD_TARGET, NAME_INPUT_POINT_CLOUD_TARGET);
        io_names_.Output(ICP::KEY_OUTPUT_CONVERGENCE     , NAME_OUTPUT_CONVERGENCE);
        io_names_.Output(ICP::KEY_OUTPUT_SCORE           , NAME_OUTPUT_SCORE);
        io_names_.Output(ICP::KEY_OUTPUT_TRANSFORMATION  , NAME_OUTPUT_TRANSFORMATION);
    }

    virtual void TearDown()
    {
        params_.clear();
    }

    // members
    PTree params_;
    LayerConfig cfg_;
    LayerIONames io_names_;
};

TEST_F(ICPInitTest, Constructor)
{
    EXPECT_NO_THROW(ICP to);
    EXPECT_NO_THROW(ICP to(cfg_));
}

TEST_F(ICPInitTest, MissingParams)
{
    cfg_.Params(PTree());
    EXPECT_NO_THROW(ICP to(cfg_));

    ICP to;
    EXPECT_NO_THROW(to.Reset(cfg_));
    EXPECT_NO_THROW(to.Reconfigure(cfg_));
}

TEST_F(ICPInitTest, MissingRequiredIONames)
{
    shared_ptr<base_Layer> to_ptr(new ICP()); // pointer to test object

    EXPECT_THROW(to_ptr->IONames(LayerIONames()), ExceptionKeyError);

    map<string, pair<bool, string> > io_pairs; // false for input, true for output
    io_pairs[ICP::KEY_INPUT_POINT_CLOUD_SRC    ] = make_pair(0, NAME_INPUT_POINT_CLOUD_SRC);
    io_pairs[ICP::KEY_INPUT_POINT_CLOUD_TARGET ] = make_pair(0, NAME_INPUT_POINT_CLOUD_TARGET);
    io_pairs[ICP::KEY_OUTPUT_CONVERGENCE       ] = make_pair(1, NAME_OUTPUT_CONVERGENCE);
    io_pairs[ICP::KEY_OUTPUT_SCORE             ] = make_pair(1, NAME_OUTPUT_SCORE);
    io_pairs[ICP::KEY_OUTPUT_TRANSFORMATION    ] = make_pair(1, NAME_OUTPUT_TRANSFORMATION);

    ValidateRequiredIONames(io_pairs, to_ptr);
}

TEST_F(ICPInitTest, CreateWithFactory)
{
    shared_ptr<base_Layer> to_ptr = LayerFactory::CreateShared("ICP", cfg_, io_names_);

    EXPECT_TRUE(bool(to_ptr));
}

/**
 * @brief Test the live methods assuming a sucessful initialization
 */
class ICPTest : public ICPInitTest
{
protected:
    virtual void SetUp()
    {
        ICPInitTest::SetUp();

        to_ = LayerFactory::CreateShared("ICP", cfg_, io_names_);

        cloud_in_.reset(new PointCloudXYZ);

        // Fill in the CloudIn data
        cloud_in_->width    = 5;
        cloud_in_->height   = 1;
        cloud_in_->is_dense = false;
        cloud_in_->points.resize(cloud_in_->width * cloud_in_->height);
        for(size_t i=0; i < cloud_in_->points.size(); ++i) {

            cloud_in_->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
            cloud_in_->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
            cloud_in_->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f);
        }

        // target.x = in.x + constant
        cloud_target_.reset(new PointCloudXYZ);
        *cloud_target_ = *cloud_in_;
        for(size_t i=0; i < cloud_in_->points.size (); ++i) {

            cloud_target_->points[i].x += 0.7f;
        }

        sig_.Append(NAME_INPUT_POINT_CLOUD_SRC, PointCloud2Mat(cloud_in_));
        sig_.Append(NAME_INPUT_POINT_CLOUD_TARGET, PointCloud2Mat(cloud_target_));
    }

    virtual void TearDown()
    {
        ICPInitTest::TearDown();
    }

    shared_ptr<base_Layer> to_; ///< pointer to test object
    Signal sig_;
    PointCloudXYZ::Ptr cloud_in_;
    PointCloudXYZ::Ptr cloud_target_;
};

/**
 * @brief Compare Layer ICP with PCL's ICP routines
 */
TEST_F(ICPTest, ActivateAndResponse) {

    IterativeClosestPoint<PointXYZ, PointXYZ> icp;
    icp.setInputSource(cloud_in_);
    icp.setInputTarget(cloud_target_);

    PointCloudXYZ fin;
    icp.align(fin);

    to_->Activate(sig_);

    EXPECT_FALSE(sig_.Exists(NAME_OUTPUT_CONVERGENCE));
    EXPECT_FALSE(sig_.Exists(NAME_OUTPUT_SCORE));
    EXPECT_FALSE(sig_.Exists(NAME_OUTPUT_TRANSFORMATION));

    to_->Response(sig_);

    EXPECT_TRUE(sig_.Exists(NAME_OUTPUT_CONVERGENCE));
    EXPECT_TRUE(sig_.Exists(NAME_OUTPUT_SCORE));
    EXPECT_TRUE(sig_.Exists(NAME_OUTPUT_TRANSFORMATION));

    // check response
    // convergence:
    ASSERT_TRUE(icp.hasConverged()) << "this test needs to converge";
    EXPECT_MAT_DIMS_EQ(sig_.MostRecent(NAME_OUTPUT_CONVERGENCE), Size2i(1, 1)) << "Expecting Mat of single element for convergence.";
    EXPECT_EQ(sig_.MostRecent(NAME_OUTPUT_CONVERGENCE).at<float>(0)==1.f, icp.hasConverged()) << "Mismatched convergence.";

    // fitness score
    EXPECT_MAT_DIMS_EQ(sig_.MostRecent(NAME_OUTPUT_SCORE), Size2i(1, 1)) << "Expecting Mat of single element for fitness score.";
    EXPECT_FLOAT_EQ(sig_.MostRecent(NAME_OUTPUT_SCORE).at<float>(0), static_cast<float>(icp.getFitnessScore())) << "Mismatched fitness score.";

    // final transformation matrix estimated by registration method
    Mat1f transformation_expected;
    eigen2cv(icp.getFinalTransformation(), transformation_expected);

    Mat1f transformation_actual = sig_.MostRecent(NAME_OUTPUT_TRANSFORMATION);
    EXPECT_MAT_EQ(transformation_expected, transformation_actual) << "Mismatch in final transformation";

    // more detailed check of transformation matrix
    EXPECT_EQ(transformation_actual.rows, transformation_actual.cols) << "Expecting square matrix";
    for(int i=0; i<transformation_actual.rows; i++) {

        EXPECT_FLOAT_EQ(1.f, transformation_actual(i, i)) << "Expecting diagonal of 1's";
    }
}

/**
 * @brief Pick a scenario that would not converge
 */
TEST_F(ICPTest, NoConvergence)
{
    Mat1f in = Mat1f::zeros(1, 3);
    Mat1f target = in.clone();

    sig_.Append(NAME_INPUT_POINT_CLOUD_SRC, in);
    sig_.Append(NAME_INPUT_POINT_CLOUD_TARGET, target);

    to_->Activate(sig_);
    to_->Response(sig_);

    EXPECT_TRUE(sig_.Exists(NAME_OUTPUT_CONVERGENCE));
    EXPECT_TRUE(sig_.Exists(NAME_OUTPUT_SCORE));
    EXPECT_TRUE(sig_.Exists(NAME_OUTPUT_TRANSFORMATION));

    // check response
    // convergence:
    EXPECT_FALSE(sig_.MostRecent(NAME_OUTPUT_CONVERGENCE).at<float>(0)==1.f) << "Not expecting convergence.";

    // fitness score
    EXPECT_MAT_EQ(sig_.MostRecent(NAME_OUTPUT_SCORE), Mat1f(1, 1, 0.f)) << "Expecting zero score.";

    // more detailed check of transformation matrix, expecting a diagonal matrix
    Mat1f diagonal(4, 4, 0.f);
    for(int i=0; i<diagonal.rows; i++) {

        diagonal(i, i) = 1.f;
    }

    EXPECT_MAT_EQ(sig_.MostRecent(NAME_OUTPUT_TRANSFORMATION), diagonal) << "Expecting diagonal matrix when convergence fails.";
}

#else // __WITH_PCL
    #warning "Skipping building ICP layer unittests"
#endif // __WITH_PCL

} // annonymous namespace for test fixtures
<|endoftext|>