/* Copyright (c) 2021, 2022, Oracle and/or its affiliates.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.

This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation.  The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.

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

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */

#include <memory>
#include <sstream>

#include "my_dbug.h"
#include "mysql_server_keyring_lockable_imp.h"

#include <include/mysql/components/my_service.h>
#include <include/mysql/components/services/registry.h>
#include <include/mysqld_error.h>
#include <include/rwlock_scoped_lock.h>
#include <include/scope_guard.h>
#include <mysql/components/services/log_builtins.h>

#include "sql/mysqld.h"        /* srv_registry */
#include "sql/set_var.h"       /* keyring_access_test */
#include "sql/sql_component.h" /* init and deinit function for RWLock */
#include "sql/sql_plugin.h"    /* opt_plugin_dir */

/* clang-format off */
/**
  @page PAGE_COMPONENT_KEYRING_MYSQL_SERVER component_keyring_mysql_server

  MySQL server component provides a wrapper implementation of all
  component services related to keyring. Following is the list
  of services:

  - keyring_aes
  - keyring_generate
  - keyring_keys_metadata_iterator
  - keyring_component_status
  - keyring_metadata_query
  - keyring_reader
  - keyring_load
  - keyring_writer

  The implementation of above services in turn calls actual keyring component
  implementation (such as component_keyring_file). However, this component
  provides concurrency control for read/write operations affecting keyring.

  In addition, this implementation also checks if KEYRING_OPERATIONS system
  variable permits given operation or not.

  This implementation is set as default implementation for MySQL server.

  Handles for underlying keyring component service implementations are
  obtained at two locations:
  1. After processing server's manifest file
  2. After installing proxy keyring component for keyring plugin

  Once handles are set, they are not set again.

  Thus, if a keyring component is installed through manifest file, keyring plugin
  will not considered even if it is installed through --early-plugin-load.

  These handles ares freed at the time of server shutdown.
*/
/* clang-format on */

/** Server handle for keyring AES encryption service */
SERVICE_TYPE(keyring_aes) *srv_keyring_aes = nullptr;

/** Server handle for keyring generator service */
SERVICE_TYPE(keyring_generator) *srv_keyring_generator = nullptr;

/** Server handle for keyring keys metadata service */
SERVICE_TYPE(keyring_keys_metadata_iterator)
*srv_keyring_keys_metadata_iterator = nullptr;

/** Server handle for keyring status service */
SERVICE_TYPE(keyring_component_status) *srv_keyring_component_status = nullptr;

/** Server handle for keyring component metadata service */
SERVICE_TYPE(keyring_component_metadata_query)
*srv_keyring_component_metadata_query = nullptr;

/** Server handle for keyring reader service */
SERVICE_TYPE(keyring_reader_with_status) *srv_keyring_reader = nullptr;

/** Server handle for keyring load service */
SERVICE_TYPE(keyring_load) *srv_keyring_load = nullptr;

/** Server handle for keyring writer service */
SERVICE_TYPE(keyring_writer) *srv_keyring_writer = nullptr;

enum lockable_service_enum {
  AES_ENCRYPTION = 0,
  GENERATOR,
  KEYS_METADATA_ITERATOR,
  COMPONENT_STATUS,
  COMPONENT_METADATA_QUERY,
  READER_WITH_STATUS,
  LOAD,
  WRITER
};
static const char *lockable_service_names[] = {
    "keyring_aes",
    "keyring_generator",
    "keyring_keys_metadata_iterator",
    "keyring_component_status",
    "keyring_component_metadata_query",
    "keyring_reader_with_status",
    "keyring_load",
    "keyring_writer"};

