Now that we have non-Latin1 SGML detection, restore Latin1 chars
[pgsql.git] / src / backend / utils / misc / guc-file.l
blob73be80076da7390b50deb6ef0471e21fcbdc7024
1 %top{
2 /*
3  * Scanner for the configuration file
4  *
5  * Copyright (c) 2000-2024, PostgreSQL Global Development Group
6  *
7  * src/backend/utils/misc/guc-file.l
8  */
10 #include "postgres.h"
12 #include <ctype.h>
13 #include <unistd.h>
15 #include "common/file_utils.h"
16 #include "guc_internal.h"
17 #include "mb/pg_wchar.h"
18 #include "miscadmin.h"
19 #include "storage/fd.h"
20 #include "utils/conffiles.h"
21 #include "utils/memutils.h"
27  * flex emits a yy_fatal_error() function that it calls in response to
28  * critical errors like malloc failure, file I/O errors, and detection of
29  * internal inconsistency.  That function prints a message and calls exit().
30  * Mutate it to instead call our handler, which jumps out of the parser.
31  */
32 #undef fprintf
33 #define fprintf(file, fmt, msg) GUC_flex_fatal(msg)
35 enum
37         GUC_ID = 1,
38         GUC_STRING = 2,
39         GUC_INTEGER = 3,
40         GUC_REAL = 4,
41         GUC_EQUALS = 5,
42         GUC_UNQUOTED_STRING = 6,
43         GUC_QUALIFIED_ID = 7,
44         GUC_EOL = 99,
45         GUC_ERROR = 100
48 static unsigned int ConfigFileLineno;
49 static const char *GUC_flex_fatal_errmsg;
50 static sigjmp_buf *GUC_flex_fatal_jmp;
52 static void FreeConfigVariable(ConfigVariable *item);
54 static int      GUC_flex_fatal(const char *msg);
56 /* LCOV_EXCL_START */
60 %option 8bit
61 %option never-interactive
62 %option nodefault
63 %option noinput
64 %option nounput
65 %option noyywrap
66 %option warn
67 %option prefix="GUC_yy"
70 SIGN                    ("-"|"+")
71 DIGIT                   [0-9]
72 HEXDIGIT                [0-9a-fA-F]
74 UNIT_LETTER             [a-zA-Z]
76 INTEGER                 {SIGN}?({DIGIT}+|0x{HEXDIGIT}+){UNIT_LETTER}*
78 EXPONENT                [Ee]{SIGN}?{DIGIT}+
79 REAL                    {SIGN}?{DIGIT}*"."{DIGIT}*{EXPONENT}?
81 LETTER                  [A-Za-z_\200-\377]
82 LETTER_OR_DIGIT [A-Za-z_0-9\200-\377]
84 ID                              {LETTER}{LETTER_OR_DIGIT}*
85 QUALIFIED_ID    {ID}"."{ID}
87 UNQUOTED_STRING {LETTER}({LETTER_OR_DIGIT}|[-._:/])*
88 STRING                  \'([^'\\\n]|\\.|\'\')*\'
92 \n                              ConfigFileLineno++; return GUC_EOL;
93 [ \t\r]+                /* eat whitespace */
94 #.*                             /* eat comment (.* matches anything until newline) */
96 {ID}                    return GUC_ID;
97 {QUALIFIED_ID}  return GUC_QUALIFIED_ID;
98 {STRING}                return GUC_STRING;
99 {UNQUOTED_STRING} return GUC_UNQUOTED_STRING;
100 {INTEGER}               return GUC_INTEGER;
101 {REAL}                  return GUC_REAL;
102 =                               return GUC_EQUALS;
104 .                               return GUC_ERROR;
108 /* LCOV_EXCL_STOP */
111  * Exported function to read and process the configuration file. The
112  * parameter indicates in what context the file is being read --- either
113  * postmaster startup (including standalone-backend startup) or SIGHUP.
114  * All options mentioned in the configuration file are set to new values.
115  * If a hard error occurs, no values will be changed.  (There can also be
116  * errors that prevent just one value from being changed.)
117  */
118 void
119 ProcessConfigFile(GucContext context)
121         int                     elevel;
122         MemoryContext config_cxt;
123         MemoryContext caller_cxt;
125         /*
126          * Config files are processed on startup (by the postmaster only) and on
127          * SIGHUP (by the postmaster and its children)
128          */
129         Assert((context == PGC_POSTMASTER && !IsUnderPostmaster) ||
130                    context == PGC_SIGHUP);
132         /*
133          * To avoid cluttering the log, only the postmaster bleats loudly about
134          * problems with the config file.
135          */
136         elevel = IsUnderPostmaster ? DEBUG2 : LOG;
138         /*
139          * This function is usually called within a process-lifespan memory
140          * context.  To ensure that any memory leaked during GUC processing does
141          * not accumulate across repeated SIGHUP cycles, do the work in a private
142          * context that we can free at exit.
143          */
144         config_cxt = AllocSetContextCreate(CurrentMemoryContext,
145                                                                            "config file processing",
146                                                                            ALLOCSET_DEFAULT_SIZES);
147         caller_cxt = MemoryContextSwitchTo(config_cxt);
149         /*
150          * Read and apply the config file.  We don't need to examine the result.
151          */
152         (void) ProcessConfigFileInternal(context, true, elevel);
154         /* Clean up */
155         MemoryContextSwitchTo(caller_cxt);
156         MemoryContextDelete(config_cxt);
160  * Read and parse a single configuration file.  This function recurses
161  * to handle "include" directives.
163  * If "strict" is true, treat failure to open the config file as an error,
164  * otherwise just skip the file.
166  * calling_file/calling_lineno identify the source of the request.
167  * Pass NULL/0 if not recursing from an inclusion request.
169  * See ParseConfigFp for further details.  This one merely adds opening the
170  * config file rather than working from a caller-supplied file descriptor,
171  * and absolute-ifying the path name if necessary.
172  */
173 bool
174 ParseConfigFile(const char *config_file, bool strict,
175                                 const char *calling_file, int calling_lineno,
176                                 int depth, int elevel,
177                                 ConfigVariable **head_p,
178                                 ConfigVariable **tail_p)
180         char       *abs_path;
181         bool            OK = true;
182         FILE       *fp;
184         /*
185          * Reject file name that is all-blank (including empty), as that leads to
186          * confusion --- we'd try to read the containing directory as a file.
187          */
188         if (strspn(config_file, " \t\r\n") == strlen(config_file))
189         {
190                 ereport(elevel,
191                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
192                                  errmsg("empty configuration file name: \"%s\"",
193                                                 config_file)));
194                 record_config_file_error("empty configuration file name",
195                                                                  calling_file, calling_lineno,
196                                                                  head_p, tail_p);
197                 return false;
198         }
200         /*
201          * Reject too-deep include nesting depth.  This is just a safety check to
202          * avoid dumping core due to stack overflow if an include file loops back
203          * to itself.  The maximum nesting depth is pretty arbitrary.
204          */
205         if (depth > CONF_FILE_MAX_DEPTH)
206         {
207                 ereport(elevel,
208                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
209                                  errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
210                                                 config_file)));
211                 record_config_file_error("nesting depth exceeded",
212                                                                  calling_file, calling_lineno,
213                                                                  head_p, tail_p);
214                 return false;
215         }
217         abs_path = AbsoluteConfigLocation(config_file, calling_file);
219         /*
220          * Reject direct recursion.  Indirect recursion is also possible, but it's
221          * harder to detect and so doesn't seem worth the trouble.  (We test at
222          * this step because the canonicalization done by AbsoluteConfigLocation
223          * makes it more likely that a simple strcmp comparison will match.)
224          */
225         if (calling_file && strcmp(abs_path, calling_file) == 0)
226         {
227                 ereport(elevel,
228                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
229                                  errmsg("configuration file recursion in \"%s\"",
230                                                 calling_file)));
231                 record_config_file_error("configuration file recursion",
232                                                                  calling_file, calling_lineno,
233                                                                  head_p, tail_p);
234                 pfree(abs_path);
235                 return false;
236         }
238         fp = AllocateFile(abs_path, "r");
239         if (!fp)
240         {
241                 if (strict)
242                 {
243                         ereport(elevel,
244                                         (errcode_for_file_access(),
245                                          errmsg("could not open configuration file \"%s\": %m",
246                                                         abs_path)));
247                         record_config_file_error(psprintf("could not open file \"%s\"",
248                                                                                           abs_path),
249                                                                          calling_file, calling_lineno,
250                                                                          head_p, tail_p);
251                         OK = false;
252                 }
253                 else
254                 {
255                         ereport(LOG,
256                                         (errmsg("skipping missing configuration file \"%s\"",
257                                                         abs_path)));
258                 }
259                 goto cleanup;
260         }
262         OK = ParseConfigFp(fp, abs_path, depth, elevel, head_p, tail_p);
264 cleanup:
265         if (fp)
266                 FreeFile(fp);
267         pfree(abs_path);
269         return OK;
273  * Capture an error message in the ConfigVariable list returned by
274  * config file parsing.
275  */
276 void
277 record_config_file_error(const char *errmsg,
278                                                  const char *config_file,
279                                                  int lineno,
280                                                  ConfigVariable **head_p,
281                                                  ConfigVariable **tail_p)
283         ConfigVariable *item;
285         item = palloc(sizeof *item);
286         item->name = NULL;
287         item->value = NULL;
288         item->errmsg = pstrdup(errmsg);
289         item->filename = config_file ? pstrdup(config_file) : NULL;
290         item->sourceline = lineno;
291         item->ignore = true;
292         item->applied = false;
293         item->next = NULL;
294         if (*head_p == NULL)
295                 *head_p = item;
296         else
297                 (*tail_p)->next = item;
298         *tail_p = item;
302  * Flex fatal errors bring us here.  Stash the error message and jump back to
303  * ParseConfigFp().  Assume all msg arguments point to string constants; this
304  * holds for flex 2.5.35 (earliest we support). Otherwise, we would need to
305  * copy the message.
307  * We return "int" since this takes the place of calls to fprintf().
309 static int
310 GUC_flex_fatal(const char *msg)
312         GUC_flex_fatal_errmsg = msg;
313         siglongjmp(*GUC_flex_fatal_jmp, 1);
314         return 0;                                       /* keep compiler quiet */
318  * Read and parse a single configuration file.  This function recurses
319  * to handle "include" directives.
321  * Input parameters:
322  *      fp: file pointer from AllocateFile for the configuration file to parse
323  *      config_file: absolute or relative path name of the configuration file
324  *      depth: recursion depth (should be CONF_FILE_START_DEPTH in the outermost
325  *      call)
326  *      elevel: error logging level to use
327  * Input/Output parameters:
328  *      head_p, tail_p: head and tail of linked list of name/value pairs
330  * *head_p and *tail_p must be initialized, either to NULL or valid pointers
331  * to a ConfigVariable list, before calling the outer recursion level.  Any
332  * name-value pairs read from the input file(s) will be appended to the list.
333  * Error reports will also be appended to the list, if elevel < ERROR.
335  * Returns TRUE if successful, FALSE if an error occurred.  The error has
336  * already been ereport'd, it is only necessary for the caller to clean up
337  * its own state and release the ConfigVariable list.
339  * Note: if elevel >= ERROR then an error will not return control to the
340  * caller, so there is no need to check the return value in that case.
342  * Note: this function is used to parse not only postgresql.conf, but
343  * various other configuration files that use the same "name = value"
344  * syntax.  Hence, do not do anything here or in the subsidiary routines
345  * ParseConfigFile/ParseConfigDirectory that assumes we are processing
346  * GUCs specifically.
347  */
348 bool
349 ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
350                           ConfigVariable **head_p, ConfigVariable **tail_p)
352         volatile bool OK = true;
353         unsigned int save_ConfigFileLineno = ConfigFileLineno;
354         sigjmp_buf *save_GUC_flex_fatal_jmp = GUC_flex_fatal_jmp;
355         sigjmp_buf      flex_fatal_jmp;
356         volatile YY_BUFFER_STATE lex_buffer = NULL;
357         int                     errorcount;
358         int                     token;
360         if (sigsetjmp(flex_fatal_jmp, 1) == 0)
361                 GUC_flex_fatal_jmp = &flex_fatal_jmp;
362         else
363         {
364                 /*
365                  * Regain control after a fatal, internal flex error.  It may have
366                  * corrupted parser state.  Consequently, abandon the file, but trust
367                  * that the state remains sane enough for yy_delete_buffer().
368                  */
369                 elog(elevel, "%s at file \"%s\" line %u",
370                          GUC_flex_fatal_errmsg, config_file, ConfigFileLineno);
371                 record_config_file_error(GUC_flex_fatal_errmsg,
372                                                                  config_file, ConfigFileLineno,
373                                                                  head_p, tail_p);
374                 OK = false;
375                 goto cleanup;
376         }
378         /*
379          * Parse
380          */
381         ConfigFileLineno = 1;
382         errorcount = 0;
384         lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE);
385         yy_switch_to_buffer(lex_buffer);
387         /* This loop iterates once per logical line */
388         while ((token = yylex()))
389         {
390                 char       *opt_name = NULL;
391                 char       *opt_value = NULL;
392                 ConfigVariable *item;
394                 if (token == GUC_EOL)   /* empty or comment line */
395                         continue;
397                 /* first token on line is option name */
398                 if (token != GUC_ID && token != GUC_QUALIFIED_ID)
399                         goto parse_error;
400                 opt_name = pstrdup(yytext);
402                 /* next we have an optional equal sign; discard if present */
403                 token = yylex();
404                 if (token == GUC_EQUALS)
405                         token = yylex();
407                 /* now we must have the option value */
408                 if (token != GUC_ID &&
409                         token != GUC_STRING &&
410                         token != GUC_INTEGER &&
411                         token != GUC_REAL &&
412                         token != GUC_UNQUOTED_STRING)
413                         goto parse_error;
414                 if (token == GUC_STRING)        /* strip quotes and escapes */
415                         opt_value = DeescapeQuotedString(yytext);
416                 else
417                         opt_value = pstrdup(yytext);
419                 /* now we'd like an end of line, or possibly EOF */
420                 token = yylex();
421                 if (token != GUC_EOL)
422                 {
423                         if (token != 0)
424                                 goto parse_error;
425                         /* treat EOF like \n for line numbering purposes, cf bug 4752 */
426                         ConfigFileLineno++;
427                 }
429                 /* OK, process the option name and value */
430                 if (guc_name_compare(opt_name, "include_dir") == 0)
431                 {
432                         /*
433                          * An include_dir directive isn't a variable and should be
434                          * processed immediately.
435                          */
436                         if (!ParseConfigDirectory(opt_value,
437                                                                           config_file, ConfigFileLineno - 1,
438                                                                           depth + 1, elevel,
439                                                                           head_p, tail_p))
440                                 OK = false;
441                         yy_switch_to_buffer(lex_buffer);
442                         pfree(opt_name);
443                         pfree(opt_value);
444                 }
445                 else if (guc_name_compare(opt_name, "include_if_exists") == 0)
446                 {
447                         /*
448                          * An include_if_exists directive isn't a variable and should be
449                          * processed immediately.
450                          */
451                         if (!ParseConfigFile(opt_value, false,
452                                                                  config_file, ConfigFileLineno - 1,
453                                                                  depth + 1, elevel,
454                                                                  head_p, tail_p))
455                                 OK = false;
456                         yy_switch_to_buffer(lex_buffer);
457                         pfree(opt_name);
458                         pfree(opt_value);
459                 }
460                 else if (guc_name_compare(opt_name, "include") == 0)
461                 {
462                         /*
463                          * An include directive isn't a variable and should be processed
464                          * immediately.
465                          */
466                         if (!ParseConfigFile(opt_value, true,
467                                                                  config_file, ConfigFileLineno - 1,
468                                                                  depth + 1, elevel,
469                                                                  head_p, tail_p))
470                                 OK = false;
471                         yy_switch_to_buffer(lex_buffer);
472                         pfree(opt_name);
473                         pfree(opt_value);
474                 }
475                 else
476                 {
477                         /* ordinary variable, append to list */
478                         item = palloc(sizeof *item);
479                         item->name = opt_name;
480                         item->value = opt_value;
481                         item->errmsg = NULL;
482                         item->filename = pstrdup(config_file);
483                         item->sourceline = ConfigFileLineno - 1;
484                         item->ignore = false;
485                         item->applied = false;
486                         item->next = NULL;
487                         if (*head_p == NULL)
488                                 *head_p = item;
489                         else
490                                 (*tail_p)->next = item;
491                         *tail_p = item;
492                 }
494                 /* break out of loop if read EOF, else loop for next line */
495                 if (token == 0)
496                         break;
497                 continue;
499 parse_error:
500                 /* release storage if we allocated any on this line */
501                 if (opt_name)
502                         pfree(opt_name);
503                 if (opt_value)
504                         pfree(opt_value);
506                 /* report the error */
507                 if (token == GUC_EOL || token == 0)
508                 {
509                         ereport(elevel,
510                                         (errcode(ERRCODE_SYNTAX_ERROR),
511                           errmsg("syntax error in file \"%s\" line %u, near end of line",
512                                          config_file, ConfigFileLineno - 1)));
513                         record_config_file_error("syntax error",
514                                                                          config_file, ConfigFileLineno - 1,
515                                                                          head_p, tail_p);
516                 }
517                 else
518                 {
519                         ereport(elevel,
520                                         (errcode(ERRCODE_SYNTAX_ERROR),
521                          errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
522                                         config_file, ConfigFileLineno, yytext)));
523                         record_config_file_error("syntax error",
524                                                                          config_file, ConfigFileLineno,
525                                                                          head_p, tail_p);
526                 }
527                 OK = false;
528                 errorcount++;
530                 /*
531                  * To avoid producing too much noise when fed a totally bogus file,
532                  * give up after 100 syntax errors per file (an arbitrary number).
533                  * Also, if we're only logging the errors at DEBUG level anyway, might
534                  * as well give up immediately.  (This prevents postmaster children
535                  * from bloating the logs with duplicate complaints.)
536                  */
537                 if (errorcount >= 100 || elevel <= DEBUG1)
538                 {
539                         ereport(elevel,
540                                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
541                            errmsg("too many syntax errors found, abandoning file \"%s\"",
542                                           config_file)));
543                         break;
544                 }
546                 /* resync to next end-of-line or EOF */
547                 while (token != GUC_EOL && token != 0)
548                         token = yylex();
549                 /* break out of loop on EOF */
550                 if (token == 0)
551                         break;
552         }
554 cleanup:
555         yy_delete_buffer(lex_buffer);
556         /* Each recursion level must save and restore these static variables. */
557         ConfigFileLineno = save_ConfigFileLineno;
558         GUC_flex_fatal_jmp = save_GUC_flex_fatal_jmp;
559         return OK;
563  * Read and parse all config files in a subdirectory in alphabetical order
565  * includedir is the absolute or relative path to the subdirectory to scan.
567  * calling_file/calling_lineno identify the source of the request.
568  * Pass NULL/0 if not recursing from an inclusion request.
570  * See ParseConfigFp for further details.
571  */
572 bool
573 ParseConfigDirectory(const char *includedir,
574                                          const char *calling_file, int calling_lineno,
575                                          int depth, int elevel,
576                                          ConfigVariable **head_p,
577                                          ConfigVariable **tail_p)
579         char       *err_msg;
580         char      **filenames;
581         int                     num_filenames;
583         filenames = GetConfFilesInDir(includedir, calling_file, elevel,
584                                                            &num_filenames, &err_msg);
586         if (!filenames)
587         {
588                 record_config_file_error(err_msg, calling_file, calling_lineno, head_p,
589                                                                  tail_p);
590                 return false;
591         }
593         for (int i = 0; i < num_filenames; i++)
594         {
595                 if (!ParseConfigFile(filenames[i], true,
596                                                          calling_file, calling_lineno,
597                                                          depth, elevel,
598                                                          head_p, tail_p))
599                         return false;
600         }
602         return true;
606  * Free a list of ConfigVariables, including the names and the values
607  */
608 void
609 FreeConfigVariables(ConfigVariable *list)
611         ConfigVariable *item;
613         item = list;
614         while (item)
615         {
616                 ConfigVariable *next = item->next;
618                 FreeConfigVariable(item);
619                 item = next;
620         }
624  * Free a single ConfigVariable
625  */
626 static void
627 FreeConfigVariable(ConfigVariable *item)
629         if (item->name)
630                 pfree(item->name);
631         if (item->value)
632                 pfree(item->value);
633         if (item->errmsg)
634                 pfree(item->errmsg);
635         if (item->filename)
636                 pfree(item->filename);
637         pfree(item);
642  *              DeescapeQuotedString
644  * Strip the quotes surrounding the given string, and collapse any embedded
645  * '' sequences and backslash escapes.
647  * The string returned is palloc'd and should eventually be pfree'd by the
648  * caller.
650  * This is exported because it is also used by the bootstrap scanner.
651  */
652 char *
653 DeescapeQuotedString(const char *s)
655         char       *newStr;
656         int                     len,
657                                 i,
658                                 j;
660         /* We just Assert that there are leading and trailing quotes */
661         Assert(s != NULL && s[0] == '\'');
662         len = strlen(s);
663         Assert(len >= 2);
664         Assert(s[len - 1] == '\'');
666         /* Skip the leading quote; we'll handle the trailing quote below */
667         s++, len--;
669         /* Since len still includes trailing quote, this is enough space */
670         newStr = palloc(len);
672         for (i = 0, j = 0; i < len; i++)
673         {
674                 if (s[i] == '\\')
675                 {
676                         i++;
677                         switch (s[i])
678                         {
679                                 case 'b':
680                                         newStr[j] = '\b';
681                                         break;
682                                 case 'f':
683                                         newStr[j] = '\f';
684                                         break;
685                                 case 'n':
686                                         newStr[j] = '\n';
687                                         break;
688                                 case 'r':
689                                         newStr[j] = '\r';
690                                         break;
691                                 case 't':
692                                         newStr[j] = '\t';
693                                         break;
694                                 case '0':
695                                 case '1':
696                                 case '2':
697                                 case '3':
698                                 case '4':
699                                 case '5':
700                                 case '6':
701                                 case '7':
702                                         {
703                                                 int                     k;
704                                                 long            octVal = 0;
706                                                 for (k = 0;
707                                                          s[i + k] >= '0' && s[i + k] <= '7' && k < 3;
708                                                          k++)
709                                                         octVal = (octVal << 3) + (s[i + k] - '0');
710                                                 i += k - 1;
711                                                 newStr[j] = ((char) octVal);
712                                         }
713                                         break;
714                                 default:
715                                         newStr[j] = s[i];
716                                         break;
717                         }                                       /* switch */
718                 }
719                 else if (s[i] == '\'' && s[i + 1] == '\'')
720                 {
721                         /* doubled quote becomes just one quote */
722                         newStr[j] = s[++i];
723                 }
724                 else
725                         newStr[j] = s[i];
726                 j++;
727         }
729         /* We copied the ending quote to newStr, so replace with \0 */
730         Assert(j > 0 && j <= len);
731         newStr[--j] = '\0';
733         return newStr;