Return PamConfigEntries it finds as it recursively follows PAM configs.

    Args:
      service: A string containing the service name we are processing.
      path: A string containing the file path name we want.
      cache: A dictionary keyed on path, with the file contents (list of str).
      filter_type: A string containing type name of the results we want.

    Returns:
      A tuple of a list of RDFValue PamConfigEntries found & a list of strings
      which are the external config references found.

def EnumerateConfig(self, service, path, cache, filter_type=None):
    """Return PamConfigEntries it finds as it recursively follows PAM configs.

    Args:
      service: A string containing the service name we are processing.
      path: A string containing the file path name we want.
      cache: A dictionary keyed on path, with the file contents (list of str).
      filter_type: A string containing type name of the results we want.

    Returns:
      A tuple of a list of RDFValue PamConfigEntries found & a list of strings
      which are the external config references found.
    """

    result = []
    external = []
    path = self._FixPath(path)

    # Make sure we only look at files under PAMDIR.
    # Check we have the file in our artifact/cache. If not, our artifact
    # didn't give it to us, and that's a problem.
    # Note: This should only ever happen if it was referenced
    # from /etc/pam.conf so we can assume that was the file.
    if path not in cache:
      external.append("%s -> %s", self.OLD_PAMCONF_FILENAME, path)
      return result, external

    for tokens in self.ParseEntries(cache[path]):
      if path == self.OLD_PAMCONF_FILENAME:
        # We are processing the old style PAM conf file. It's a special case.
        # It's format is "service type control module-path module-arguments"
        # i.e. the 'service' is the first arg, the rest is line
        # is like everything else except for that addition.
        try:
          service = tokens[0]  # Grab the service from the start line.
          tokens = tokens[1:]  # Make the rest of the line look like "normal".
        except IndexError:
          continue  # It's a blank line, skip it.

      # Process any inclusions in the line.
      new_path = None
      filter_request = None
      try:
        # If a line starts with @include, then include the entire referenced
        # file.
        # e.g. "@include common-auth"
        if tokens[0] == "@include":
          new_path = tokens[1]
        # If a line's second arg is an include/substack, then filter the
        # referenced file only including entries that match the 'type'
        # requested.
        # e.g. "auth include common-auth-screensaver"
        elif tokens[1] in ["include", "substack"]:
          new_path = tokens[2]
          filter_request = tokens[0]
      except IndexError:
        # It's not a valid include line, so keep processing as normal.
        pass

      # If we found an include file, enumerate that file now, and
      # included it where we are in this config file.
      if new_path:
        # Preemptively check to see if we have a problem where the config
        # is referencing a file outside of the expected/defined artifact.
        # Doing it here allows us to produce a better context for the
        # problem. Hence the slight duplication of code.

        new_path = self._FixPath(new_path)
        if new_path not in cache:
          external.append("%s -> %s" % (path, new_path))
          continue  # Skip to the next line of the file.
        r, e = self.EnumerateConfig(service, new_path, cache, filter_request)
        result.extend(r)
        external.extend(e)
      else:
        # If we have been asked to filter on types, skip over any types
        # we are not interested in.
        if filter_type and tokens[0] != filter_type:
          continue  # We can skip this line.

        # If we got here, then we want to include this line in this service's
        # config.

        # Reform the line and break into the correct fields as best we can.
        # Note: ParseEntries doesn't cope with what we need to do.
        match = self.PAMCONF_RE.match(" ".join(tokens))
        if match:
          p_type, control, module_path, module_args = match.group(1, 2, 3, 4)
          # Trim a leading "-" from the type field if present.
          if p_type.startswith("-"):
            p_type = p_type[1:]
          result.append(
              rdf_config_file.PamConfigEntry(
                  service=service,
                  type=p_type,
                  control=control,
                  module_path=module_path,
                  module_args=module_args))
    return result, external