namespace keyring_lockable {

/** Server component internal handle for keyring AES encryption service */
SERVICE_TYPE(keyring_aes) *internal_keyring_aes = nullptr;

/** Server component internal handle for keyring generator service */
SERVICE_TYPE(keyring_generator) *internal_keyring_generator = nullptr;

/** Server component internal handle for keyring keys metadata service */
SERVICE_TYPE(keyring_keys_metadata_iterator)
*internal_keyring_keys_metadata_iterator = nullptr;

/** Server component internal handle for keyring component status service */
SERVICE_TYPE(keyring_component_status) *internal_keyring_component_status =
    nullptr;

/** Server component internal handle for keyring component metadata service */
SERVICE_TYPE(keyring_component_metadata_query)
*internal_keyring_component_metadata_query = nullptr;

/** Server component internal handle for keyring reader service */
SERVICE_TYPE(keyring_reader_with_status) *internal_keyring_reader = nullptr;

/** Server component internal handle for keyring load service */
SERVICE_TYPE(keyring_load) *internal_keyring_load = nullptr;

/** Server component internal handle for keyring writer service */
SERVICE_TYPE(keyring_writer) *internal_keyring_writer = nullptr;

/** Access control for keyring */
mysql_rwlock_t LOCK_keyring_component;
/** PSI key for @sa LOCK_keyring_component */
static PSI_rwlock_key key_LOCK_keyring_component;

/** Error messages */

/**
  Wrapper over my_h_keyring_reader_object
  to associate RWlock.

  Read lock is taken when reader is initialized
  and freed when reader is deinitialized.

  This makes sure that fetch_length() and fetch()
  APIs are protected by the read lock.
*/
struct my_h_keyring_reader_object_server {
  my_h_keyring_reader_object object_;
  rwlock_scoped_lock lock_;
};

/**
  Wrapper over my_h_keyring_keys_metadata_iterator
  to associate RWlock.

  Read lock is taken when iterator is initialized
  and freed when iterator is deinitialized.

  This makes sure that get_length(), get(), next()
  and is_valid() APIs are protected by the read lock.
*/
struct my_h_keyring_keys_metadata_iterator_server {
  my_h_keyring_keys_metadata_iterator iterator_;
  rwlock_scoped_lock lock_;
};

namespace keyring_common {
namespace service_definition {

/**
  Helper to check service validity

  @param [in] service Service handle
  @param [in] name    Service name enum

  @returns validity state of esrvice
    @retval false Valid service handle
    @retval true  Invalid service handle. Error raised.
*/
static bool check_service(const void *service,
                          enum lockable_service_enum name) {
  if (service == nullptr) {
    LogErr(WARNING_LEVEL, ER_WARN_NO_KEYRING_COMPONENT_SERVICE_FOUND,
           lockable_service_names[name]);
    return true;
  }
  return false;
}

/* AES Encryption Service */

DEFINE_BOOL_METHOD(Keyring_aes_service_impl::get_size,
                   (size_t input_length, const char *mode, size_t block_size,
                    size_t *out_size)) {
  if (check_service(internal_keyring_aes, AES_ENCRYPTION)) return true;

  /* No need to take a lock as this one does not access keyring */
  return internal_keyring_aes->get_size(input_length, mode, block_size,
                                        out_size);
}

DEFINE_BOOL_METHOD(Keyring_aes_service_impl::encrypt,
                   (const char *data_id, const char *auth_id, const char *mode,
                    size_t block_size, const unsigned char *iv, int padding,
                    const unsigned char *data_buffer, size_t data_buffer_length,
                    unsigned char *out_buffer, size_t out_buffer_length,
                    size_t *out_length)) {
  if (check_service(internal_keyring_aes, AES_ENCRYPTION)) return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, false, __FILE__, __LINE__);
  return internal_keyring_aes->encrypt(
      data_id, auth_id, mode, block_size, iv, padding, data_buffer,
      data_buffer_length, out_buffer, out_buffer_length, out_length);
}

DEFINE_BOOL_METHOD(Keyring_aes_service_impl::decrypt,
                   (const char *data_id, const char *auth_id, const char *mode,
                    size_t block_size, const unsigned char *iv, int padding,
                    const unsigned char *data_buffer, size_t data_buffer_length,
                    unsigned char *out_buffer, size_t out_buffer_length,
                    size_t *out_length)) {
  if (check_service(internal_keyring_aes, AES_ENCRYPTION)) return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, false, __FILE__, __LINE__);
  return internal_keyring_aes->decrypt(
      data_id, auth_id, mode, block_size, iv, padding, data_buffer,
      data_buffer_length, out_buffer, out_buffer_length, out_length);
}

/* keyring_generator */

DEFINE_BOOL_METHOD(Keyring_generator_service_impl::generate,
                   (const char *data_id, const char *auth_id,
                    const char *data_type, size_t data_size)) {
  if (keyring_access_test()) return true;

  if (check_service(internal_keyring_generator, GENERATOR)) return true;

  DBUG_EXECUTE_IF("keyring_generate_fail", DBUG_SUICIDE(););
  rwlock_scoped_lock lock(&LOCK_keyring_component, true, __FILE__, __LINE__);
  return internal_keyring_generator->generate(data_id, auth_id, data_type,
                                              data_size);
}

/* keyring_keys_metdata_iterator */

DEFINE_BOOL_METHOD(Keyring_keys_metadata_iterator_service_impl::init,
                   (my_h_keyring_keys_metadata_iterator * forward_iterator)) {
  if (check_service(internal_keyring_keys_metadata_iterator,
                    KEYS_METADATA_ITERATOR))
    return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, false, __FILE__, __LINE__);
  my_h_keyring_keys_metadata_iterator_server *local_object =
      new my_h_keyring_keys_metadata_iterator_server{nullptr, std::move(lock)};
  if (local_object == nullptr) return true;
  bool retval =
      internal_keyring_keys_metadata_iterator->init(&local_object->iterator_);
  if (retval == true) {
    delete local_object;
    return retval;
  }

