#  -*- text -*-
#
#
#  $Id: 21edc221a27a969c612fa816fa7b47280b708bc5 $

#######################################################################
#
#  = Exec Module
#
#  The module `exec` is used for executing external programs.
#
#  == Don't use this module.
#
#  We provide this module for the _extremely rare_ case when it is
#  useful.  The main utility of the module is for testing, and for
#  doing some unusual things which aren't possible via any other
#  method.
#
#  It can be appealing to just ignore `unlang`, and run `%exec()`
#  everywhere.  This kind of configuration can be written quickly,
#  means that you don't need to spend any time understanding the
#  server configuration.  This process is generally more effort than
#  it is worth.
#
#  The biggest problem with `exec` is that it is slow.  Very, very,
#  very, slow.  We have tested the server at 80K Access-Requests per
#  second (to OpenLDAP) on low-end hardware.  This is fast enough to
#  run a major ISP.
#
#  In contrast, when each Access-Request causes an `exec` script to be
#  run, the rate of access-Request can drop by a factor of 100 or
#  more.  It is not unusual for the server to max out at a few hundred
#  authentications per second when running multiple scripts per packet.
#
#  The server has sufficient functionality that it is essentially
#  never necessary to `exec` and external script.  Please use the
#  built-in functionality of the server; it is hundreds of times
#  faster than running a script, and it is designed to process
#  packets.  An external script is worse, by nearly all possible
#  standards of measurement.
#
#  == %exec() function
#
#  This module also provides a dynamic expansion function.  You can
#  then run a script like this:
#
#    Attribute-Name = %exec('/path/to/program', 'args', ...)
#
#  The output of `exec` is parsed and assigned to the attribute.
#
#  Depending on the module configuration, the output of `%exec()` can
#  even be pairs, such as `User-Name = foo`.  Those pairs can be
#  assigned to a list.  If the program fails, it will output nothing.
#
#  Note that when the assignment is to a list, the `exec` call _must_ be
#  inside of a double-quoted string.
#
#    &request += "%exec(/path/to/program,args, ...)"
#
#  The value of the attribute will be replaced with the output of the
#  program which is executed.
#
#  The attributes from the list referenced in the `input_pairs`
#  configuration item will be placed into environment variables of the executed
#  program.
#
#  Alternatively, by setting the `program` item of the module configuration,
#  the module can be called as a module rather than as an xlat function.
#  See the `echo` module for an example of this.
#
#  In this scenario, if the configuration item `output_pairs` is set,
#  and the `wait` configuration item is set to "yes", the output of
#  executing the program will be parsed for attribute pairs which
#  will be added to the list referenced in `output_pairs`.
#
#  When called as a module, the return value of the program run determines
#  the result of the exec instance call as follows:
#
#  [options="header,autowidth"]
#  |===
#  | Code | Return    | Description
#  | < 0  | fail      | the module failed.
#  | = 0  | ok        | the module succeeded.
#  | = 1  | reject    | the module rejected the user.
#  | = 2  | fail      | the module failed.
#  | = 3  | ok        | the module succeeded.
#  | = 4  | handled   | the module has done everything to handle the request.
#  | = 5  | invalid   | the user's configuration entry was invalid.
#  | = 6  | disallow  | the user was locked out.
#  | = 7  | notfound  | the user was not found.
#  | = 8  | noop      | the module did nothing.
#  | = 9  | updated   | the module updated information in the request.
#  | > 9  | fail      | the module failed.
#  |===
#

#
#  ## Configuration Settings
#
exec {
	#
	#  wait:: Wait for the program to finish.
	#
	#  If we do NOT wait, then the program is "fire and
	#  forget", and any output attributes from it are ignored.
	#
	#  If we are looking for the program to output
	#  attributes, and want to add those attributes to the
	#  request, then we MUST wait for the program to
	#  finish, and therefore set `wait=yes`
	#
	wait = yes

	#
	#  program:: The name of the program to execute, and it's
	#  arguments, when called as a module.
	#
	#  The string is dynamically expanded, so it can contain
	#  attribute references, etc.  However, quoting of programs
	#  and arguments is complex.  The program name and arguments
	#  are parsed by the server as a single string.  But that
	#  string is parsed by the shell into multiple arguments,
	#  which are then passed to the program.
	#
	#  We recommend not using the `exec` module, and instead using
	#  the `%exec(...)` function.  That function will generally be
	#  easier to use and understand.
	#
	#  Where this module is used, the `program` string below
	#  should use triple quotes.  These allow the text inside of
	#  the string to contain double-quote characters without
	#  needing to escape them.  This doesn't affect the output
	#  string, but it does make the configuration easier to read.
	#
	#  If the program is expected to take quoted strings as
	#  arguments, then the quotes have to be done carefully.  See
	#  the `echo` module for more information, and for a worked
	#  example/
	#
	#  The summary of all of the above is that it's usually easier
	#  to run one shell script with no arguments.  That shell
	#  script can then print the attribute names, operators, and
	#  values.
	#
#	program = """/bin/true "%{User-Name}" """

	#
	#  input_pairs:: The attributes which are placed into the
	#  environment variables for the program.
	#
	#  The `input_pairs` can be any "group" style attribute.
	#  Usually it is the top-level list such as `request`,
	#  `reply`, etc.
	#
	input_pairs = request

	#
	#  output_pairs::: Where to place the output attributes (if any) from
	#  the executed program.
	#
	#  The values allowed are the same as for the `input_pairs`.
	#
	#  This configuration item is used only when the `program`
	#  configuration item is set, and when `wait = yes` is also
	#  set.  Otherwise it is ignored.
	#
#	output_pairs = reply

	#
	#  shell_escape:: Escape the environment variables.
	#
	#  If this is set, all the RADIUS attributes are capitalised and dashes
	#  replaced with underscores. Also, RADIUS values are surrounded with
	#  double-quotes.
	#
	#  That is to say:
	#
	#    User-Name=BobUser => USER_NAME="BobUser"
	#
	#  Note that this escaping only applies to environmental variables
	#  created from the request list.  For environmental variables inherited
	#  from the main radiusd process no escaping is applied.
	#
	shell_escape = yes

	#
	#  env_inherit:: Pass the server environment variables to the called program
	#
	#  For security, the server environment variables are not passed to the
	#  program being executed.  Setting this flag to `yes` will pass the
	#  server environment variables to the program.
	#
	#  Any `input_pairs` will be merged with these environmental variables.
	#
	#  The default is `no`.
	#
	env_inherit = no

	#
	#  timeout:: Set a time wait for the program to finish.
	#
	#  Default is `10` seconds, which should be plenty for nearly
	#  anything. Range is `1` to `30` seconds.
	#
	#  WARNING: You are strongly encouraged to NOT increase this
	#  value.  In fact, you are much better off decreasing it to a
	#  lower value.  Doing so will improve network stability and
	#  responsiveness.
	#
	timeout = 10
}
