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::pair<uint64_t, uint64_t> Krb5CCache::getLifetime(
    krb5_principal principal) const {
  const std::string client_realm = getClientPrincipal().getRealm();
  std::string princ_realm;
  krb5_context ctx = context_.get();
  if (principal) {
    Krb5Principal princ(ctx, std::move(principal));
    princ_realm = princ.getRealm();
    princ.release();
  } else {
    princ_realm = client_realm;
  }

  for (auto& creds : *this) {
    Krb5Principal server(ctx, std::move(creds.server));
    if (server.isTgt() &&
        server.getComponent(1) == princ_realm &&
        server.getRealm() == client_realm) {
      return std::make_pair(creds.times.starttime, creds.times.endtime);
    }
  }

  return std::make_pair(0, 0);
}