Recipe language

Recipe files are used to automate configuration management tasks within the  utility. Recipe files are expressed using a domain specific language. Since recipe files are intended to manipulate configuration information, many parts of which most naturally appear as names and string values or lists of strings, the recipe language is rather string oriented. The primary inspirations for the recipe language is the Icon programming language designed by Ralph Griswald as well as the IETF&#x27;s Sieve mail filtering language.

Recipe file syntax includes C-like expressions, operators, and assignments, Sieve-like conditionals, and loops. The available data types are integers, strings, and lists.

Like  itself recipes can only be used with a unified configuration.

Note that  can be used to test the syntax of recipe operations.

Recipes are typically written to operate in several phases:



 Checks are done to make sure the right conditions exist for the recipe to be effective. If the desired conditions aren&#x27;t met the recipe can either issue a warning or exit with an error. 

 The recipe asks a number of questions to determine exactly what changes should be made. 

 The recipe optionally uses the  function to make sure the user wishes to proceed if any warnings were issued. 

 Finally, the recipe implements the requested changes. 



Note that while this is the typical ordering, recipes are not constrained to use it and may use other approaches if appropriate.

Comments
The  character indicates that the remainder of a line is a  comment.

Integer values
Integers are expressed as decimal values with an optional sign. An optional base specification prefix can be used to write values in other bases: 2%10 == 2    16%ff == 255 2%-1010 == -10 All integers are represented internally as signed 32 bit values.

String values and list values
 As recipe files are intended for manipulating configuration files and parameters, which are generally thought of most naturally as strings or lists of strings, the recipe language is rather string oriented.

A string value is written simply as characters within double quotes, e.g., "a sample string" "strings may    include line breaks" Backslashes have special meaning in string values: "\""    - quote     "\t"     - tab     "\r"     - carriage return     "\n"     - line feed     "\\"     - backslash     "\uNNNN" - Unicode character, must specify exactly 4 hex digits                (new in MS 8.0.2.3) A list value is written as a comma-separated list of elements, delimited by brackets, e.g.,      &#x5b;"e1", "e2", "e3", "e4"&#x5d; As of MS 8.1.0.1 trailing commas are allowed in lists, making them easier to maintain:      &#x5b;"c1", "c2", &#x5d; Note that the elements of a list are always strings.

Optlists
 Optlists are regular lists with an even number of elements. The elements are processed in pairs: the first element in a pair is the "name" element while the second element is the "value" element. A number of the built-in functions are designed to work with optlists. The special case of empty string as the first element in a pair is used to represent annotations; in this case the second element of the pair contains the annotation text.

Variables
 Variables may have integer, string, or list values. Variables are created by assigning them a value; no type declarations are necessary. Variable names are case-insensitive. v = 1; w = "this is a test"; x = &#x5b;"a", "b", "c"&#x5d;; Variables may be used in most places where a string, integer, or list value is expected, including in list constructs. Given: s = "string"; l = &#x5b;"list"&#x5d;; the following expressions are true: &#x5b;s&#x5d; == &#x5b;"string"&#x5d; &#x5b;s,s&#x5d; == &#x5b;"string", "string"&#x5d; &#x5b;s,l&#x5d; == &#x5b;"string", "list"&#x5d; &#x5b;l,l&#x5d; == &#x5b;"list", "list"&#x5d;

Variable indices
 Substrings and sublists may be referenced through the use of indices. Indices into strings and lists  point not at characters, but between characters, as for strings in the Icon programming language. That is, 1 points just before the first character, 2 between the first and second character, etc., and 0 points just after the last character, -1 points between the penultimate and last characters, -2 between the antepenultimate and penultimate characters, etc. The expression  returns the character immediately following the interstice pointed to by the index.

For instance, if     s = "abcdef"; then the following expressions are true: s&#x5b;1&#x5d; == "a" s&#x5b;3&#x5d; == "c" s&#x5b;6&#x5d; == "f" s&#x5b;-1&#x5d; == "f" s&#x5b;-4&#x5d; == "c" s&#x5b;-6&#x5d; == "a" whereas  is illegal as it is trying to return the character after the last one in the string.

When a range is specified, then the substring between the two indices is returned. There is no question about whether this is inclusive or exclusive as the indices point at interstices. Thus again taking as a sample string: s = "abcdef"; the following expressions are true: s&#x5b;1,0&#x5d; == s    s&#x5b;1,0&#x5d; == "abcdef" s&#x5b;1,2&#x5d; == "a" s&#x5b;2,0&#x5d; == "bcdef" s&#x5b;2,-1&#x5d; == "bcde" s&#x5b;-3, -1&#x5d; == "de" s&#x5b;i,i&#x5d; == "" # when 1 &#x3c;= i &#x3c;= length(s)+1 # or -length(s) &#x3c;= i &#x3c;= 0 s&#x5b;1,i+1&#x5d; == left(s,i) s&#x5b;-i,0&#x5d; == right(s,i) Indices can be used on the left hand side of assignment statements. For example, after the assignment s&#x5b;1&#x5d; = "z"; will have the value " ".