  *forward_iterator =
      reinterpret_cast<my_h_keyring_keys_metadata_iterator>(local_object);
  return retval;
}

DEFINE_BOOL_METHOD(Keyring_keys_metadata_iterator_service_impl::deinit,
                   (my_h_keyring_keys_metadata_iterator forward_iterator)) {
  if (check_service(internal_keyring_keys_metadata_iterator,
                    KEYS_METADATA_ITERATOR))
    return true;

  my_h_keyring_keys_metadata_iterator_server *local_object =
      reinterpret_cast<my_h_keyring_keys_metadata_iterator_server *>(
          forward_iterator);
  if (!local_object) return true;

  /*
    Even if underlying keyring fails to deinitialize
    actual iterator, we will release the lock.
    Otherwise, keyring wil remain in locked state
  */
  bool retval =
      internal_keyring_keys_metadata_iterator->deinit(local_object->iterator_);
  local_object->iterator_ = nullptr;

  /* This shall release the lock too */
  delete local_object;
  return retval;
}

DEFINE_BOOL_METHOD(Keyring_keys_metadata_iterator_service_impl::is_valid,
                   (my_h_keyring_keys_metadata_iterator forward_iterator)) {
  if (check_service(internal_keyring_keys_metadata_iterator,
                    KEYS_METADATA_ITERATOR))
    return false;

  my_h_keyring_keys_metadata_iterator_server *local_object =
      reinterpret_cast<my_h_keyring_keys_metadata_iterator_server *>(
          forward_iterator);
  if (!local_object) return false;

  return internal_keyring_keys_metadata_iterator->is_valid(
      local_object->iterator_);
}

DEFINE_BOOL_METHOD(Keyring_keys_metadata_iterator_service_impl::get_length,
                   (my_h_keyring_keys_metadata_iterator forward_iterator,
                    size_t *data_id_length, size_t *auth_id_length)) {
  if (check_service(internal_keyring_keys_metadata_iterator,
                    KEYS_METADATA_ITERATOR))
    return true;

  my_h_keyring_keys_metadata_iterator_server *local_object =
      reinterpret_cast<my_h_keyring_keys_metadata_iterator_server *>(
          forward_iterator);
  if (!local_object) return true;

  return internal_keyring_keys_metadata_iterator->get_length(
      local_object->iterator_, data_id_length, auth_id_length);
}

DEFINE_BOOL_METHOD(Keyring_keys_metadata_iterator_service_impl::next,
                   (my_h_keyring_keys_metadata_iterator forward_iterator)) {
  if (check_service(internal_keyring_keys_metadata_iterator,
                    KEYS_METADATA_ITERATOR))
    return true;

  my_h_keyring_keys_metadata_iterator_server *local_object =
      reinterpret_cast<my_h_keyring_keys_metadata_iterator_server *>(
          forward_iterator);
  if (!local_object) return true;

  return internal_keyring_keys_metadata_iterator->next(local_object->iterator_);
}

DEFINE_BOOL_METHOD(Keyring_keys_metadata_iterator_service_impl::get,
                   (my_h_keyring_keys_metadata_iterator forward_iterator,
                    char *data_id, size_t data_id_length, char *auth_id,
                    size_t auth_id_length)) {
  if (check_service(internal_keyring_keys_metadata_iterator,
                    KEYS_METADATA_ITERATOR))
    return true;

  my_h_keyring_keys_metadata_iterator_server *local_object =
      reinterpret_cast<my_h_keyring_keys_metadata_iterator_server *>(
          forward_iterator);
  if (!local_object) return true;

  return internal_keyring_keys_metadata_iterator->get(local_object->iterator_,
                                                      data_id, data_id_length,
                                                      auth_id, auth_id_length);
}

