Add support for -bP config
authorHeiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
Thu, 12 Nov 2015 11:14:14 +0000 (12:14 +0100)
committerHeiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
Thu, 12 Nov 2015 21:46:47 +0000 (22:46 +0100)
doc/doc-docbook/spec.xfpt
src/src/exim.c
src/src/functions.h
src/src/readconf.c

index 54480ee8d01f41177b2a2541ab0a5905fff9fb0b..b5c07d55db69d0c2abbd92cd2f1b8a79357977c9 100644 (file)
@@ -3107,6 +3107,9 @@ users, the output is as in this example:
 .code
 mysql_servers = <value not displayable>
 .endd
+If &%config%& is given as an argument, the config is
+output, as it was parsed, any include file resolved, any comment removed.
+
 If &%configure_file%& is given as an argument, the name of the run time
 configuration file is output.
 If a list of configuration files was supplied, the value that is output here
index 26f415398b945e871ea779cf89e82fa77c1d5568..27b73b764fb3fa1209f5f3ba79be114b56bb3725 100644 (file)
@@ -1492,6 +1492,7 @@ BOOL f_end_dot = FALSE;
 BOOL deliver_give_up = FALSE;
 BOOL list_queue = FALSE;
 BOOL list_options = FALSE;
+BOOL list_config = FALSE;
 BOOL local_queue_only;
 BOOL more = TRUE;
 BOOL one_msg_action = FALSE;
@@ -2156,9 +2157,19 @@ for (i = 1; i < argc; i++)
 
     else if (Ustrcmp(argrest, "P") == 0)
       {
-      list_options = TRUE;
-      debug_selector |= D_v;
-      debug_file = stderr;
+      /* -bP config: we need to setup here, because later,
+       * when list_options is checked, the config is read already */
+      if (argv[i+1] && Ustrcmp(argv[i+1], "config") == 0)
+        {
+        list_config = TRUE;
+        readconf_save_config(version_string);
+        }
+      else
+        {
+        list_options = TRUE;
+        debug_selector |= D_v;
+        debug_file = stderr;
+        }
       }
 
     /* -brt: Test retry configuration lookup */
@@ -4537,6 +4548,13 @@ if (list_options)
   exim_exit(EXIT_SUCCESS);
   }
 
+if (list_config)
+  {
+  set_process_info("listing config");
+  readconf_print(US"config", NULL, FALSE);
+  exim_exit(EXIT_SUCCESS);
+  }
+
 
 /* Handle a request to deliver one or more messages that are already on the
 queue. Values of msg_action other than MSG_DELIVER and MSG_LOAD are dealt with
index 9df8030f51657aad85ba892d1c22cf5de2875b0e..6f6c643b86e2f02acc356592db5353906204aa1b 100644 (file)
@@ -312,8 +312,9 @@ extern void    readconf_print(uschar *, uschar *, BOOL);
 extern uschar *readconf_printtime(int);
 extern uschar *readconf_readname(uschar *, int, uschar *);
 extern int     readconf_readtime(const uschar *, int, BOOL);
-extern void    readconf_rest();
+extern void    readconf_rest(void);
 extern uschar *readconf_retry_error(const uschar *, const uschar *, int *, int *);
+extern void    readconf_save_config(const uschar *);
 extern void    read_message_body(BOOL);
 extern void    receive_bomb_out(uschar *, uschar *);
 extern BOOL    receive_check_fs(int);
index 2ffdd4720d06f580753c57c0dc79fe4b99869f2c..10ebc5e17f89aa40ffb7162788a0b89aab1f4078 100644 (file)
@@ -12,7 +12,9 @@ implementation of the conditional .ifdef etc. */
 #include "exim.h"
 
 static void fn_smtp_receive_timeout(const uschar * name, const uschar * str);
-
+static void save_config_line(const uschar* line);
+static void save_config_position(const uschar *file, int line);
+static void print_config(BOOL admin);
 
 #define CSTATE_STACK_SIZE 10
 
@@ -26,6 +28,15 @@ typedef struct config_file_item {
   int lineno;
 } config_file_item;
 
+/* Structure for chain of configuration lines (-bP config) */
+
+typedef struct config_line_item {
+  struct config_line_item *next;
+  uschar *line;
+} config_line_item;
+
+static config_line_item* config_lines;
+
 /* Structure of table of conditional words and their state transitions */
 
 typedef struct cond_item {
@@ -43,6 +54,8 @@ typedef struct syslog_fac_item {
   int    value;
 } syslog_fac_item;
 
+/* constants */
+static const char * const hidden = "<value not displayable>";
 
 /* Static variables */
 
@@ -697,6 +710,8 @@ for (;;)
       config_filename = config_file_stack->filename;
       config_lineno = config_file_stack->lineno;
       config_file_stack = config_file_stack->next;
+      if (config_lines)
+        save_config_position(config_filename, config_lineno);
       continue;
       }
 