List indices operate in a similar fashion, except that indices refer to list elements rather than characters. Single value list indices return a string while two-valued indices return a sublist. So if   is given a value l = &#x5b;"a","b","c",d","e","f"&#x5d;; the following expressions are all true:     l&#x5b;1&#x5d; == "a"     l&#x5b;3&#x5d; == "c"     l&#x5b;6&#x5d; == "f"     l&#x5b;-1&#x5d; == "f"     l&#x5b;-4&#x5d; == "c"     l&#x5b;-6&#x5d; == "a"     l&#x5b;1,0&#x5d; == &#x5b;"a", "b", "c", "d", "e", "f"&#x5d;     l&#x5b;1,2&#x5d; == &#x5b;"a"&#x5d;     l&#x5b;2,0&#x5d; == &#x5b;"b", "c", "d", "e", "f"&#x5d;     l&#x5b;2,-1&#x5d; == &#x5b;"b", "c", "d", "e"&#x5d;     l&#x5b;3,-1&#x5d; == &#x5b;"c", "d", "e"&#x5d;     l&#x5b;-3,-1&#x5d; == &#x5b;"d", "e"&#x5d; Note that parentheses may be used in place of square brackets for indexing.

Statements
The if...then...else... statements in the recipe language are akin to those of the Sieve email filtering language: if expression { ... }    if expression { ... }    else { ... }    if expression { ... }    elsif { ... }    if expression { ... }    elsif { ... }    else { ... } A general loop construct is also provided, with the syntax: loop { ...          exitif (expression); ...           nextif (expression); ...          } A loop may contain zero or more   and/or   statements. The loop terminates if the argument to  evaluates to. New in 8.0.2.3,  can be used to cause the loop to restart if the associated condition evaluates to.

