Support include directive in pgppol.conf file
authorTakuma Hoshiai <takuma.hoshiai@gmail.com>
Tue, 31 Aug 2021 03:15:12 +0000 (12:15 +0900)
committerTakuma Hoshiai <takuma.hoshiai@gmail.com>
Tue, 31 Aug 2021 03:15:12 +0000 (12:15 +0900)
The pgpool.conf can be nested by 'include' directive.A depth of include have a
restriction for avoiding loop back of include files. 'include' is not regular
parameter, so SHOW command don't display it.

doc.ja/src/sgml/config.sgml
doc/src/sgml/config.sgml
src/config/pool_config.c
src/config/pool_config.l

index a2a3ea2d2305961e529d8bc7773f6e50c751a148..d885682094ca1a63da5c9454182f5fb24de1f7d6 100644 (file)
    </para>
   </sect2>
 
+  <sect2 id="config-setting-includes">
+   <!--
+   <title>Managing Configuration File Contents</title>
+   -->
+   <title>設定ファイルの内容の管理</title>
+
+   <para>
+    <!--
+    <productname>Pgpool-II</productname> provides a feature for breaking down
+    <filename>pgpool.conf</filename> files into sub-files. This feature is
+    especially useful when managing multiple servers or dividing by features.
+    <literal>include</literal> directive can be used like this:
+    -->
+    <productname>Pgpool-II</productname>は<filename>pgpool.conf</filename>ファイルを複数のファイルに分割して記述する方法を提供しています。
+    機能やサーバ固有のパラメータの設定を分割したい場合に有用です。
+    次のように<literal>include</literal>ディレクティブを追加します。
+    <programlisting>
+     include = 'filename'
+    </programlisting>
+    <!--
+    If the file name is not an absolute path, it is taken as relative
+    to the directory containing the referencing configuration file.
+    Inclusions can be nested.
+    -->
+    ファイル名が絶対パスでない場合、参照する設定ファイルを含むディレクトリからの相対パスであると受け取られます。includeコマンドは入れ子にすることができます。
+   </para>
+  </sect2>
+
   <sect2 id="config-setting-sql-command-interaction">
    <!--
    <title>Parameter Interaction via SQL Clients</title>
index 730cf7e40802be2c5f99070b62dde7ea679fd18e..542217d09d8a60914b9344686239d1d04071fac0 100644 (file)
    </para>
   </sect2>
 
+  <sect2 id="config-setting-includes">
+   <title>Managing Configuration File Contents</title>
+
+   <para>
+    <productname>Pgpool-II</productname> provides a feature for breaking down
+    <filename>pgpool.conf</filename> files into sub-files. This feature is
+    especially useful when managing multiple servers or dividing by features.
+    <literal>include</literal> directive can be used like this:
+
+    <programlisting>
+     include = 'filename'
+    </programlisting>
+
+    If the file name is not an absolute path, it is taken as relative
+    to the directory containing the referencing configuration file.
+    Inclusions can be nested.
+   </para>
+  </sect2>
+
   <sect2 id="config-setting-sql-command-interaction">
    <title>Parameter Interaction via SQL Clients</title>
 