/* keyring_component_metadata_query */

DEFINE_BOOL_METHOD(Keyring_metadata_query_service_impl::is_initialized, ()) {
  if (check_service(internal_keyring_component_status, COMPONENT_STATUS))
    return false;
  return internal_keyring_component_status->is_initialized();
}

DEFINE_BOOL_METHOD(Keyring_metadata_query_service_impl::init,
                   (my_h_keyring_component_metadata_iterator *
                    metadata_iterator)) {
  if (check_service(internal_keyring_component_metadata_query,
                    COMPONENT_METADATA_QUERY))
    return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, false, __FILE__, __LINE__);
  return internal_keyring_component_metadata_query->init(metadata_iterator);
}

DEFINE_BOOL_METHOD(
    Keyring_metadata_query_service_impl::deinit,
    (my_h_keyring_component_metadata_iterator metadata_iterator)) {
  if (check_service(internal_keyring_component_metadata_query,
                    COMPONENT_METADATA_QUERY))
    return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, false, __FILE__, __LINE__);
  return internal_keyring_component_metadata_query->deinit(metadata_iterator);
}

DEFINE_BOOL_METHOD(
    Keyring_metadata_query_service_impl::is_valid,
    (my_h_keyring_component_metadata_iterator metadata_iterator)) {
  if (check_service(internal_keyring_component_metadata_query,
                    COMPONENT_METADATA_QUERY))
    return false;

  rwlock_scoped_lock lock(&LOCK_keyring_component, false, __FILE__, __LINE__);
  return internal_keyring_component_metadata_query->is_valid(metadata_iterator);
}

DEFINE_BOOL_METHOD(
    Keyring_metadata_query_service_impl::next,
    (my_h_keyring_component_metadata_iterator metadata_iterator)) {
  if (check_service(internal_keyring_component_metadata_query,
                    COMPONENT_METADATA_QUERY))
    return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, false, __FILE__, __LINE__);
  return internal_keyring_component_metadata_query->next(metadata_iterator);
}

DEFINE_BOOL_METHOD(Keyring_metadata_query_service_impl::get_length,
                   (my_h_keyring_component_metadata_iterator metadata_iterator,
                    size_t *key_buffer_length, size_t *value_buffer_length)) {
  if (check_service(internal_keyring_component_metadata_query,
                    COMPONENT_METADATA_QUERY))
    return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, false, __FILE__, __LINE__);
  return internal_keyring_component_metadata_query->get_length(
      metadata_iterator, key_buffer_length, value_buffer_length);
}

DEFINE_BOOL_METHOD(Keyring_metadata_query_service_impl::get,
                   (my_h_keyring_component_metadata_iterator metadata_iterator,
                    char *key_buffer, size_t key_buffer_length,
                    char *value_buffer, size_t value_buffer_length)) {
  if (check_service(internal_keyring_component_metadata_query,
                    COMPONENT_METADATA_QUERY))
    return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, false, __FILE__, __LINE__);
  return internal_keyring_component_metadata_query->get(
      metadata_iterator, key_buffer, key_buffer_length, value_buffer,
      value_buffer_length);
}

/* keyring_reader_with_status */
DEFINE_BOOL_METHOD(Keyring_reader_service_impl::init,
                   (const char *data_id, const char *auth_id,
                    my_h_keyring_reader_object *reader_object)) {
  if (check_service(internal_keyring_reader, READER_WITH_STATUS)) return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, false, __FILE__, __LINE__);
  my_h_keyring_reader_object_server *local_object =
      new my_h_keyring_reader_object_server{nullptr, std::move(lock)};
  if (local_object == nullptr) return true;

  bool retval =
      internal_keyring_reader->init(data_id, auth_id, &(local_object->object_));
  if (retval == true || local_object->object_ == nullptr) {
    delete local_object;
    *reader_object = nullptr;
    return retval;
  }

  *reader_object = reinterpret_cast<my_h_keyring_reader_object>(local_object);
  return retval;
}

DEFINE_BOOL_METHOD(Keyring_reader_service_impl::deinit,
                   (my_h_keyring_reader_object reader_object)) {
  if (check_service(internal_keyring_reader, READER_WITH_STATUS)) return true;

  my_h_keyring_reader_object_server *local_object =
      reinterpret_cast<my_h_keyring_reader_object_server *>(reader_object);
  if (!local_object) return true;

  /*
    Even if underlying keyring fails to deinitialize
    actual iterator, we will release the lock.
    Otherwise, keyring wil remain in locked state
  */
  bool retval = internal_keyring_reader->deinit(local_object->object_);
  local_object->object_ = nullptr;

  /* This shall release the lock too */
  delete local_object;
  return retval;
}