@@ -714,6 +729,9 @@ for (;;)
   config_lineno++;
   newlen = len + Ustrlen(big_buffer + len);
 
+  if (config_lines && config_lineno == 1)
+    save_config_position(config_filename, config_lineno);
+
   /* Handle pathologically long physical lines - yes, it did happen - by
   extending big_buffer at this point. The code also copes with very long
   logical lines. */
@@ -902,6 +920,8 @@ for (;;)
 
     if (include_if_exists != 0 && (Ustat(ss, &statbuf) != 0)) continue;
 
+    if (config_lines)
+      save_config_position(config_filename, config_lineno);
     save = store_get(sizeof(config_file_item));
     save->next = config_file_stack;
     config_file_stack = save;
@@ -958,6 +978,10 @@ next_section, truncate it. It will be unrecognized later, because all the known
 section names do fit. Leave space for pluralizing. */
 
 s = big_buffer + startoffset;            /* First non-space character */
+
+if (config_lines)
+  save_config_line(s);
+
 if (strncmpic(s, US"begin ", 6) == 0)
   {
   s += 6;
@@ -2246,7 +2270,6 @@ if (ol == NULL)
 
 if (!admin_user && (ol->type & opt_secure) != 0)
   {
-  const char * const hidden = "<value not displayable>";
   if (no_labels)
     printf("%s\n", hidden);
   else
@@ -2522,6 +2545,7 @@ second argument is NULL. There are some special values:
   macro_list         print a list of macro names
   +name              print a named list item
   local_scan         print the local_scan options
+  config             print the configuration as it is parsed
 
 If the second argument is not NULL, it must be one of "router", "transport",
 "authenticator" or "macro" in which case the first argument identifies the
@@ -2612,6 +2636,12 @@ if (type == NULL)
     return;
     }
 
+  if (Ustrcmp(name, "config") == 0)
+    {
+    print_config(admin_user);
+    return;
+    }
+
   if (Ustrcmp(name, "routers") == 0)
     {
     type = US"router";
@@ -4197,6 +4227,102 @@ while(next_section[0] != 0)
 (void)fclose(config_file);
 }
 
+/* Init the storage for the pre-parsed config lines */
+void
+readconf_save_config(const uschar *s)
+{
+  save_config_line(string_sprintf("# Exim Configuration (%s)",
+    running_in_test_harness ? US"X" : s));
+}
+
+static void
+save_config_position(const uschar *file, int line)
+{
+  save_config_line(string_sprintf("# %d \"%s\"", line, file));
+}
+
+/* Append a pre-parsed logical line to the config lines store,
+this operates on a global (static) list that holds all the pre-parsed
+config lines */
+static void
+save_config_line(const uschar* line)
+{
+static config_line_item *current;
+config_line_item *next;
+
+next = (config_line_item*) store_get(sizeof(config_line_item));
+next->line = string_copy(line);
+next->next = NULL;
+
+if (!config_lines) config_lines = next;
+else current->next = next;
+
+current = next;
+}
+
+/* List the parsed config lines, care about nice formatting and
+hide the <hide> values unless we're the admin user */
+void
+print_config(BOOL admin)
+{
+config_line_item *i;
+const int TS = 2;
+int indent = 0;
+
+for (i = config_lines; i; i = i->next)
+  {
+  const uschar *current;
+  uschar *p;
+
+  /* skip over to the first non-space */
+  for (current = i->line; *current && isspace(*current); ++current)
+    ;
+
+  if (*current == '\0')
+    continue;
+
+  /* TODO: Collapse or insert spaces around the first '=' */
+
+  /* # lines */
+  if (current[0] == '#')
+    {
+    puts(current);
+    continue;
+    }
+
+  /* begin lines are left aligned */
+  if (strncmp(current, "begin", 5) == 0 && isspace(current[5]))
+    {
+    puts(current);
+    indent = TS;
+    continue;
+    }
+
+  /* router/acl/transport block names */
+  if (current[strlen(current)-1] == ':' && !strchr(current, '='))
+    {
+    printf("%*s%s\n", TS, "", current);
+    indent = 2 * TS;
+    continue;
+    }
+
+  /* as admin we don't care, as we do for "public" lines */
+  if (admin || (!isupper(*current) && (strcmp(current, "hide") != 0)))
+    {
+    printf("%*s%s\n", indent, "", current);
+    continue;
+    }
+
+  /* hidden lines */
+  if (p = strchr(current, '='))
+    {
+    *p = '\0';
+    printf("%*s%s = %s\n", indent, "", current, hidden);
+    continue;
+    }
+  }
+}
+
 /* vi: aw ai sw=2
 */
 /* End of readconf.c */