Krb5Credentials Krb5CCache::retrieveCred(krb5_principal sprinc) {
  Krb5Principal cprinc = getClientPrincipal();

  krb5_creds in_creds;
  memset(&in_creds, 0, sizeof(in_creds));
  in_creds.client = cprinc.get();
  in_creds.server = sprinc;

  return retrieveCred(in_creds, 0 /* flags */);
}
void Krb5CredentialsCacheManager::writeOutCache(size_t limit) {
  auto cc_mem = getCache();
  if (cc_mem == nullptr) {
    throw std::runtime_error("Trying to persist an empty cache");
  }

  // Get a default file cache and empty it out
  Krb5CCache file_cache = Krb5CCache::makeDefault(ctx_.get());
  Krb5Principal client = cc_mem->getClientPrincipal();
  krb5_error_code code = krb5_cc_initialize(
    ctx_.get(), file_cache.get(), client.get());
  raiseIf(code, "Failed initializing file credentials cache");

  // Put 'limit' number of most frequently used credentials into the
  // top_services set.
  vector<pair<string, uint64_t>> count_vector;
  ReadLock readLock(&serviceCountLock_);
  for (auto& element : serviceCountMap_) {
    count_vector.push_back(pair<string, uint64_t>(
      element.first, element.second->getCount()));
  }
  readLock.reset();
  sort(count_vector.begin(), count_vector.end(), serviceCountCompare);

  std::set<string> top_services;
  int count = 0;
  for (auto& element : count_vector) {
    if (count >= limit) {
      break;
    }
    top_services.insert(element.first);
    count++;
  }

  // Iterate through the cc
  for (auto it = cc_mem->begin(true); it != cc_mem->end(); ++it) {
    krb5_principal borrowed_server = it->server;
    Krb5Principal server(ctx_.get(), std::move(borrowed_server));
    const string princ_string = folly::to<string>(server);
    SCOPE_EXIT { server.release(); };  // give back borrowed_server
    // Always persist config and tgt principals. And only persist
    // top 'limit' services.
    if (!it.isConfigEntry() && !server.isTgt() &&
        top_services.count(princ_string) == 0) {
      continue;
    }

    // Store the cred into a file
    code = krb5_cc_store_cred(ctx_.get(), file_cache.get(), &(*it));
    // Erase from top_services struct so we don't persist the same
    // principal more than once.
    top_services.erase(princ_string);
    raiseIf(code, "Failed storing a credential into a file");
  }
}
Krb5Credentials Krb5CCache::getCredentials(
  krb5_principal sprinc, krb5_flags options) {

  Krb5Principal cprinc = getClientPrincipal();

  krb5_creds in_creds;
  memset(&in_creds, 0, sizeof(in_creds));
  in_creds.client = cprinc.get();
  in_creds.server = sprinc;

  return getCredentials(in_creds, options);
}
std::unique_ptr<Krb5CCache> Krb5CredentialsCacheManager::readInCache() {
  auto mem = folly::make_unique<Krb5CCache>(
    Krb5CCache::makeNewUnique(ctx_.get(), "MEMORY"));
  // Get the default local cache
  Krb5CCache file_cache = Krb5CCache::makeDefault(ctx_.get());
  Krb5Principal client = file_cache.getClientPrincipal();

  // Copy the cache into memory
  krb5_error_code code = krb5_cc_initialize(
    ctx_.get(), mem->get(), client.get());
  raiseIf(code, "initializing memory ccache");
  code = krb5_cc_copy_creds(ctx_.get(), file_cache.get(), mem->get());
  raiseIf(code, "copying to memory cache");

  auto service_list = mem->getServicePrincipalList();
  for (auto& service : service_list)  {
    incUsedService(folly::to<string>(service));
  }

  return mem;
}