DEFINE_BOOL_METHOD(Keyring_reader_service_impl::fetch_length,
                   (my_h_keyring_reader_object reader_object, size_t *data_size,
                    size_t *data_type_size)) {
  if (check_service(internal_keyring_reader, READER_WITH_STATUS)) return true;

  my_h_keyring_reader_object_server *local_object =
      reinterpret_cast<my_h_keyring_reader_object_server *>(reader_object);
  if (!local_object) return true;

  return internal_keyring_reader->fetch_length(local_object->object_, data_size,
                                               data_type_size);
}

DEFINE_BOOL_METHOD(Keyring_reader_service_impl::fetch,
                   (my_h_keyring_reader_object reader_object,
                    unsigned char *data_buffer, size_t data_buffer_length,
                    size_t *data_size, char *data_type,
                    size_t data_type_buffer_length, size_t *data_type_size)) {
  if (check_service(internal_keyring_reader, READER_WITH_STATUS)) return true;

  my_h_keyring_reader_object_server *local_object =
      reinterpret_cast<my_h_keyring_reader_object_server *>(reader_object);
  if (!local_object) return true;

  return internal_keyring_reader->fetch(
      local_object->object_, data_buffer, data_buffer_length, data_size,
      data_type, data_type_buffer_length, data_type_size);
}

/* keyring_load */

DEFINE_BOOL_METHOD(Keyring_load_service_impl::load,
                   (const char *component_path, const char *instance_path)) {
  if (keyring_access_test()) return true;

  if (check_service(internal_keyring_load, LOAD)) return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, true, __FILE__, __LINE__);
  return internal_keyring_load->load(component_path, instance_path);
}

/* keyring_writer */

DEFINE_BOOL_METHOD(Keyring_writer_service_impl::store,
                   (const char *data_id, const char *auth_id,
                    const unsigned char *data, size_t data_size,
                    const char *data_type)) {
  if (keyring_access_test()) return true;

  if (check_service(internal_keyring_writer, WRITER)) return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, true, __FILE__, __LINE__);
  return internal_keyring_writer->store(data_id, auth_id, data, data_size,
                                        data_type);
}

DEFINE_BOOL_METHOD(Keyring_writer_service_impl::remove,
                   (const char *data_id, const char *auth_id)) {
  if (keyring_access_test()) return true;

  if (check_service(internal_keyring_writer, WRITER)) return true;

  rwlock_scoped_lock lock(&LOCK_keyring_component, true, __FILE__, __LINE__);
  return internal_keyring_writer->remove(data_id, auth_id);
}

}  // namespace service_definition
}  // namespace keyring_common
}  // namespace keyring_lockable

using keyring_aes_t = SERVICE_TYPE_NO_CONST(keyring_aes);
using keyring_generator_t = SERVICE_TYPE_NO_CONST(keyring_generator);
using keyring_keys_metadata_iterator_t =
    SERVICE_TYPE_NO_CONST(keyring_keys_metadata_iterator);
using keyring_component_status_t =
    SERVICE_TYPE_NO_CONST(keyring_component_status);
using keyring_component_metadata_query_t =
    SERVICE_TYPE_NO_CONST(keyring_component_metadata_query);
using keyring_reader_with_status_t =
    SERVICE_TYPE_NO_CONST(keyring_reader_with_status);
using keyring_load_t = SERVICE_TYPE_NO_CONST(keyring_load);
using keyring_writer_t = SERVICE_TYPE_NO_CONST(keyring_writer);

/** Initialize lockable keyring component */
void keyring_lockable_init() {
  mysql_rwlock_init(keyring_lockable::key_LOCK_keyring_component,
                    &keyring_lockable::LOCK_keyring_component);
}

/** Deinitialize lockable keyring component */
void keyring_lockable_deinit() {
  mysql_rwlock_destroy(&keyring_lockable::LOCK_keyring_component);
}

