ACC SHELL
(*
* Module: Xinetd
* Parses xinetd configuration files
*
* The structure of the lens and allowed attributes are ripped directly
* from xinetd's parser in xinetd/parse.c in xinetd's source checkout
* The downside of being so precise here is that if attributes are added
* they need to be added here, too. Writing a catchall entry, and getting
* to typecheck correctly would be a huge pain.
*
* A really enterprising soul could tighten this down even further by
* restricting the acceptable values for each attribute.
*
* Author: David Lutterkort
*)
module Xinetd =
autoload xfm
let comment = [ del /[ \t]*(#.*|[ \t]*)\n/ "#\n" ]
let name = key /[^ \t\n\/+-=]+/
let bol_spc = del /[ \t]*/ "\t"
let spc = del /[ \t]+/ " "
let eol = del /[ \t]*\n/ "\n"
let op_delim (op:string) = del (/[ \t]*/ . op . /[ \t]*/) (" " . op . " ")
let eq = op_delim "="
let op = ([ label "add" . op_delim "+=" ]
|[ label "del" . op_delim "-=" ]
| eq)
let value = store /[^ \t\n]+/
let attr_one (n:regexp) =
[ bol_spc . key n . eq . value . eol ]
let attr_lst (n:regexp) (op_eq: lens) =
[ bol_spc . key n . op_eq .
[label "value" . value] . [label "value" . spc . value]* .
eol ]
let attr_lst_eq (n:regexp) = attr_lst n eq
let attr_lst_op (n:regexp) = attr_lst n op
(* Variable: service_attr
* Note:
* It is much faster to combine, for example, all the attr_one
* attributes into one regexp and pass that to a lens instead of
* using lens union (attr_one "a" | attr_one "b"|..) because the latter
* causes the type checker to work _very_ hard.
*)
let service_attr =
attr_one ("socket_type" | "protocol" | "wait" | "user" | "group"
|"server" | "instances" | "rpc_version" | "rpc_number"
| "id" | "port" | "nice" | "banner" | "bind" | "interface"
| "per_source" | "groups" | "banner_success" | "banner_fail"
| "disable" | "max_load" | "rlimit_as" | "rlimit_cpu"
| "rlimit_data" | "rlimit_rss" | "rlimit_stack" | "v6only"
| "deny_time" | "umask" | "mdns" | "libwrap")
(* redirect and cps aren't really lists, they take exactly two values *)
|attr_lst_eq ("server_args" | "log_type" | "access_times" | "type"
| "flags" | "redirect" | "cps")
|attr_lst_op ( "log_on_success" | "log_on_failure"| "only_from"
| "no_access" | "env" | "passenv")
let default_attr =
attr_one ( "instances" | "banner" | "bind" | "interface" | "per_source"
| "groups" | "banner_success" | "banner_fail" | "max_load"
| "v6only" | "umask" | "mdns")
|attr_lst_eq "cps" (* really only two values, not a whole list *)
|attr_lst_op ( "log_type" | "log_on_success" | "log_on_failure" | "disabled"
| "no_access" | "only_from" | "passenv" | "enabled" )
(* View: body
* Note:
* We would really like to say "the body can contain any of a list
* of a list of attributes, each of them at most once"; but that
* would require that we build a lens that matches the permutation
* of all attributes; with around 40 individual attributes, that's
* not computationally feasible, even if we didn't have to worry
* about how to write that down. The resulting regular expressions
* would simply be prohibitively large.
*)
let body (attr:lens) = Util.del_str "\n{\n"
. (comment|attr)*
. del /[ \t]*}[ \t]*\n/ "}\n"
(* View: includes
* Note:
* It would be nice if we could use the directories given in include and
* includedir directives to parse additional files instead of hardcoding
* all the places where xinetd config files can be found; but that is
* currently not possible, and implementing that has a good amount of
* hairy corner cases to consider.
*)
let includes = [ key /include|includedir/
. Util.del_ws_spc . store /[^ \t\n]+/ . eol ]
let service =
let key_re = /[^ \t\n\/]+/ - /include|includedir|defaults/ in
[ del /service[ \t]+/ "service " . key key_re . body service_attr ]
let defaults = [ key "defaults" . del /[ \t]*/ "" . body default_attr ]
let lns = ( comment | includes | defaults | service )*
let filter = incl "/etc/xinetd.d/*"
. incl "/etc/xinetd.conf"
. Util.stdexcl
let xfm = transform lns filter
(* Local Variables: *)
(* mode: caml *)
(* End: *)
ACC SHELL 2018