[augeas-devel] [PATCH] Add Httpd lens

Francis Giraldeau francis.giraldeau at gmail.com
Sun Jan 23 06:32:14 UTC 2011


The Httpd lens parses Apache Web server configuration according to the manual.

  * Generic sections with square lens
  * Generic directives
  * Arguments of sections and directives are handled as list

The lens doesn't support automatic quoting of string values, because arguments
are separated by spaces and it raises ambiguity when using unquoting. The lens
doesn't support either long lines splitted by backslash.
---
 lenses/httpd.aug            |   89 ++++++++++++
 lenses/tests/test_httpd.aug |  328 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 417 insertions(+), 0 deletions(-)
 create mode 100644 lenses/httpd.aug
 create mode 100644 lenses/tests/test_httpd.aug

diff --git a/lenses/httpd.aug b/lenses/httpd.aug
new file mode 100644
index 0000000..1f4a814
--- /dev/null
+++ b/lenses/httpd.aug
@@ -0,0 +1,89 @@
+(* Apache HTTPD lens for Augeas
+
+Author: Francis Giraldeau <francis.giraldeau at usherbrooke.ca>
+
+About: Reference
+  Online Apache configuration manual: http://httpd.apache.org/docs/trunk/
+
+About: License
+    This file is licensed under the GPL.
+
+About: Lens Usage
+  Sample usage of this lens in augtool
+
+  Apache configuration is represented by two main structures, nested sections
+  and directives. Sections are used as labels, while directives are kept as a
+  value. Sections and directives can have positional arguments inside values
+  of "arg" nodes. Arguments of sections must be the firsts child of the
+  section node.
+
+  This lens doesn't support automatic string quoting. Hence, the string must
+  be quoted when containing a space.
+
+  Create a new VirtualHost section with one directive:
+  > clear /files/etc/apache2/sites-available/foo/VirtualHost
+  > set /files/etc/apache2/sites-available/foo/VirtualHost/arg "172.16.0.1:80"
+  > set /files/etc/apache2/sites-available/foo/VirtualHost/directive "ServerAdmin"
+  > set /files/etc/apache2/sites-available/foo/VirtualHost/*[self::directive="ServerAdmin"]/arg "admin at example.com"
+
+About: Configuration files
+  This lens applies to /etc/dhcpd3/dhcpd.conf. See <filter>.
+
+*)
+
+
+module Httpd =
+
+autoload xfm
+
+(******************************************************************
+ *                           Utilities lens
+ *****************************************************************)
+let dels (s:string)     = del s s
+let sep_spc             = del /[ \t]+/ " "
+let sep_osp             = del /[ \t]*/ ""
+let sep_eq              = del /[ \t]*=[ \t]*/ "="
+
+let nmtoken             = /[a-zA-Z:_][a-zA-Z0-9:_\.-]*/
+let word                = /[a-zA-Z][a-zA-Z0-9\._\-]*/
+
+let comment             = Util.comment
+let eol                 = Util.eol
+let empty               = Util.empty
+let indent              = Util.indent
+
+(* borrowed from shellvars.aug *)
+let char_arg_dir  = /[^ '"\t\n]|\\\\"/
+let char_arg_sec  = /[^ '"\t\n>]|\\\\"/
+let dquot = /"([^"\\\n]|\\\\.)*"/
+
+(******************************************************************
+ *                            Attributes
+ *****************************************************************)
+
+let arg_dir = [ label "arg" . store (char_arg_dir+|dquot) ]
+let arg_sec = [ label "arg" . store (char_arg_sec+|dquot) ]
+
+let argv (l:lens) = l . (sep_spc . l)*
+
+let directive = [ indent . label "directive" . store word .
+                  sep_spc . argv arg_dir . eol ]
+
+let section (body:lens) =
+    let h = (sep_spc . argv arg_sec)? . sep_osp .
+             dels ">" . eol . body* . indent . dels "</" in
+        [ indent . dels "<" . square word h . del ">" ">" . eol ]
+
+let rec content = section (content|directive|comment|empty)
+
+let lns = (content|directive|comment|empty)*
+
+let filter = (incl "/etc/apache2/apache2.conf") .
+             (incl "/etc/apache2/httpd.conf") .
+             (incl "/etc/apache2/ports.conf") .
+             (incl "/etc/apache2/conf.d/*") .
+             (incl "/etc/apache2/mods-available/*") .
+             (incl "/etc/apache2/sites-available/*") .
+             Util.stdexcl
+
+let xfm = transform lns filter
diff --git a/lenses/tests/test_httpd.aug b/lenses/tests/test_httpd.aug
new file mode 100644
index 0000000..577264e
--- /dev/null
+++ b/lenses/tests/test_httpd.aug
@@ -0,0 +1,328 @@
+module Test_httpd =
+
+(* directives testing *)
+let d1 = "ServerRoot \"/etc/apache2\"\n"
+test Httpd.directive get d1 =
+  { "directive" = "ServerRoot"
+    { "arg" = "\"/etc/apache2\"" }
+  }
+
+let d2 = "ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/\n"
+test Httpd.directive get d2 =
+  { "directive" = "ScriptAlias"
+    { "arg" = "/cgi-bin/" }
+    { "arg" = "/usr/lib/cgi-bin/" }
+  }
+
+let d3 = "LockFile /var/lock/apache2/accept.lock\n"
+test Httpd.directive get d3 =
+  { "directive" = "LockFile"
+    { "arg" = "/var/lock/apache2/accept.lock" }
+  }
+
+let c1 = "
+<IfModule>
+</IfModule>
+"
+let c1_put =
+"
+<IfModule foo bar>
+</IfModule>
+"
+
+
+test Httpd.lns get c1 = { }{ "IfModule" }
+
+test Httpd.lns put c1 after set "/IfModule/arg[1]" "foo";
+                            set "/IfModule/arg[2]" "bar" = c1_put
+
+let c2 = "
+<IfModule !mpm_winnt.c>
+  <IfModule !mpm_netware.c>
+    LockFile /var/lock/apache2/accept.lock
+  </IfModule>
+</IfModule>
+"
+
+test Httpd.lns get c2 =
+  {  }
+  { "IfModule"
+    { "arg" = "!mpm_winnt.c" }
+    { "IfModule"
+      { "arg" = "!mpm_netware.c" }
+      { "directive" = "LockFile"
+        { "arg" = "/var/lock/apache2/accept.lock" }
+      }
+    }
+  }
+
+(* arguments must be the first child of the section *)
+test Httpd.lns put c2 after rm "/IfModule/arg";
+                            insb "arg" "/IfModule/*[1]";
+                            set "/IfModule/arg" "foo"  =
+"
+<IfModule foo>
+  <IfModule !mpm_netware.c>
+    LockFile /var/lock/apache2/accept.lock
+  </IfModule>
+</IfModule>
+"
+
+let c3 = "
+<IfModule mpm_event_module>
+    StartServers          2
+    MaxClients          150
+    MinSpareThreads      25
+    MaxSpareThreads      75
+    ThreadLimit          64
+    ThreadsPerChild      25
+    MaxRequestsPerChild   0
+</IfModule>
+"
+
+test Httpd.lns get c3 =
+  {  }
+  { "IfModule"
+    { "arg" = "mpm_event_module" }
+    { "directive" = "StartServers"
+      { "arg" = "2" }
+    }
+    { "directive" = "MaxClients"
+      { "arg" = "150" }
+    }
+    { "directive" = "MinSpareThreads"
+      { "arg" = "25" }
+    }
+    { "directive" = "MaxSpareThreads"
+      { "arg" = "75" }
+    }
+    { "directive" = "ThreadLimit"
+      { "arg" = "64" }
+    }
+    { "directive" = "ThreadsPerChild"
+      { "arg" = "25" }
+    }
+    { "directive" = "MaxRequestsPerChild"
+      { "arg" = "0" }
+    }
+  }
+
+
+
+let c4 = "
+<Files ~ \"^\.ht\">
+    Order allow,deny
+    Deny from all
+    Satisfy all
+</Files>
+"
+
+test Httpd.lns get c4 =
+  {  }
+  { "Files"
+    { "arg" = "~" }
+    { "arg" = "\"^\.ht\"" }
+    { "directive" = "Order"
+      { "arg" = "allow,deny" }
+    }
+    { "directive" = "Deny"
+      { "arg" = "from" }
+      { "arg" = "all" }
+    }
+    { "directive" = "Satisfy"
+      { "arg" = "all" }
+    }
+  }
+
+
+
+let c5 = "LogFormat \"%{User-agent}i\" agent\n"
+test Httpd.lns get c5 =
+  { "directive" = "LogFormat"
+    { "arg" = "\"%{User-agent}i\"" }
+    { "arg" = "agent" }
+  }
+
+let c7 = "LogFormat \"%v:%p %h %l %u %t \\"%r\\" %>s %O \\"%{Referer}i\\" \\"%{User-Agent}i\\"\" vhost_combined\n"
+test Httpd.lns get c7 =
+  { "directive" = "LogFormat"
+    { "arg" = "\"%v:%p %h %l %u %t \\"%r\\" %>s %O \\"%{Referer}i\\" \\"%{User-Agent}i\\"\"" }
+    { "arg" = "vhost_combined" }
+  }
+
+let c8 = "IndexIgnore .??* *~ *# RCS CVS *,v *,t \n"
+test Httpd.directive get c8 =
+  { "directive" = "IndexIgnore"
+    { "arg" = ".??*" }
+    { "arg" = "*~" }
+    { "arg" = "*#" }
+    { "arg" = "RCS" }
+    { "arg" = "CVS" }
+    { "arg" = "*,v" }
+    { "arg" = "*,t" }
+  }
+
+(* FIXME: not yet supported:
+ * The backslash "\" may be used as the last character on a line to indicate
+ * that the directive continues onto the next line. There must be no other
+ * characters or white space between the backslash and the end of the line.
+ *)
+test Httpd.directive get "Options Indexes \
+FollowSymLinks MultiViews\n" = *
+(*
+  { "directive" = "Options"
+    { "arg" = "Indexes" }
+    { "arg" = "FollowSymLinks" }
+    { "arg" = "MultiViews" }
+  }
+*)
+
+let conf2 = "<VirtualHost *:80>
+    ServerAdmin webmaster at localhost
+
+    DocumentRoot /var/www
+    <Directory />
+        Options FollowSymLinks
+        AllowOverride None
+    </Directory>
+    <Directory /var/www/>
+        Options Indexes FollowSymLinks MultiViews
+        AllowOverride None
+        Order allow,deny
+        allow from all
+    </Directory>
+
+    ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
+    <Directory \"/usr/lib/cgi-bin\">
+        AllowOverride None
+        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
+        Order allow,deny
+        Allow from all
+    </Directory>
+
+    ErrorLog /var/log/apache2/error.log
+
+    # Possible values include: debug, info, notice, warn, error, crit,
+    # alert, emerg.
+    LogLevel warn
+
+    CustomLog /var/log/apache2/access.log combined
+
+    Alias /doc/ \"/usr/share/doc/\"
+    <Directory \"/usr/share/doc/\">
+        Options Indexes MultiViews FollowSymLinks
+        AllowOverride None
+        Order deny,allow
+        Deny from all
+        Allow from 127.0.0.0/255.0.0.0 ::1/128
+    </Directory>
+
+</VirtualHost>
+"
+
+test Httpd.lns get conf2 =
+   { "VirtualHost"
+    { "arg" = "*:80" }
+    { "directive" = "ServerAdmin"
+      { "arg" = "webmaster at localhost" }
+    }
+    {  }
+    { "directive" = "DocumentRoot"
+      { "arg" = "/var/www" }
+    }
+    { "Directory"
+      { "arg" = "/" }
+      { "directive" = "Options"
+        { "arg" = "FollowSymLinks" }
+      }
+      { "directive" = "AllowOverride"
+        { "arg" = "None" }
+      }
+    }
+    { "Directory"
+      { "arg" = "/var/www/" }
+      { "directive" = "Options"
+        { "arg" = "Indexes" }
+        { "arg" = "FollowSymLinks" }
+        { "arg" = "MultiViews" }
+      }
+      { "directive" = "AllowOverride"
+        { "arg" = "None" }
+      }
+      { "directive" = "Order"
+        { "arg" = "allow,deny" }
+      }
+      { "directive" = "allow"
+        { "arg" = "from" }
+        { "arg" = "all" }
+      }
+    }
+    {  }
+    { "directive" = "ScriptAlias"
+      { "arg" = "/cgi-bin/" }
+      { "arg" = "/usr/lib/cgi-bin/" }
+    }
+    { "Directory"
+      { "arg" = "\"/usr/lib/cgi-bin\"" }
+      { "directive" = "AllowOverride"
+        { "arg" = "None" }
+      }
+      { "directive" = "Options"
+        { "arg" = "+ExecCGI" }
+        { "arg" = "-MultiViews" }
+        { "arg" = "+SymLinksIfOwnerMatch" }
+      }
+      { "directive" = "Order"
+        { "arg" = "allow,deny" }
+      }
+      { "directive" = "Allow"
+        { "arg" = "from" }
+        { "arg" = "all" }
+      }
+    }
+    {  }
+    { "directive" = "ErrorLog"
+      { "arg" = "/var/log/apache2/error.log" }
+    }
+    {  }
+    { "#comment" = "Possible values include: debug, info, notice, warn, error, crit," }
+    { "#comment" = "alert, emerg." }
+    { "directive" = "LogLevel"
+      { "arg" = "warn" }
+    }
+    {  }
+    { "directive" = "CustomLog"
+      { "arg" = "/var/log/apache2/access.log" }
+      { "arg" = "combined" }
+    }
+    {  }
+    { "directive" = "Alias"
+      { "arg" = "/doc/" }
+      { "arg" = "\"/usr/share/doc/\"" }
+    }
+    { "Directory"
+      { "arg" = "\"/usr/share/doc/\"" }
+      { "directive" = "Options"
+        { "arg" = "Indexes" }
+        { "arg" = "MultiViews" }
+        { "arg" = "FollowSymLinks" }
+      }
+      { "directive" = "AllowOverride"
+        { "arg" = "None" }
+      }
+      { "directive" = "Order"
+        { "arg" = "deny,allow" }
+      }
+      { "directive" = "Deny"
+        { "arg" = "from" }
+        { "arg" = "all" }
+      }
+      { "directive" = "Allow"
+        { "arg" = "from" }
+        { "arg" = "127.0.0.0/255.0.0.0" }
+        { "arg" = "::1/128" }
+      }
+    }
+    {  }
+  }
+
-- 
1.7.1




More information about the augeas-devel mailing list