prune_services_on_reload(smartlist_t *old_service_list,
                         smartlist_t *new_service_list)
{
  origin_circuit_t *ocirc = NULL;
  smartlist_t *surviving_services = NULL;

  tor_assert(old_service_list);
  tor_assert(new_service_list);

  /* This contains all _existing_ services that survives the relaod that is
   * that haven't been removed from the configuration. The difference between
   * this list and the new service list is that the new list can possibly
   * contain newly configured service that have no introduction points opened
   * yet nor key material loaded or generated. */
  surviving_services = smartlist_new();

  /* Preserve the existing ephemeral services.
   *
   * This is the ephemeral service equivalent of the "Copy introduction
   * points to new services" block, except there's no copy required since
   * the service structure isn't regenerated.
   *
   * After this is done, all ephemeral services will be:
   *  * Removed from old_service_list, so the equivalent non-ephemeral code
   *    will not attempt to preserve them.
   *  * Added to the new_service_list (that previously only had the
   *    services listed in the configuration).
   *  * Added to surviving_services, which is the list of services that
   *    will NOT have their intro point closed.
   */
  SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) {
    if (rend_service_is_ephemeral(old)) {
      SMARTLIST_DEL_CURRENT(old_service_list, old);
      smartlist_add(surviving_services, old);
      smartlist_add(new_service_list, old);
    }
  } SMARTLIST_FOREACH_END(old);

  /* Copy introduction points to new services. This is O(n^2), but it's only
   * called on reconfigure, so it's ok performance wise. */
  SMARTLIST_FOREACH_BEGIN(new_service_list, rend_service_t *, new) {
    SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) {
      /* Skip ephemeral services as we only want to copy introduction points
       * from current services to newly configured one that already exists.
       * The same directory means it's the same service. */
      if (rend_service_is_ephemeral(new) || rend_service_is_ephemeral(old) ||
          strcmp(old->directory, new->directory)) {
        continue;
      }
      smartlist_add_all(new->intro_nodes, old->intro_nodes);
      smartlist_clear(old->intro_nodes);
      smartlist_add_all(new->expiring_nodes, old->expiring_nodes);
      smartlist_clear(old->expiring_nodes);
      /* This regular service will survive the closing IPs step after. */
      smartlist_add(surviving_services, old);
      break;
    } SMARTLIST_FOREACH_END(old);
  } SMARTLIST_FOREACH_END(new);

  /* For every service introduction circuit we can find, see if we have a
   * matching surviving configured service. If not, close the circuit. */
  while ((ocirc = circuit_get_next_service_intro_circ(ocirc))) {
    int keep_it = 0;
    tor_assert(ocirc->rend_data);
    SMARTLIST_FOREACH_BEGIN(surviving_services, const rend_service_t *, s) {
      if (rend_circuit_pk_digest_eq(ocirc, (uint8_t *) s->pk_digest)) {
        /* Keep this circuit as we have a matching configured service. */
        keep_it = 1;
        break;
      }
    } SMARTLIST_FOREACH_END(s);
    if (keep_it) {
      continue;
    }
    log_info(LD_REND, "Closing intro point %s for service %s.",
             safe_str_client(extend_info_describe(
                                        ocirc->build_state->chosen_exit)),
             safe_str_client(rend_data_get_address(ocirc->rend_data)));
    /* Reason is FINISHED because service has been removed and thus the
     * circuit is considered old/uneeded. */
    circuit_mark_for_close(TO_CIRCUIT(ocirc), END_CIRC_REASON_FINISHED);
  }
  smartlist_free(surviving_services);
}