4.1 What is a List Exit?
List "exits" are available to control the major events associated with list maintenance. While the implementation of list exits is necessarily system dependent, the list exits themselves (i.e. the tasks that they may carry out, as opposed to how such tasks would be carried out on a particular operating system) are system independent.
To prepare a list exit, you must go through the following steps:
- Create an appropriate exit program, as explained below. Let's assume that the program's name is XYZ. (Note that exit programs should be named with only alphanumeric characters, i.e., A-Z, 0-9.)
- Modify the LIST_EXITS configuration option in the LISTSERV site configuration file (create one if none was present in your configuration). This variable lists the names of all the "known" exits. For security reasons, LISTSERV will not call an exit which is not listed there. To enable XYZ and ABC as valid list exits, you would set LIST_EXITS to "XYZ ABC" (with or without the quotes, depending on your operating system). You must restart LISTSERV after making this change.
Note: Do not code the extension of the exit program (if any) into the LIST_EXITS configuration variable. For instance if you are running under Windows and your exit is called XYZ.CMD, you set LIST_EXITS to “XYZ”. |
- Modify the header of all the lists which should call the XYZ exit, and add "Exit= XYZ". This tells LISTSERV to call the XYZ exit at various exit points.
SECURITY NOTE: Once an exit has been listed in the LIST_EXITS option, ANY list owner may activate it for his own lists. In other words, step 2 merely tells LISTSERV that the program is a “bona fide” exit. There is no mechanism to restrict the use of an exit to a particular list, because it is very easy to implement this in the exit itself, by checking that the name of the list is what you expect or allow. |
LISTSERV exits receive one or more parameters as input, and may provide a numeric and (in a few cases) supplemental string result as output. Each operating system has its own set of numeric return codes for various kinds of failures, but LISTSERV always uses the same internal return code system for its exits - anything else would quickly become unmanageable. The value 0 always means "success" or "normal/default action". Positive values indicate various non-default actions, depending on the particular exit point. Negative values indicate system errors. Exit programs are responsible for doing their own error reporting, since LISTSERV has no way to know which errors they may or may not run into.
The location, name, programming language and calling conventions of the exit program vary from one operating system to another. Let's examine the basics first:
- On z/VM, the program must be called XYZ EXEC A1 (on LISTSERV's A-disk) and must be written in REXX.
- On unix, the program must be called XYZ and must be located in the 'home/' subdirectory (i.e., $LSVROOT/home). To distinguish them from the standard L-Soft-provided scripts and executables, exit programs must always be named in upper case. Thus, the program must be called XYZ and not xyz. It can be written in any supported language and LISTSERV must have execute permission.1
- On Windows, the program must be called XYZ.CMD and must be located in the MAIN subdirectory. It must be written in the Windows batch language.
Naturally, you are free to call a program in another language from your exit program. The programming language restriction only applies to the exit program itself.
Important: Even though the exit program is usually called from LISTSERV’s root directory, it should not make any assumption about its current directory. For optimal portability, the program should always use absolute pathnames to access the various files it might need. For instance, list files should be accessed (if at all) as $A/ or A: or %A%\, and so forth. It is particularly important to note that an intermediary script file (for instance a CMD file under Windows) must normally make use of absolute paths, including an absolute path to the interpreter that will run the program called by your exit. If your CMD file calls a perl file, your CMD file will need to look something like this (depending on the actual locations of the files in question, of course): c:\bin\perl -w e:\listserv\main\MYEXIT.pl Similarly, all files referenced in MYEXIT.pl (including, but not limited to, exit.input and exit.output) must be referenced with absolute paths. Not using absolute paths in your programming is guaranteed to produce unexpected results, if indeed LISTSERV is even able to find your exit when the exit point is called. |
The exit may receive one or more string parameters as input. Most operating systems provide a mechanism to pass one parameter to a script or program, with some restrictions. However, LISTSERV may need to pass several parameters, and the restrictions may not be acceptable. Thus, a system independent parameter passing convention had to be designed. This convention is used by all systems except z/VM, where multiple parameters of arbitrary length and contents may be passed to a REXX program. On z/VM, the system independent convention is never used, because it is unnecessary and less efficient than the native method. z/VM exits use the standard PARSE ARG directive to retrieve their parameters.
The system independent convention uses a disk file called 'exit.input' in the same directory as the exit program. This is a standard text file, where each record is one input string parameter. This file is always created if there are 2 or more string parameters for the exit, or if the EXIT_INPUT configuration parameter is set to the value 1. In addition, it is always created on Windows operating systems. Under unix, the file is not created when there is only one parameter and EXIT_INPUT defaults to 0. Since most exits have only a single parameter, in most cases this improves performance. Note that LISTSERV will take care of deleting the 'exit.input' file when appropriate.
Regardless of whether or not the 'exit.input' file is created, the first parameter is always passed to the exit using system-specific methods under unix. Under Windows systems, the first parameter is only passed through the 'exit.input' file. Again, this may simplify programming for simple exits.
SECURITY NOTE: LISTSERV will always quote/escape the first parameter to prevent the recognition of special characters by the underlying operating system. However, your program should be very careful in its use of the parameters in any subsequent system call. For instance, if the parameter to your unix exit is the string “a|b”, LISTSERV will quote the vertical bar so that it does not result in the execution of the program ‘b’, and so that the value “a|b” is present in your argument vector. However, you must still be careful in the use of the arguments within your program, especially if you plan to launch a compiled program from a shell and pass it the same arguments. In that case L-Soft recommends the use of EXIT_INPUT = 1, which allows the second program to read its parameters safely from the ‘exit.input’ file. |
For list exits, there is at least one input parameter, of the form (blank separated):
epname listname more
where 'epname' is the name of the entry point being called (SUB_FILTER, SUB_FAIL, etc.), 'listname' is the name of the list, and 'more' depends on the particular exit point. Under z/VM, 'more' may contain '15'x characters delimiting additional parameters. Again, while 'epname' and 'listname' are unlikely to contain "tricky" characters, the same cannot be assumed about the remainder of the parameter string.
In most cases, your program will only handle a limited set of exit points. When it does not recognize the 'epname', it should exit with the numeric result 0, which tells LISTSERV to take the default action. To exit with the result 0, you can take a normal operating system dependent exit. To return any other numeric code, or to return a string code, you must use the system independent parameter return convention return below, except on z/VM where the operating system provides a suitable native convention for the return of one parameter of arbitrary length and contents. So, REXX programs return their results with a standard RETURN or EXIT statement. The first blank-delimited word is interpreted as the numeric result, and the rest, if any, is the string result. In other words, the return string is broken up into number and string in exactly the same manner as with a PARSE VAR RESULT NUMBER STRING instruction. On z/VM, the system independent return convention is not used, because it is unnecessary and less efficient than the native method.
The system independent convention is based on a file called 'exit.output', in the same directory as the exit program. LISTSERV erases this file before calling your program, and reads it when it regains control. This file is a standard text file, which contains a number of directives. To set the numeric return code, you use the EXIT directive:
EXIT 2
This would set the numeric return code to 2. To set the string result, use:
EXIT-STRING This is the exit string
Note: You must ALWAYS set the numeric code if you want the string result to have any effect. The default numeric code is 0, which means “default behavior”, and the default behavior never uses the string you supply. By definition, the default behavior is whatever LISTSERV would do if the exit were not present. |
In addition to EXIT and EXIT-STRING, a number of other directives are available. For instance, you can tell LISTSERV to send a message to the originating user, to explain why the exit rejected his subscription request. These directives are processed sequentially until the end of the file. Note that the EXIT directives merely set the final exit codes. They do not interrupt the processing of the 'exit.output' file, which is always read to the end of the file. This means that, if you change your mind about the exit code, you can write a new EXIT instructions and LISTSERV will use the last value that you supplied.
Each directive has 0 or more mandatory parameters, and may support a number of optional parameters, which are always listed after the mandatory ones. Some parameters may be simple blank-delimited keywords or options, while others may contain arbitrary data. The exit should not need to provide placeholders for optional parameters, and above all it should be possible to add new optional parameters without requiring all exits to be rewritten. To solve this problem, each directive was given two forms: a simple form, where the entire directive fits in a single line, and an explicit form, where you indicate the number of parameters that you intend to provide, and each parameter follows on a line by its own. In the simple form, the mandatory parameters are filled from the data supplied on the single directive line, and all the optional parameters are set to their default value. Each blank delimited word supplies one parameter, until the first N-1 parameters have been set. The remainder is used for the last parameter. Here is an example of the simple form:
TELL jack@XYZ.COM The FOO-L list is only open to FOO Inc. employees.
Parameter 1 (mandatory): "jack@XYZ.COM"
Parameter 2 (mandatory): "The FOO-L list is only open to FOO Inc. employees."
Parameter 3 (optional): <default>
If, on the other hand, you want to send the message to more than one person, you need to use the explicit form. In the explicit form, you specify the parameters on separate lines and modify the directive to add the number of lines which you are passing to the processor. By way of example, here is the explicit form of the TELL directive shown above, where the number 2 is added to the directive, telling LISTSERV that there will be two lines of parameters following:
TELL2
jack@XYZ.COM cc: honcho@FOO.COM
The FOO-L list is only open to FOO Inc. employees.
If you wanted the message echoed to the LISTSERV console log, you would have to add another line containing the ECHO parameter, and you would have to specify TELL3:
TELL3
jack@XYZ.COM cc: honcho@FOO.COM
The FOO-L list is only open to FOO Inc. employees.
ECHO
It is always safer to use the explicit form if you are not sure how many words you will have in the various parameters. The simple form is provided mostly for directives such as EXIT or EXIT-STRING which only take one parameter, and for hardcoded parameters.
Currently, the supported directives are as follows. The "VM API" indicates the corresponding REXX API for z/VM users (it is not possible to provide an API for non-z/VM systems because the exits run in a different virtual address space and may not call back into LISTSERV entry points).
Name: EXIT, EXIT-CODE, RETURN
P1: Numeric return code
Action: Sets numeric return code; does NOT abort exit.output processing!
VM API: EXIT/RETURN
Name: EXIT-STRING
P1: String result code
Action: Sets exit string result
VM API: EXIT/RETURN
Name: TELL, LTELL
P1: List of RFC822 addresses
P2: Message text
P3 (opt): Blank separated list of options (default = off)
- ECHO: echoes message to log
- RAGGED: flows but does not justify message
Action: Sends long (paragraph) message to specified users
VM API: LSVLTELL
Name: TELLNF
P1: List of RFC822 addresses
P2: Message text
P3 (opt): Blank separated list of options (default = off)
- ECHO: echoes message to log
Action: Sends unformatted message to specified users
VM API: LSVTELL
1. To be clear, the exit program MUST NOT have a file extension. If you write a MYEXIT exit program in perl, for instance, DO NOT name the exit MYEXIT.pl -- LISTSERV will not see it and you will see errors in the LISTSERV log each time an exit point is called for the list(s) in question. Thus the script simply should be named MYEXIT regardless of the language in which it is written. Since the script contains (or should contain) its own internal information about the interpreter being used to execute it (the "bang-hash" path in the first line), there is no overriding reason for the file to have an associative extension.