index a238874639ffc4d83d3e8fae7c2ef96e5deb3664..5b1cd2c3911d4842845e62f79af8b798422ca584 100644 (file)
@@ -550,8 +550,8 @@ typedef enum {
 
 static char *extract_string(char *value, POOL_TOKEN token);
 static void FreeConfigVariable(ConfigVariable *item);
-static bool ParseConfigFile( const char *config_file, int elevel,
-                       ConfigVariable **head_p, ConfigVariable **tail_p);
+static bool ParseConfigFile(const char *config_file, const char *calling_file,
+                               int depth, int elevel, ConfigVariable **head_p, ConfigVariable **tail_p);
 
 #define YY_NO_INPUT 1
 #line 558 "config/pool_config.c"
@@ -2042,6 +2042,9 @@ FreeConfigVariables(ConfigVariable *list)
  *
  * Input parameters:
  *  config_file: absolute or relative path name of the configuration file
+ *  calling_file: absolute path of file containing the "include" directive,
+ *    or NULL at outer level (config_file must be absolute at outer level)
+ *  depth: recursion depth (used only to prevent infinite recursion)
  *  elevel: error logging level to use
  * Input/Output parameters:
  *  head_p, tail_p: head and tail of linked list of name/value pairs
@@ -2055,34 +2058,66 @@ FreeConfigVariables(ConfigVariable *list)
  *
  */
 static bool
-ParseConfigFile(const char *config_file, int elevel,
-                               ConfigVariable **head_p, ConfigVariable **tail_p)
+ParseConfigFile(const char *config_file, const char *calling_file,
+                               int depth, int elevel, ConfigVariable **head_p,
+                               ConfigVariable **tail_p)
 {
 
        FILE *fd;
+       YY_BUFFER_STATE lex_buffer;
        int token;
        char *key;
        char *val;
        ConfigVariable *item;
        char buf[POOLMAXPATHLEN + 1];
+       char *config_filepath;
+
+       /*
+        * Reject too-deep include nesting depth.
+        */
+       if (depth > 10)
+               ereport(elevel,
+                               (errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
+                                                               config_file)));
+
+       if (depth == 0)
+       {
+               /* get directory path of config file pgpool.conf */
+               strlcpy(buf, config_file, sizeof(buf));
+               get_parent_directory(buf);
+               config_file_dir = buf;
+       }
 
-       *head_p = NULL;
 
-       /* get directory path of config file pgpool.conf */
-       strlcpy(buf, config_file, sizeof(buf));
-       get_parent_directory(buf);
-       config_file_dir = buf;
+       if (calling_file == NULL || is_absolute_path(config_file))
+       {
+               /* absolute path is taken as-is */
+               config_filepath = pstrdup(config_file);
+       }
+       else
+       {
+               /* relative path is relative to dir of calling file */
+               config_filepath = (char *) palloc(strlen(config_file) + 1 +
+                                                                          strlen(calling_file) + 1);
+               strcpy(config_filepath, calling_file);
+               get_parent_directory(config_filepath);
+               join_path_components(config_filepath, config_filepath, config_file);
+               canonicalize_path(config_filepath);
+       }
 
        /* open config file */
-       fd = fopen(config_file, "r");
+       fd = fopen(config_filepath, "r");
        if (!fd)
        {
                ereport(WARNING,
-                       (errmsg("could not open configuration file: \"%s\"",config_file),
+                       (errmsg("could not open configuration file: \"%s\"",config_filepath),
                                errdetail("using default configuration parameter values")));
                return false;
        }
 
+       lex_buffer = yy_create_buffer(fd,YY_BUF_SIZE);
+       yy_switch_to_buffer(lex_buffer);
+
        yyin = fd;
        Lineno = 1;
 
@@ -2115,17 +2150,36 @@ ParseConfigFile(const char *config_file, int elevel,
                ereport(DEBUG5,
                        (errmsg("key: \"%s\" value: \"%s\" kind: %d",key, val, token)));
 
-               /* Add this to the list */
-               item = palloc(sizeof(ConfigVariable));
-               item->name = key;
-               item->value = val;
-               item->sourceline = Lineno;
-               item->next = NULL;
-               if (*head_p == NULL)
-                       *head_p = item;
+               if (strcmp(key, "include") == 0)
+               {
+                       /*
+                        * An include directive isn't a variable and should be processed
+                        * immediately.
+                        */
+                       unsigned save_Lineno = Lineno;
+
+                       if (!ParseConfigFile(val, config_filepath, depth + 1, elevel, head_p, tail_p))
+                       {
+                               goto parse_error;
+                       }
+                       yy_switch_to_buffer(lex_buffer);
+                       Lineno = save_Lineno;
+               }
                else
-                       (*tail_p)->next = item;
-               *tail_p = item;
+               {
+                       /* Add this to the list */
+                       item = palloc(sizeof(ConfigVariable));
+                       item->name = key;
+                       item->value = val;
+                       item->sourceline = Lineno;
+                       item->next = NULL;
+
+                       if (*head_p == NULL)
+                               *head_p = item;
+                       else
+                               (*tail_p)->next = item;
+                       *tail_p = item;
+               }
        }
 
        fclose(fd);
@@ -2138,7 +2192,7 @@ parse_error:
        *head_p = NULL;
        *tail_p = NULL;
        ereport(elevel,
-               (errmsg("syntax error in configuration file \"%s\"",config_file),
+               (errmsg("syntax error in configuration file \"%s\"", config_filepath),
                        errdetail("parse error at line %d '%s' token = %d", Lineno, yytext,token)));
 
        return false;
@@ -2154,7 +2208,7 @@ bool pool_get_config(const char *config_file, ConfigContext context)
        bool res;
        int elevel = (context == CFGCXT_INIT)?FATAL:WARNING;
 
-       res = ParseConfigFile(config_file, elevel, &head_p, &tail_p);
+       res = ParseConfigFile(config_file, NULL, 0, elevel, &head_p, &tail_p);
        if (res == false || head_p == NULL)
                return false;
 
index 3451279f1d94bcb4ab5a6c50aa5d0607cc6d3ca4..1cbe22ec79ac9a230309ee03681a8c4470f9fb59 100644 (file)
@@ -60,8 +60,8 @@ typedef enum {
 
 static char *extract_string(char *value, POOL_TOKEN token);
 static void FreeConfigVariable(ConfigVariable *item);
-static bool ParseConfigFile( const char *config_file, int elevel,
-                       ConfigVariable **head_p, ConfigVariable **tail_p);
+static bool ParseConfigFile(const char *config_file, const char *calling_file,
+                               int depth, int elevel, ConfigVariable **head_p, ConfigVariable **tail_p);
 
 %}
 
@@ -311,6 +311,9 @@ FreeConfigVariables(ConfigVariable *list)
  *
  * Input parameters:
  *  config_file: absolute or relative path name of the configuration file
+ *  calling_file: absolute path of file containing the "include" directive,
+ *    or NULL at outer level (config_file must be absolute at outer level)
+ *  depth: recursion depth (used only to prevent infinite recursion)
  *  elevel: error logging level to use
  * Input/Output parameters:
  *  head_p, tail_p: head and tail of linked list of name/value pairs
@@ -324,34 +327,66 @@ FreeConfigVariables(ConfigVariable *list)
  *
  */
 static bool
-ParseConfigFile(const char *config_file, int elevel,
-                               ConfigVariable **head_p, ConfigVariable **tail_p)
+ParseConfigFile(const char *config_file, const char *calling_file,
+                               int depth, int elevel, ConfigVariable **head_p,
+                               ConfigVariable **tail_p)
 {
 
        FILE *fd;
+       YY_BUFFER_STATE lex_buffer;
        int token;
        char *key;
        char *val;
        ConfigVariable *item;
        char buf[POOLMAXPATHLEN + 1];
+       char *config_filepath;
 
-       *head_p = NULL;
+       /*
+        * Reject too-deep include nesting depth.
+        */
+       if (depth > 10)
+               ereport(elevel,
+                               (errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
+                                                               config_file)));
+
+       if (depth == 0)
+       {
+               /* get directory path of config file pgpool.conf */
+               strlcpy(buf, config_file, sizeof(buf));
+               get_parent_directory(buf);
+               config_file_dir = buf;
+       }
 
-       /* get directory path of config file pgpool.conf */
-       strlcpy(buf, config_file, sizeof(buf));
-       get_parent_directory(buf);
-       config_file_dir = buf;
+
+       if (calling_file == NULL || is_absolute_path(config_file))
+       {
+               /* absolute path is taken as-is */
+               config_filepath = pstrdup(config_file);
+       }
+       else
+       {
+               /* relative path is relative to dir of calling file */
+               config_filepath = (char *) palloc(strlen(config_file) + 1 +
+                                                                          strlen(calling_file) + 1);
+               strcpy(config_filepath, calling_file);
+               get_parent_directory(config_filepath);
+               join_path_components(config_filepath, config_filepath, config_file);
+               canonicalize_path(config_filepath);
+       }
 
        /* open config file */
-       fd = fopen(config_file, "r");
+       fd = fopen(config_filepath, "r");
        if (!fd)
        {
                ereport(WARNING,
-                       (errmsg("could not open configuration file: \"%s\"",config_file),
+                       (errmsg("could not open configuration file: \"%s\"",config_filepath),
                                errdetail("using default configuration parameter values")));
                return false;
        }
 
+       lex_buffer = yy_create_buffer(fd, YY_BUF_SIZE);
+       yy_switch_to_buffer(lex_buffer);
+
        yyin = fd;
        Lineno = 1;
 
@@ -384,17 +419,36 @@ ParseConfigFile(const char *config_file, int elevel,
                ereport(DEBUG5,
                        (errmsg("key: \"%s\" value: \"%s\" kind: %d",key, val, token)));
 
-               /* Add this to the list */
-               item = palloc(sizeof(ConfigVariable));
-               item->name = key;
-               item->value = val;
-               item->sourceline = Lineno;
-               item->next = NULL;
-               if (*head_p == NULL)
-                       *head_p = item;
+               if (strcmp(key, "include") == 0)
+               {
+                       /*
+                        * An include directive isn't a variable and should be processed
+                        * immediately.
+                        */
+                       unsigned save_Lineno = Lineno;
+
+                       if (!ParseConfigFile(val, config_filepath, depth + 1, elevel, head_p, tail_p))
+                       {
+                               goto parse_error;
+                       }
+                       yy_switch_to_buffer(lex_buffer);
+                       Lineno = save_Lineno;
+               }
                else
-                       (*tail_p)->next = item;
-               *tail_p = item;
+               {
+                       /* Add this to the list */
+                       item = palloc(sizeof(ConfigVariable));
+                       item->name = key;
+                       item->value = val;
+                       item->sourceline = Lineno;
+                       item->next = NULL;
+
+                       if (*head_p == NULL)
+                               *head_p = item;
+                       else
+                               (*tail_p)->next = item;
+                       *tail_p = item;
+               }
        }
 
        fclose(fd);
@@ -407,7 +461,7 @@ parse_error:
        *head_p = NULL;
        *tail_p = NULL;
        ereport(elevel,
-               (errmsg("syntax error in configuration file \"%s\"",config_file),
+               (errmsg("syntax error in configuration file \"%s\"", config_filepath),
                        errdetail("parse error at line %d '%s' token = %d", Lineno, yytext,token)));
 
        return false;
@@ -423,7 +477,7 @@ bool pool_get_config(const char *config_file, ConfigContext context)
        bool res;
        int elevel = (context == CFGCXT_INIT)?FATAL:WARNING;
 
-       res = ParseConfigFile(config_file, elevel, &head_p, &tail_p);
+       res = ParseConfigFile(config_file, NULL, 0, elevel, &head_p, &tail_p);
        if (res == false || head_p == NULL)
                return false;