Loops may be nested: loop { ...          loop { ...                exitif (expression-1); # Exit from inner loop #1 }          ...           exitif (expression-2); # Exit from outer loop ...          loop { ...                exitif (expression-3); # Exit from inner loop #2 }         } Assignment statements are akin to those of the C programming language. a = "string"; b = 2; c = d = &#x5b;"list"&#x5d;;

Operators
The recipe language provides a variety of prefix, postfix, and infix operators. The following table lists the available operators in order of decreasing precedence.

 +Most operators are available for use in Sieve filters, as well as in recipes, except for use of square brackets,, for indexing into strings or lists. Sieve filter syntax uses square brackets to indicate Sieve lists, so in Sieve filters the alternate syntax for substring indexing of  must be used.

Functions
The recipe language provides a large number of built-in functions. The following table lists all of the built-in functions in alphabetical order; subsequent subsections describe all recipe-specific functions in groups. For general string/list/integer functions, see Symbol table functions.

Configuration option access
 The primary purpose of the recipe language is to manipulate the various option settings in a Messaging Server configuration. This functionality is provided by a number of separate functions. These functions all accept either an option name or optlist containing option name/value pairs as arguments. Some functions allow incomplete option names and/or wildcards while others do not.

The existence of an option setting can be determined with the   function. This function accepts an option name string as an argument and returns a count of the number of options set in the configuration that match the name. (false) is returned if no options are set that match the name. For example: exists_option("os_debug")     -&#x3e; 0  (os_debug is not currently set) exists_option("channel:tcp_&#x2a;") -&#x3e; 42 (42 options are set on tcp_ channels) As of MS 8.0.2.1, the default value for an option setting can be determined with the    function. It returns the built-in default for the option. An empty string is returned if the option has no built-in default value. An error occurs if the specified option does not exist.

The    &#x5b;  &#x5d; function returns, as an optlist, a list of the options (and their values) whose names begin with the specified string  . The optional integer second argument,  , specifies whether or not to retain backslash characters (for quoting special characters) in the options&#x27; values; the default is  (false).

The   function returns the value of a single option. An error will occur if the name given matches multiple options or does not exist. An empty string will be returned in either or two cases: if the specified option name is valid and the option is a no-value option which is set, or if the specified option name is valid for a valued option which is not set. (So note that  is not sufficient for checking  whether a no-value option is set; instead  use   to check on a whether or not a no-value option is set.) get_option("mm_debug")      -&#x3e; 0       (mm_debug is set to 0) get_option("os_debug")      -&#x3e; ""      (os_debug, valued option, is not set) get_option("slave_debug")   -&#x3e; ""      (slave_debug, no-value, may be set) exists_option("slave_debug") -&#x3e; 0      (slave_debug not set) get_option("&#x2a;_debug")       -&#x3e; &#x3c;error&#x3e; (multiple &#x2a;_debug options) The function      returns an optlist containing the names and values of the options with name  . (So note that unlike, the result returned by   has no ambiguity and differentiates between set no-value options vs. unset valued options.) Note that when both role and instance flavors of an option are set   returns only the instance flavor of the option.

The function    &#x5b;  &#x5d; sets the option named   for options that take no value, or sets the option names   to the value   for options  that do take a value. The function     sets the option-value pairs in the optlist   .

The     unsets (deletes) the option named by the string  . The     function deletes (unsets) the options named in the list  .

New in MS 8.0.2, recipes support a   function. This      function accepts a single string argument which then undergoes the       option resolution process. The result of that process is then returned      as a bit-encoded integer. The bits are:

The    &#x5b;  &#x5d; function validates that the specified option name is valid, and that the specified value is valid. It does not actually set the option to the specified value. For instance: validate_option("service:SMTP.enable","test")    -&#x3e; 0 validate_option("service:SMTP.enable","0")       -&#x3e; 1 The   and   functions tell the recipe to set the specified flavor of option. The   function tells the recipe to set options according to the option&#x27;s own preferred flavor (which note is   for all but a few options).

For enabling modification of restricted options, see also the   switch to the   command, or the     operation.

Configuration group access
<span id='recipe_group_operations'> In Unified Configuration, some options may be set under a named group. So there are function to access/set/delete/etc. an entire such named group of options.

The       function adds the group named (by the string value of)   with options specified by the  optlist   ;  the function returns   if this operation was successful, or   if there was an error (such as in the case where the specified group name already existed). For example, the call: add_group("dispatcher.service:SMTP_SPECIAL",          &#x5b;"enable", "1",            "tcp_ports", "28225",            "image", "IMTA_BIN:tcp_smtp_server",            "parameter", "CHANNEL=tcp_special"            "logfilename", "IMTA_LOG:tcp_special_server.log"&#x5d;) adds a Dispatcher service named, which will be a "special" SMTP server listening on port   running the channel.

The       function appends to the group named (by the string value of)    the options specified by the  optlist ; the function returns   if this operation was successful, or   if an error occurred. (The group will be created if it does not already exist; that is,  on a previously non-existent group is not an error.)

The     function deletes the group named (by the string value of)   .

The     function returns   if the   group named (by the string value of)    exists, or the number of options set in the group if the group does exist.

The     function returns the content of the group (the option names and values) as an optlist.

The       function prepends to the group named (by the string value of)    the options specified by the  optlist ; the function returns   if this operation was successful, or   if an error occurred. (The group will be created if it does not already exist; that is,  on a previously non-existent group is not an error.)

The       function replaces the group named (by the string value of)   with options specified by the  optlist ;  the function returns   if this operation was successful, or   if there was an error (such as in the case where the specified group name does not already exist).

System information
<span id='recipe_system_info_access'> New in 8.0.1.2. The  function provides access to various pieces of static information about the system msconfig is running on. The function accepts a single case-insensitive string as its only argument specifying the piece of information to return.

In the following table, the Messaging Server version is taken to be "a.b.c.d" and the product build date is "yyyymmdd".

msconfig information
<span id='recipe_msconfig_info_access'> New in 8.0.1.2. The  function provides access to various pieces of  information about how msconfig was invoked and its current status.

The following information items are currently supported:

Environment access
<span id='recipe_environment_access'> The recipe language can find out the values of environment variables, or find file path specifications using Messaging Server&#x27;s basic environment variable values or (for the MTA) special symbolic names.

The     function returns the value of the environment variable named  ,  or the empty string if  no such environment variable is defined.

The     function takes a string ,  , or   as argument, returning the file path specification of the SERVERROOT, DATAROOT, or CONFIGROOT, respectively.

The     function will return a file path specification by converting any environment variable or  special symbolic name in the string argument   into its path equivalent.

information operations
<span id='recipe_msconfig_information_operations'> The   function returns the number of additional arguments given to the  command.

The     function returns, as a string, the  th argument given to the   command. The value   must be between  and   inclusive.

The     function sets a string to display when the recipe is listed by the   command. Note that  must be a literal string enclosed in quotes; it cannot be an expression. When the recipe itself is executed, the   function does nothing other than return the value of its argument  .

The      function specifies keywords to display when the recipe is listed  by the    command.

File operations
<span id='recipe_file_operations'> Although recipes primarily operate directly on Messaging Server configuration data without any need for explicit file operations, situations may arise where additional files need to be read or written. Accordingly, a set of file manipulation functions is provided in the recipe language.

If no explicit path is given in the file name, the file location defaults to the config root directory. Explicit paths may be specified or any of the following special prefixes may be used: IMTA_ROOT:   -&#x3e; &#x3c;serverroot&#x3e; "/" IMTA_LIB:    -&#x3e; &#x3c;serverroot&#x3e; "/lib/" IMTA_TABLE:  -&#x3e; &#x3c;configroot&#x3e; "/" IMTA_PROGRAM: -&#x3e; &#x3c;dataroot&#x3e; "/site-programs/" IMTA_LOG:    -&#x3e; &#x3c;dataroot&#x3e; "/log/" IMTA_QUEUE:  -&#x3e; &#x3c;dataroot&#x3e; "/queue/" IMTA_HTTP:   -&#x3e; &#x3c;dataroot&#x3e; "/www/" For manipulating paths in file names, see also the    and    functions.

The     function returns   if the file named by    exists,   if it doesn&#x27;t.

Files may be read with    . The contents of the file named by   are returned as a string. Line feeds are used as line delimiters in the string. For example: split(trim(read_file("a.a"),"\n"),"\n") returns the contents of the file with each line as a list element and any trailing blank lines removed.

There are two ways to write files.       writes the contents of string   to the file named by string  .     &#x5b;  &#x5d;  writes the contents of the list   into the file named by  . Each list element written is terminated by the value of  ; line feed is the default terminator if   isn&#x27;t supplied. In either form an error occurs if the file cannot be opened or written.

(New in MS 8.0.1.2) The     function deletes the file named by the string  , returning   if the delete operation was successful, or    if not.

Unless  was specified for the   invocation, the utility will check whether to perform a requested   operation: Allow recipe to delete file &#x5b;Y, N, A&#x5d;? and if such prompting is disallowed ( specified), will not execute the delete operaiton and instead issue an error: Delete_file not allowed to prompt for permission in -noprompt mode

Terminal I/O operations
<span id='recipe_terminal_io_operations'> The recipe language has a few terminal I/O primitives.

The     operation prints a string to the terminal of the administrator executing the recipe.

The     operation prints a warning string to the terminal of the administrator executing the recipe, and updates the internal setting (which may be tested via  ) that a  warning (an additional warning) has occurred.

New in MS 8.0.2.3, warn without any arguments returns the number of warnings that have been issued during the current run.

The     operation issues a specified error string back to the administrator.

The  &#x5b; &#x5b;  &#x5d;  operation asks the administrator whether to continue after a warning. (Note that the prompt string argument   and default response string argument   are optional, with a default prompt string and empty response string, interpreted as false, used they are omitted.  So merely   is also valid syntax.)

The   &#x5b;  &#x5b;  &#x5d;&#x5d;  operation asks the administrator to respond with a yes or no response.

The   &#x5b;  &#x5b;  &#x5d;&#x5d;  operation reads a string input from the terminal of the administrator executing the recipe.   specifies the prompt to print on the terminal,   specifies a default to return if no value is entered or  was specified when msconfig was invoked, and (new in 8.0.2.3)   specifies the name of a statefile variable whose value is used as a default and updated with any value that&#x27;s entered. The  switch must be specified on the msconfig command line for   to have any effect.

The   operation obtains a password string from the administrator executing the recipe.

If the  switch was specified on a   command, the   operation merely returns a   unconditionally. But otherwise, the   operation prompts the administrator for whether or not to enable modification access to restricted options.

Statefile operations
<span id='recipe_statefile_operations'> The functions  ,  ,  , and   can be used to check the existence of, get, set, and delete statefile variables, respectively. Note that   returns -1 and the other functions are no-ops if statefile suport is not enabled by specifying the  switch on the msconfig command line.

Alias creation and manipulation operations
<span id='recipe_alias_operations'> Messaging Server 8.0.1.2 now provides a set of recipe functions to create and manipulate aliases.

An MTA alias consists of a named set of option-value pairs, always containing one ore more  options which specify the alias expansion addresses. A single alias is represented in the recipe language using an optlist, and a number of functions are provided to access and manipulate aliases. All of these functions accept the unquoted name of the alias as the first argument. This name may be specified in any case and is converted to lower case.

Alias existence can be checked with  . A nonzero value is returned if the named alias is already part of the configuration; zero if it isn&#x27;t.

The contents of an alias definition can be retrieved as an optlist using the   function. For example, if the configuration has a  alias: user-prefix@example.com: &#x5b;prefix_text&#x5d; Prefix text, user1@example.com, user2@example.com The call  will return an optlist: &#x5b;"alias_prefix_text", "Prefix text", "alias_entry", "user1@example.com", "alias_entry", "user2@example.com"&#x5d; Note that alias options that do not accept a value will appear with a zero length string as the value.

The   function is used to add a new alias to the configuration. A second argument is required specifying the various alias options as an optlist. An optional third parameter can also be specified containing a list of alias entries; these are converted to  alias options. An error is returned if the alias already exists. For example, the call: add_alias("list@example.com",            &#x5b;"envelope_from", "list-error@example.com"&#x5d;,             &#x5b;"list1@example.com, "list2@example.com&#x5d;) adds this alias to the configuration: list@example.com: &#x5b;envelope_from&#x5d; list-error@example.com, list1@example.com, list2@example.com The   function is the same as, except that any alias with that name that already exists will be removed prior to the addition.

The   function is also the same as, except that specified alias options and entries will be appended to any existing alias definition.

The   function is also the same as, except that specified alias options and entries will be preended to any existing alias definition.

The   function deletes the named channel from the configuration. No operation is performed if the alias doesn&#x27;t exist.

The   function takes two arguments: The name of an existing alias and a list of alias options to delete from it. Note that unset_alias is not designed to work on alias entries.

Channel creation and manipulation operations
<span id='recipe_channel_operations'> An MTA channel consists of a named set of option-value pairs, usually containing an    option. A single channel is represented in the recipe language using an optlist, and a number of functions are provided to access and manipulate channels. All of these functions accept the name of the channel as the first argument. This name may be specified in any case and is converted to lower case.

Channel existence can be checked with  . A nonzero value is returned if the named channel is already part of the configuration; zero if it isn&#x27;t.

The contents of a channel definition can be retrieved as an optlist using the   function. For example, if the configuration has a  channel: tcp_tas deliveryflags 2 mustsaslserver smtp allowswitchchannel maytlsserver tcp_tas-daemon The call  will return an optlist: &#x5b;"official_host_name", "tcp_tas-daemon", "deliveryflags", "2", "mustsaslserver", "", "smtp", "", "allowswitchchannel", "", "maytlsserver", ""&#x5d; Note that channel options that do not accept a value appear with a zero length string as the value.

The   function is used to add a new channel to the configuration. A second argument is required specifying the various channel options as an optlist. An error is returned if the channel already exists. For example, the call: add_channel("tcp_aol", &#x5b;"official_host_name", "tcp-aol",                          "single_sys", "",                           "randonmx", "",                           "noswitchchannel", "",                           "pool", "SMTP_POOL",                           "smtp", ""&#x5d;); adds this channel to the configuration: tcp_aol single_sys randommx noswitchchannel pool SMTP_POOL smtp tcp-aol The fact that a channel is represented as an optlist makes it easy to add a channel based on an existing one: add_channel("tcp_new",              put_optlist(get_channel("tcp_local"), "official_host_name", "tcp-new")); The   function is the same as, except that any channel with that name that already exists will be removed prior to the addition.

The   function deletes the named channel from the configuration. No operation is performed if the channel doesn&#x27;t exist.

Finally, the   function changes an existing channel. The second argument to   must be an optlist containing the  channel options to set. Existing options will be overridden; new options will be added. For example, given the channel: tcp_intranet loopcheck maysaslserver mx pool SMTP_POOL \ saslswitchchannel tcp_auth single_sys smtp \ allowswitchchannel maytlsserver tcp_intranet-daemon The call: set_channel("tcp_intranet", &#x5b;"master_debug", "",                               "nomx", "",                                "daemon", "router.example.com",                                "multiple", ""&#x5d;); will modify the channel to be: tcp_intranet daemon router.example.com loopcheck master_debug \ maysaslserver multiple nomx pool SMTP_POOL \ saslswitchchannel tcp_auth single_sys smtp \ allowswitchchannel maytlsserver tcp_intranet-daemon

Rewrite rule creation and manipulation operations
<span id='recipe_rewrite_operations'> MTA rewrite rules are represented in Unified Configuration a list of   values under the    group. Each such rule value has a pattern and a template, separated by white space. The recipe language supports a number of functions which operate on rewrite rules; for rewrite rule values, such functions use an optlist specifying pattern-template pairs. For instance, the call: append_rewrites(&#x5b;".lmtp", "$E$F$U%$H.lmtp@lmtpcs-daemon",                 ".lmtp", "$B$F$U%$H@$H@lmtpcs-daemon"&#x5d;) adds rewrite rules: msconfig&#x3e; show rewrite &#x2a; .lmtp&#x2a; role.rewrite.rule = .lmtp $E$F$U%$H.lmtp@lmtpcs-daemon role.rewrite.rule = .lmtp $B$F$U%$H@$H@lmtpcs-daemon Note how, in contrast to many other recipe language function uses of optlists, when it comes to the rewrite rule creation and manipulation functions, the optlist arguments used are not  specifying option-value pairs, but rather are specifying (an ordered rule list of) pattern-template pairs.

The recipe language functions available specifically for creating or manipulating rewrite rules are:

<ul>

     </li>

     </li>

  &#x5b;  &#x5b;  &#x5d; &#x5d; </li>

     </li>

     </li>

</ul>

Mapping creation and manipulation operations
<span id='recipe_mapping_operations'> An MTA mapping consists of a named and possibly annotated set of pattern-template pairs. A single mapping is represented in the recipe language using an optlist, and a number of functions are provided to access and manipulate mappings. All of these functions accept the name of a mapping as the first argument. This name may be specified in any case and is converted to upper case.

Mapping existence can be checked with  . A nonzero value is returned if the named mapping is already part of the configuration; zero if it isn&#x27;t.

The contents of a mapping can be retrieved as an optlist using the   function. For example, if the configuration has a  mapping: PORT_ACCESS ! Handle internal IP addresse &#x2a;&#x7c;&#x2a;&#x7c;&#x2a;&#x7c;&#x2a;&#x7c;&#x2a;                   $C$&#x7c;INTERNAL_IP;$3&#x7c;$Y$E &#x2a;                           $NEXTERNAL The call  will return an optlist: &#x5b;"", " Handle internal IP addresses\n", "&#x2a;&#x7c;&#x2a;&#x7c;&#x2a;&#x7c;&#x2a;&#x7c;&#x2a;", "$C$&#x7c;INTERNAL_IP;$3&#x7c;$Y$E", "&#x2a;", "$NEXTERNAL"&#x5d; Note that the comment appears as name-value pair with an empty string as the name.

The   function is used to add a new mapping to the configuration. A second argument is required specifying the content of the mapping as an optlist. An error is returned if the mapping already exists. For example, the call: add_mapping("test_mapping", &#x5b;"a","b","c","d","","Last","e","f"&#x5d;); adds this mapping to the configuration: TEST_MAPPING a b  c d ! Last e f The   function is the same as  , except that if the mapping already exists its contents will be replaced.

The   function deletes the named mapping from the configuration. No operation is performed if the mapping doesn&#x27;t already exist.

Finally, the   and   functions add entries to an existing mapping. The second argument to these functions must be an optlist containing the entries to add. Both functions are equivalent to   if the specified mapping doesn&#x27;t already exist. For example, given the mapping: TEST_MAPPING c d then the calls: prepend_mapping("Test_Mapping", &#x5b;"a","b"&#x5d;); append_mapping("test_mapping", &#x5b;"e","f"&#x5d;); will modify the mapping to be: TEST_MAPPING a b  c d   e f

Deployment map operations
<span id='recipe_deploymap_operations'> The  recipe language function&#x27;s semantics closely follow those of the DEPLOYMAP   command.

All of the deploymap recipe and msconfig support was added in the 8.0.1.1.0 release of Messaging Server.

Add operations
deploymap :add :deployment d Adds deployment   to the deployment. Adding a deployment that already exists is a no-op. deploymap :add &#x5b;:deployment d&#x5d; :host h &#x5b;:role r&#x5d; Adds host(s)   with role   to deployment   in the deployment map.   can be either a string or a list. An error occurs if any of the hosts already exist. The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected. A deployment with the default name of " " will be created if no deployment exists in the deployment map. No role will be associated with the hosts if   is not specified. deploymap :add &#x5b;:deployment d&#x5d; :host h :property p Add property/properties   to host   in deployment  . Host   will be created if it does not already exist. The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected. A deployment with the default name of " " will be created if no deployment exists in the deployment map. deploymap :add &#x5b;:deployment d&#x5d; :host h :property p :role r The host   is created in deployment   with role   and properties  . The host must not already exist. The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected. A deployment with the default name of " " will be created if no deployment exists in the deployment map.

All add operations return the number of modifications that were made to the deployment map.

Create operations
deploymap :create Create a new, empty deployment map. Note that if you write this out using &#x27;s     command you will delete all your existing entries!

Delete operations
deploymap :delete :deployment d Deletes deployment   from the deployment map. Deleting a nonexistent deployment is a no-op. deploymap :delete &#x5b;:deployment d&#x5d; :host h Deletes host(s)   from deployment  .   can be either a string or a list. Deleting a nonexistent host is a no-op. The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected. deploymap :delete &#x5b;:deployment d&#x5d; :host h :role Delete any role associated with host(s)   in deployment  . Deleting a nonexistent role is a no-op. An error occurs if host   does not exist. The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected. deploymap :delete &#x5b;:deployment d&#x5d; :host h :property p Delete any properties for host   in deployment   that match any of the glob-style wildcard(s)  . Both   and   can be a  string or a list. The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected.

All delete operations return the number of modifications that were made to the deployment map.

Dump operation
deploymap :dump Returns a string containing an outline of the entire deployment map.

List operations
deploymap :list :deployment &#x5b;d&#x5d; Returns a list of all the deployments in the deployment map that match the pattern  . All deployments are returned if   is omitted. deploymap :list &#x5b;:deployment d&#x5d; :host &#x5b;h&#x5d; &#x5b;:online &#x7c; :offline&#x5d; Returns a list of all of the hosts in deployment   which match the pattern glob-style pattern  . The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected. All hosts are returned if   is not specified. deploymap :list &#x5b;:deployment d&#x5d; &#x5b;:host h&#x5d; :role &#x5b;r&#x5d; &#x5b;:online &#x7c; :offline&#x5d; Returns a list of all the unique roles of the hosts in deployment   matching the glob-style pattern  . The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected.   is an optional glob-style pattern used to filter the roles that are returned. A list of all the roles associated with all of the hosts is returned if     is not specified. Note that the expression: string(deploymap :list :host h :role) can be used to obtain the role of host   as a string. deploymap :list &#x5b;:deployment d&#x5d; :host :role &#x5b;r&#x5d; &#x5b;:online &#x7c; :offline&#x5d; Returns an optlist containing a list of all the hosts in deployment   and their associated roles. Hosts that do not have roles are not returned. The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected.   is an optional glob-style pattern containing one or more wildcards  or two or more non-wildcard strings used to filter the roles that are returned.

Note that a sublist  of the first elements in an optlist    is easily extracted with a loop of the form: l = &#x5b;&#x5d;; loop {exitif t == &#x5b;&#x5d;; l = pop(t); pop(t);} deploymap :list &#x5b;:deployment d&#x5d; :host :role r &#x5b;:online &#x7c; :offline&#x5d; Returns a list of all the hosts in deployment   with role  .   must not contain more than one element or any glob-style wildcard characters; if it does an optlist is returned as described above. The current deployment is used if  is not specified; if there is no current deployment the first deployment in the deployment map is selected. deploymap :list &#x5b;:deployment d&#x5d; &#x5b;:host h&#x5d; &#x5b;:role r&#x5d; :property &#x5b;p&#x5d; &#x5b;:online &#x7c; :offline&#x5d; Returns a list of property values for hosts matching   in deployment   and with a role matching  . The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected. If   is not specified the properties of  all hosts are returned. No role check is performed if   is not specified.   is an optional glob-style pattern used to filter the properties that are returned. deploymap :list &#x5b;:deployment d&#x5d; :host &#x5b;:role r&#x5d; :property p &#x5b;:online &#x7c; :offline&#x5d; Returns a list of hosts in deployment   which have properties matching  , and optionally, a role matching  . The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected.   must be a string or list of glob-style patterns.

In all of these operations the  and    parameters will limit hosts to those known to be online or offline, respectively.

Read operations
deploymap :read s Reads a JSON-format deployment map from string  .

Rename operations
deploymap :rename n :deployment d Renames deployment   to  .   must specify an existing deployment;   must be a nonempty utf-8 string. deploymap &#x5b;:deployment d&#x5d; :rename n :host h Renamed host   in deployment   to  . The current deployment is used if   is not specified; if there is no current deployment the first deployment in the deployment map is selected. Host   must already exist and   must be a valid domain name.

All rename operations return the number of modifications that were made to the deployment map.

Set operations
deploymap :set &#x5b;:deployment d&#x5d; :host h :role r Sets the role for host(s)   in deployment   to  .   can be either a string or list. The specified hosts must already exist in the deployment. The current deployment is used if  is not specified; if there is no current deployment the first deployment in the deployment map is selected.

All set operations return the number of modifications that were made to the deployment map.

Write operations
<span id='Deploymap_write_operations'> deploymap :write Returns the contents of the current deployment map as a JSON-formatted string. Note that no mechanism is provided in the recipe language to update the active deployment map; this can only be done at the   level.

Optlist manipulation operations
<span id='recipe_optlist_operations'> As previously described, an optlist is a list containing an even number of strings which are interpreted as name-value pairs. A number of functions are provided to manipulate these sorts of lists.

An optlist can be created and populated just like any other list. Alternately, the   function can be used to add elements to an empty optlist. Optlists can also be read from files in      format. For example: o = &#x5b;&#x5d;; # Empty optlist o = &#x5b;"A","B"&#x5d;; # Optlist containing a single option A with value B  o = &#x5b;"A","C", "B", "D"&#x5d;; Optlist containing two options A and B with values C and D   o = put_optlist(&#x5b;&#x5d;, "A","C", "B", "D"); # Same as previous optlist o = read_optlist(read_file("optlist.txt"); # Read optlist from file optlist.txt You can get option values from an optlist or check if a given option exists. For example, given an optlist, the following results would be returned:    get_optlist(o, "A") -&#x3e; "C"   get_optlist(o, "B") -&#x3e; "D"   get_optlist(o, "E") -&#x3e; ""   exists_optlist(o, "A") -&#x3e; 1   exists_optlist(o, "N") -&#x3e; 0 Options can be set or deleted from an optlist. Note that it is common to assign the results of these functions back to the same optlist.    o = put_optlist(o, "E", "F"); # Add option E with value F to optlist o   o = delete_optlist(o, "A"); # Delete option A from optlist o  And in MS 8.0.1.3 or later:    o = delete_optlist(o, &#x5b;"A","B"&#x5d;); # Delete option A with value B from optlist o  Optlists can be written out as       format files:    write_file("optlist.txt", write_optlist(o));

LDAP operations
<span id='recipe_ldap_operations'> (New in MS 8.0.1) The recipe language can access LDAP. Several functions exist for such operations.

The     function initializes the built in LDAP client. It must be called prior to using any of the other recipe language  functions.

By default, the LDAP client uses the current settings of the       ,        ,        ,        ,       and         base options       when it initializes. The      single argument   is an        optlist specifying       override values for any or all of these options. The optlist may      be empty. Repeated calls will shut down and reinitialize the LDAP client      with new settings. The LDAP client is shut down automatically when      the recipe terminates.

The    &#x5b;  &#x5d; function applies the LDIF contained in the string   to an LDAP directory. (Details of which LDAP directory -- and how to connect to it -- must previously be established via an  call;  attempting to call   before   will result in an error.)

By default, any LDAP errors that occur attempting to perform the operation of applying the LDAP will be treated as a recipe warning, but see bit 4096 of the optional flags argument  . The optional flag argument   is a bit-encoded integer specifying flags; the currently defined flag bits are:

The  function returns an integer count of the number of successful modifications performed.

(New in MS 8.0.1.1.0) The   function takes an optlist argument  specifying at least the   for the search, and optionally also the attribute to return, the   of the search, and a filter  for the search. Valid values for the " " are:,   (or  ),    (or  ). An optional second integer argument specifies the number of entries to return, and defaults to  (return all entries) if omitted. The function returns an optlist containing the matching attribute-value pairs.

Random value generation
<span id='recipe_strongrandom'> The     function returns the specified number of bytes of random value. This is a cryptographically strong generator.

The     function returns a uniformly distributed random number between 0 and n-1. The linear congruential generator described in "Random Number Generators: Good Ones Are Hard To Find", S. Park and K. Miller, CACM 31 No. 10, pp. 1192-1201, October 1988 is used to generate these numbers. The sequence is initially seeded with the sytem time.

An explicit 32 bit integer seed for the   function can be specified by calling the     function. This may be useful for debugging purposes. A value of 0 will cause the sequence to be reinitialized from the system clock.

Call-out to routine in external library
<span id='recipe_call_user_operations'> The        &#x5b;  ...&#x5d; function lets recipes call out to routines in external libraries. The argument   specifies the path to the external library (assumed to be in  if a full path is not provided); the argument    specifies the routine name; and argument(s)  ,  ,  , etc., specify the arguments to provide to routine  . The result of the routine call is returned as the result of.

performs a call of the routine named   of the form: s2(int argc, struct argv, char &#x2a;reason, int &#x2a;rlength); This feature is intended for calling out to site-supplied external routines.

User-defined routines
As of the 8.0 release, the recipe language supports user-defined routines: sub routine(p1, ...) { ...        return expression-1; }    ...     variable = routine(expression-1, ...); Up to 20 parameters are allowed. Parameters are passed by value and evaluation is lazy: An unused parameter is never evaluated. Parameters are only evaluated once, so it is easy to force evaluation to occur at the beginning of the routine: sub f(x, y, z) { x; y; z;        ... } The entire parameter list can be omitted if the routine requires no parameters.

Routines may call themselves recursively, e.g., sub factorial(n) {if n &#x3c;= 1 {return 1;} else {return n &#x2a; factorial(n-1);}} Note, however, that since there is currently no mechanism for forward declarations of routines, two or more routines cannot call each other recursively.

Routines can reference and modify global variables. Local variables can also be defined by placing the my control command immediately prior to the first use of the variable: sub fib(n) { my s = &#x5b;1, 1&#x5d;; my a = 1; my b = 1; loop { exitif --n &#x3c; 2; my c = a + b;            s .= c;             a = b;             b = c;         } return s;    } Autoincrement, autodecrement, and the various augmented assignment operators (, , and so on) are all allowed on  parameters and local variables. So is the exchange operator ; however, exchange cannot be used with a global variable on the right hand side and a local variable or parameter on the left hand side.

Preprocessing Directives
The recipe language provides a very limited preprocessor facility as of the 8.0.1 release. The following preprocessing directives are supported:

Preprocessing directives must appear in column 1 to be recognized. Note that preprocessor directives have lower precedence than quoted strings, so directives won&#x27;t be recognized inside of multiline quoted strings.

See also:
 * Sieve filters
 * Random number generation