@@ -362,6 +362,39 @@ ProcessConfigFile(GucContext context)
362362 }
363363}
364364
365+ /*
366+ * Given a configuration file or directory location that may be a relative
367+ * path, return an absolute one. We consider the location to be relative to
368+ * the directory holding the calling file.
369+ */
370+ static char *
371+ AbsoluteConfigLocation (const char *location, const char *calling_file)
372+ {
373+ char abs_path[MAXPGPATH];
374+
375+ if (is_absolute_path (location))
376+ return pstrdup (location);
377+ else
378+ {
379+ if (calling_file != NULL )
380+ {
381+ strlcpy (abs_path, calling_file, sizeof (abs_path));
382+ get_parent_directory (abs_path);
383+ join_path_components (abs_path, abs_path, location);
384+ canonicalize_path (abs_path);
385+ }
386+ else
387+ {
388+ /*
389+ * calling_file is NULL, we make an absolute path from $PGDATA
390+ */
391+ join_path_components (abs_path, data_directory, location);
392+ canonicalize_path (abs_path);
393+ }
394+ return pstrdup (abs_path);
395+ }
396+ }
397+
365398/*
366399 * Read and parse a single configuration file. This function recurses
367400 * to handle "include" directives.
@@ -378,7 +411,6 @@ ParseConfigFile(const char *config_file, const char *calling_file, bool strict,
378411{
379412 bool OK = true ;
380413 FILE *fp;
381- char abs_path[MAXPGPATH];
382414
383415 /*
384416 * Reject too-deep include nesting depth. This is just a safety check
@@ -394,31 +426,7 @@ ParseConfigFile(const char *config_file, const char *calling_file, bool strict,
394426 return false ;
395427 }
396428
397- /*
398- * If config_file is a relative path, convert to absolute. We consider
399- * it to be relative to the directory holding the calling file.
400- */
401- if (!is_absolute_path (config_file))
402- {
403- if (calling_file != NULL )
404- {
405- strlcpy (abs_path, calling_file, sizeof (abs_path));
406- get_parent_directory (abs_path);
407- join_path_components (abs_path, abs_path, config_file);
408- canonicalize_path (abs_path);
409- config_file = abs_path;
410- }
411- else
412- {
413- /*
414- * calling_file is NULL, we make an absolute path from $PGDATA
415- */
416- join_path_components (abs_path, data_directory, config_file);
417- canonicalize_path (abs_path);
418- config_file = abs_path;
419- }
420- }
421-
429+ config_file = AbsoluteConfigLocation (config_file,calling_file);
422430 fp = AllocateFile (config_file, " r" );
423431 if (!fp)
424432 {
@@ -563,20 +571,35 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
563571 }
564572
565573 /* OK, process the option name and value */
566- if (guc_name_compare (opt_name, " include_if_exists " ) == 0 )
574+ if (guc_name_compare (opt_name, " include_dir " ) == 0 )
567575 {
568576 /*
569- * An include_if_exists directive isn't a variable and should be
577+ * An include_dir directive isn't a variable and should be
570578 * processed immediately.
571579 */
572- if (!ParseConfigFile (opt_value, config_file, false ,
580+ if (!ParseConfigDirectory (opt_value, config_file,
573581 depth + 1 , elevel,
574582 head_p, tail_p))
575583 OK = false ;
576584 yy_switch_to_buffer (lex_buffer);
585+ ConfigFileLineno = save_ConfigFileLineno;
577586 pfree (opt_name);
578587 pfree (opt_value);
579588 }
589+ else if (guc_name_compare (opt_name, " include_if_exists" ) == 0 )
590+ {
591+ /*
592+ * An include_if_exists directive isn't a variable and should be
593+ * processed immediately.
594+ */
595+ if (!ParseConfigFile (opt_value, config_file, false ,
596+ depth + 1 , elevel,
597+ head_p, tail_p))
598+ OK = false ;
599+ yy_switch_to_buffer (lex_buffer);
600+ pfree (opt_name);
601+ pfree (opt_value);
602+ }
580603 else if (guc_name_compare (opt_name, " include" ) == 0 )
581604 {
582605 /*
@@ -665,6 +688,111 @@ cleanup:
665688 return OK;
666689}
667690
691+ /*
692+ * Read and parse all config files in a subdirectory in alphabetical order
693+ */
694+ bool
695+ ParseConfigDirectory (const char *includedir,
696+ const char *calling_file,
697+ int depth, int elevel,
698+ ConfigVariable **head_p,
699+ ConfigVariable **tail_p)
700+ {
701+ char *directory;
702+ DIR *d;
703+ struct dirent *de;
704+ char **filenames = NULL ;
705+ int num_filenames = 0 ;
706+ int size_filenames = 0 ;
707+ bool status;
708+
709+ directory = AbsoluteConfigLocation (includedir, calling_file);
710+ d = AllocateDir (directory);
711+ if (d == NULL )
712+ {
713+ ereport (elevel,
714+ (errcode_for_file_access (),
715+ errmsg (" could not open configuration directory \" %s\" : %m" ,
716+ directory)));
717+ return false ;
718+ }
719+
720+ /*
721+ * Read the directory and put the filenames in an array, so we can sort
722+ * them prior to processing the contents.
723+ */
724+ while ((de = ReadDir (d, directory)) != NULL )
725+ {
726+ struct stat st;
727+ char filename[MAXPGPATH];
728+
729+ /*
730+ * Only parse files with names ending in ".conf". Explicitly reject
731+ * files starting with ".". This excludes things like "." and "..",
732+ * as well as typical hidden files, backup files, and editor debris.
733+ */
734+ if (strlen (de->d_name ) < 6 )
735+ continue ;
736+ if (de->d_name [0 ] == ' .' )
737+ continue ;
738+ if (strcmp (de->d_name + strlen (de->d_name ) - 5 , " .conf" ) != 0 )
739+ continue ;
740+
741+ join_path_components (filename, directory, de->d_name );
742+ canonicalize_path (filename);
743+ if (stat (filename, &st) == 0 )
744+ {
745+ if (!S_ISDIR (st.st_mode ))
746+ {
747+ /* Add file to list, increasing its size in blocks of 32 */
748+ if (num_filenames == size_filenames)
749+ {
750+ size_filenames += 32 ;
751+ if (num_filenames == 0 )
752+ /* Must initialize, repalloc won't take NULL input */
753+ filenames = palloc (size_filenames * sizeof (char *));
754+ else
755+ filenames = repalloc (filenames, size_filenames * sizeof (char *));
756+ }
757+ filenames[num_filenames] = pstrdup (filename);
758+ num_filenames++;
759+ }
760+ }
761+ else
762+ {
763+ /*
764+ * stat does not care about permissions, so the most likely reason
765+ * a file can't be accessed now is if it was removed between the
766+ * directory listing and now.
767+ */
768+ ereport (elevel,
769+ (errcode_for_file_access (),
770+ errmsg (" could not stat file \" %s\" : %m" ,
771+ filename)));
772+ return false ;
773+ }
774+ }
775+
776+ if (num_filenames > 0 )
777+ {
778+ int i;
779+ qsort (filenames, num_filenames, sizeof (char *), pg_qsort_strcmp);
780+ for (i = 0 ; i < num_filenames; i++)
781+ {
782+ if (!ParseConfigFile (filenames[i], NULL , true ,
783+ depth, elevel, head_p, tail_p))
784+ {
785+ status = false ;
786+ goto cleanup;
787+ }
788+ }
789+ }
790+ status = true ;
791+
792+ cleanup:
793+ FreeDir (d);
794+ return status;
795+ }
668796
669797/*
670798 * Free a list of ConfigVariables, including the names and the values
0 commit comments