/**
  Set server's implementation of keyring as default

  server component provides implementation of all keyring
  related services to provide concurrency control.

  In turn it uses either one of the following as actual
  implementation(in order of priority):
  A> A keyring component loaded through manifest file
  B> Proxy keyring component over keyring plugin

  There are two places where this function is called.

  Path 1
  ======

  At the time of server startup, server_component's services
  are registered when minimal chassis is initialized. However,
  without actual keyring implementation, they are not really
  of any use.

  After minimal chassis initialization, server read manifest
  file. If file is present and contains keyring component details,
  the component is loaded and services are registered.

  At this point we have 2 implementations of each keyring
  services: One provided by server and another provided by
  keyring component.

  One component is loaded set_srv_keyring_implementation_as_default()
  is called. At this stage function will:
  1. Set server's implementation of lockable keyring as default
  2. Acquire handles to all services provided by keyring component

  After this point, keyring functionality can be used.

  Path 2
  ======

  If manifest file is not provided or does not contain details
  of keyring plugin, call to set_srv_keyring_implementation_as_default()
  will still be made and set server's implementation as default.

  However, no internal handles are set. Thus, keyring functionality
  can not be used still.

  Startup sequence will then process --early-plugin-load if provided.

  Afterwards, daemon_proxy_keyring_implementation plugin is loaded
  which registers a subset of keyring component services. These services
  will use keyring plugin if installed.

  At this point set_srv_keyring_implementation_as_default() is called
  once again. If internal handles were not set with the first call,
  they are set now and will use daemon_proxy_keyring_implementation.

  This means, if keyring plugin is available (either through
  --early-plugin-load OR loaded later through INSTALL PLUGIN),
  it will be used.

*/
void set_srv_keyring_implementation_as_default() {
  my_service<SERVICE_TYPE(registry_registration)> registrator(
      "registry_registration", srv_registry);

  auto obtain_service_handles =
      [](const char *component_part,
         SERVICE_TYPE(keyring_aes) * *aes_encryption,
         SERVICE_TYPE(keyring_generator) * *generator,
         SERVICE_TYPE(keyring_keys_metadata_iterator) * *keys_metadata_iterator,
         SERVICE_TYPE(keyring_component_status) * *component_status,
         SERVICE_TYPE(keyring_component_metadata_query) *
             *component_metadata_query,
         SERVICE_TYPE(keyring_reader_with_status) * *reader,
         SERVICE_TYPE(keyring_load) * *load,
         SERVICE_TYPE(keyring_writer) * *writer) {
        my_service<SERVICE_TYPE(registry_registration)> lamda_registrator(
            "registry_registration", srv_registry);

        std::string service_name;

        /* Reader service */
        service_name.assign(lockable_service_names[READER_WITH_STATUS]);
        service_name.append(component_part);
        srv_registry->acquire(
            service_name.c_str(),
            reinterpret_cast<my_h_service *>(
                const_cast<keyring_reader_with_status_t **>(reader)));

        /* AES encryption service */
        service_name.assign(lockable_service_names[AES_ENCRYPTION]);
        srv_registry->acquire_related(
            service_name.c_str(),
            reinterpret_cast<my_h_service>(
                const_cast<keyring_reader_with_status_t *>(*reader)),
            reinterpret_cast<my_h_service *>(
                const_cast<keyring_aes_t **>(aes_encryption)));

        /* Generator service */
        service_name.assign(lockable_service_names[GENERATOR]);
        srv_registry->acquire_related(
            service_name.c_str(),
            reinterpret_cast<my_h_service>(
                const_cast<keyring_reader_with_status_t *>(*reader)),
            reinterpret_cast<my_h_service *>(
                const_cast<keyring_generator_t **>(generator)));

        /* Keys metadata iterator service */
        service_name.assign(lockable_service_names[KEYS_METADATA_ITERATOR]);
        srv_registry->acquire_related(
            service_name.c_str(),
            reinterpret_cast<my_h_service>(
                const_cast<keyring_reader_with_status_t *>(*reader)),
            reinterpret_cast<my_h_service *>(
                const_cast<keyring_keys_metadata_iterator_t **>(
                    keys_metadata_iterator)));

        /* Component status service */
        service_name.assign(lockable_service_names[COMPONENT_STATUS]);
        srv_registry->acquire_related(
            service_name.c_str(),
            reinterpret_cast<my_h_service>(
                const_cast<keyring_reader_with_status_t *>(*reader)),
            reinterpret_cast<my_h_service *>(
                const_cast<keyring_component_status_t **>(component_status)));

        /* Component metadata query service */
        service_name.assign(lockable_service_names[COMPONENT_METADATA_QUERY]);
        srv_registry->acquire_related(
            service_name.c_str(),
            reinterpret_cast<my_h_service>(
                const_cast<keyring_reader_with_status_t *>(*reader)),
            reinterpret_cast<my_h_service *>(
                const_cast<keyring_component_metadata_query_t **>(
                    component_metadata_query)));

        /* Reload service */
        service_name.assign(lockable_service_names[LOAD]);
        srv_registry->acquire_related(
            service_name.c_str(),
            reinterpret_cast<my_h_service>(
                const_cast<keyring_reader_with_status_t *>(*reader)),
            reinterpret_cast<my_h_service *>(
                const_cast<keyring_load_t **>(load)));

        /* Writer service */
        service_name.assign(lockable_service_names[WRITER]);
        srv_registry->acquire_related(
            service_name.c_str(),
            reinterpret_cast<my_h_service>(
                const_cast<keyring_reader_with_status_t *>(*reader)),
            reinterpret_cast<my_h_service *>(
                const_cast<keyring_writer_t **>(writer)));
      };

  /* Part 1: Set lockable keyring as default */

  /*
    The service infrastructure's current behavior is that for any given service,
    the very first implementation that's registered with service registry
    becomes the default implementation for that service.

    Since server component's services are registered right after initializing
    minimal chassis, we don't have to set server component's implementation
    for keyring services as default explicitly.

    If this changes in future, something similar to following will be needed.

    for (auto service : lockable_service_names) {
      std::string name(service);
      name.append(".mysql_server");
      registrator->set_default(name.c_str());
    }
  */

  /* Part 2: Set internal handles that point to actual implementation */
  if (keyring_lockable::internal_keyring_aes == nullptr &&
      keyring_lockable::internal_keyring_generator == nullptr &&
      keyring_lockable::internal_keyring_component_status == nullptr &&
      keyring_lockable::internal_keyring_keys_metadata_iterator == nullptr &&
      keyring_lockable::internal_keyring_component_metadata_query == nullptr &&
      keyring_lockable::internal_keyring_reader == nullptr &&
      keyring_lockable::internal_keyring_load == nullptr &&
      keyring_lockable::internal_keyring_writer == nullptr) {
    /*
      1. Acquire iterator for keyring_reader
      2. Move forward if current handle is pointing to
      keyring_reader.mysql_server
      3. If found, acquire all related services (writer, generator, forward
      interator, status)
      4. Set global handles to point to them
    */
    my_h_service_iterator iterator;
    my_service<SERVICE_TYPE(registry_query)> reg_query("registry_query",
                                                       srv_registry);
    if (reg_query->create(lockable_service_names[READER_WITH_STATUS],
                          &iterator))
      return;
    std::string service_name;
    {
      auto guard = create_scope_guard(
          [&reg_query, &iterator] { reg_query->release(iterator); });
      for (; !reg_query->is_valid(iterator); reg_query->next(iterator)) {
        const char *name = nullptr;
        if (reg_query->get(iterator, &name)) return;
        service_name.assign(name);
        /*
          Registry implementation does not necessarily return services
          with name "keyring_error_state". Current implementation returns an
          iterator over std::map. Hence, if following entries are present in the
          service registry:
          "keyring_reader.mysql_server"
          "keyring_writer.mysql_server"
          "keyring_generator.mysql_server"

          iterator will not only return "keyring_error_state.mysql_server", but
          also "keyring_reader.mysql_server" and "keyring_writer.mysql_server".

          Thus, we can not assume that only services matching with
          "keyring_error_state" will be returned through iterator.

          Hence, we need to match service name and component name part.

          This will fall apart if a clever implemention decides to register
          keyring_error_state service with name other than
          "keyring_error_state". To resolve that, we need iterator based on
          service type and not service name.
        */
        if (service_name.find(lockable_service_names[READER_WITH_STATUS]) !=
            std::string::npos) {
          if (service_name.find("mysql_server") == std::string::npos) break;
        }
        service_name.clear();
      }
    }

    if (service_name.length()) {
      obtain_service_handles(
          /* Find dot, the component name is right after the dot. */
          strchr(service_name.c_str(), '.'),
          &keyring_lockable::internal_keyring_aes,
          &keyring_lockable::internal_keyring_generator,
          &keyring_lockable::internal_keyring_keys_metadata_iterator,
          &keyring_lockable::internal_keyring_component_status,
          &keyring_lockable::internal_keyring_component_metadata_query,
          &keyring_lockable::internal_keyring_reader,
          &keyring_lockable::internal_keyring_load,
          &keyring_lockable::internal_keyring_writer);
    }

    /* Initiliaze keyring */
    if (keyring_lockable::internal_keyring_load != nullptr) {
      (void)keyring_lockable::internal_keyring_load->load(opt_plugin_dir,
                                                          mysql_real_data_home);
    }
  }

  /* Part 3: Set server wide handles */
  if (srv_keyring_aes == nullptr &&
      srv_keyring_component_metadata_query == nullptr &&
      srv_keyring_generator == nullptr &&
      srv_keyring_keys_metadata_iterator == nullptr &&
      srv_keyring_reader == nullptr && srv_keyring_load == nullptr &&
      srv_keyring_writer == nullptr) {
    obtain_service_handles(
        ".mysql_server", &srv_keyring_aes, &srv_keyring_generator,
        &srv_keyring_keys_metadata_iterator, &srv_keyring_component_status,
        &srv_keyring_component_metadata_query, &srv_keyring_reader,
        &srv_keyring_load, &srv_keyring_writer);
  }
}

void release_keyring_handles() {
  auto release_service_handles =
      [](SERVICE_TYPE(keyring_aes) * *aes_encryption,
         SERVICE_TYPE(keyring_generator) * *generator,
         SERVICE_TYPE(keyring_keys_metadata_iterator) * *keys_metadata_iterator,
         SERVICE_TYPE(keyring_component_status) * *component_status,
         SERVICE_TYPE(keyring_component_metadata_query) *
             *component_metadata_query,
         SERVICE_TYPE(keyring_reader_with_status) * *reader,
         SERVICE_TYPE(keyring_load) * *load,
         SERVICE_TYPE(keyring_writer) * *writer) {
        my_service<SERVICE_TYPE(registry_registration)> registrator(
            "registry_registration", srv_registry);
        if (*aes_encryption)
          srv_registry->release(reinterpret_cast<my_h_service>(
              const_cast<keyring_aes_t *>(*aes_encryption)));
        *aes_encryption = nullptr;

        if (*generator)
          srv_registry->release(reinterpret_cast<my_h_service>(
              const_cast<keyring_generator_t *>(*generator)));
        *generator = nullptr;

        if (*keys_metadata_iterator)
          srv_registry->release(reinterpret_cast<my_h_service>(
              const_cast<keyring_keys_metadata_iterator_t *>(
                  *keys_metadata_iterator)));
        *keys_metadata_iterator = nullptr;

        if (*component_status)
          srv_registry->release(reinterpret_cast<my_h_service>(
              const_cast<keyring_component_status_t *>(*component_status)));
        *component_status = nullptr;

        if (*component_metadata_query)
          srv_registry->release(reinterpret_cast<my_h_service>(
              const_cast<keyring_component_metadata_query_t *>(
                  *component_metadata_query)));
        *component_metadata_query = nullptr;

        if (*reader)
          srv_registry->release(reinterpret_cast<my_h_service>(
              const_cast<keyring_reader_with_status_t *>(*reader)));
        *reader = nullptr;

        if (*load)
          srv_registry->release(reinterpret_cast<my_h_service>(
              const_cast<keyring_load_t *>(*load)));
        *load = nullptr;

        if (*writer)
          srv_registry->release(reinterpret_cast<my_h_service>(
              const_cast<keyring_writer_t *>(*writer)));
        *writer = nullptr;
      };

  /* Part 1: Release server wide handles */
  release_service_handles(
      &srv_keyring_aes, &srv_keyring_generator,
      &srv_keyring_keys_metadata_iterator, &srv_keyring_component_status,
      &srv_keyring_component_metadata_query, &srv_keyring_reader,
      &srv_keyring_load, &srv_keyring_writer);

  /* Part 2: Release internal handles that point to actual implementation */
  release_service_handles(
      &keyring_lockable::internal_keyring_aes,
      &keyring_lockable::internal_keyring_generator,
      &keyring_lockable::internal_keyring_keys_metadata_iterator,
      &keyring_lockable::internal_keyring_component_status,
      &keyring_lockable::internal_keyring_component_metadata_query,
      &keyring_lockable::internal_keyring_reader,
      &keyring_lockable::internal_keyring_load,
      &keyring_lockable::internal_keyring_writer);
}

bool keyring_status_no_error() {
  return (
      keyring_lockable::internal_keyring_component_status != nullptr &&
      keyring_lockable::internal_keyring_component_status->is_initialized());
}
