12. Embedded Perl
Author: The Exim Project. <https://www.exim.org/>
Description: Exim is a message transfer agent (MTA) developed at the University of Cambridge for use on Unix systems connected to the Internet.
Keywords: exim,smtp,mta,email

# Exim Internet Mailer

<-previous next->

## Chapter 12 - Embedded Perl

Exim can be built to include an embedded Perl interpreter. When this is done, Perl subroutines can be called as part of the string expansion process. To make use of the Perl support, you need version 5.004 or later of Perl installed on your system. To include the embedded interpreter in the Exim binary, include the line

    EXIM_PERL = perl.o

in your Local/Makefile and then build Exim in the normal way.

### 1. Setting up so Perl can be used

Access to Perl subroutines is via a global configuration option called perl_startup and an expansion string operator ${perl ...}. If there is no perl_startup option in the Exim configuration file then no Perl interpreter is started and there is almost no overhead for Exim (since none of the Perl library will be paged in unless used). If there is a perl_startup option then the associated value is taken to be Perl code which is executed in a newly created Perl interpreter.

The value of perl_startup is not expanded in the Exim sense, so you do not need backslashes before any characters to escape special meanings. The option should usually be something like

    perl_startup = do '/etc/exim.pl'

where /etc/exim.pl is Perl code which defines any subroutines you want to use from Exim. Exim can be configured either to start up a Perl interpreter as soon as it is entered, or to wait until the first time it is needed. Starting the interpreter at the beginning ensures that it is done while Exim still has its setuid privilege, but can impose an unnecessary overhead if Perl is not in fact used in a particular run. Also, note that this does not mean that Exim is necessarily running as root when Perl is called at a later time. By default, the interpreter is started only when it is needed, but this can be changed in two ways:

- Setting perl_at_start (a boolean option) in the configuration requests a startup when Exim is entered.

- The command line option -ps also requests a startup when Exim is entered, overriding the setting of perl_at_start.

There is also a command line option -pd (for delay) which suppresses the initial startup, even if perl_at_start is set.

- To provide more security executing Perl code via the embedded Perl interpreter, the perl_taintmode option can be set. This enables the taint mode of the Perl interpreter. You are encouraged to set this option to a true value. To avoid breaking existing installations, it defaults to false.

  Note: This is entirely separate from Exim’s tainted-data tracking.

### 2. Calling Perl subroutines

When the configuration file includes a perl_startup option you can make use of the string expansion item to call the Perl subroutines that are defined by the perl_startup code. The operator is used in any of the following forms:

    ${perl{foo}}
    ${perl{foo}{argument}}
    ${perl{foo}{argument1}{argument2} ... }

which calls the subroutine foo with the given arguments. A maximum of eight arguments may be passed. Passing more than this results in an expansion failure with an error message of the form

    Too many arguments passed to Perl subroutine "foo" (max is 8)

The return value of the Perl subroutine is evaluated in a scalar context before it is passed back to Exim to be inserted into the expanded string. If the return value is undef, the expansion is forced to fail in the same way as an explicit “fail” on an if or lookup item. If the subroutine aborts by obeying Perl’s die function, the expansion fails with the error message that was passed to die.

### 3. Calling Exim functions from Perl

Within any Perl code called from Exim, the function Exim::expand_string() is available to call back into Exim’s string expansion function. For example, the Perl code

    my $lp = Exim::expand_string('$local_part');

makes the current Exim $local_part available in the Perl variable $lp. Note those are single quotes and not double quotes to protect against $local_part being interpolated as a Perl variable.

If the string expansion is forced to fail by a “fail” item, the result of Exim::expand_string() is undef. If there is a syntax error in the expansion string, the Perl call from the original expansion string fails with an appropriate error message, in the same way as if die were used.

Two other Exim functions are available for use from within Perl code. Exim::debug_write() writes a string to the standard error stream if Exim’s debugging is enabled. If you want a newline at the end, you must supply it. Exim::log_write() writes a string to Exim’s main log, adding a leading timestamp. In this case, you should not supply a terminating newline.

### 4. Use of standard output and error by Perl

You should not write to the standard error or output streams from within your Perl code, as it is not defined how these are set up. In versions of Exim before 4.50, it is possible for the standard output or error to refer to the SMTP connection during message reception via the daemon. Writing to this stream is certain to cause chaos. From Exim 4.50 onwards, the standard output and error streams are connected to /dev/null in the daemon. The chaos is avoided, but the output is lost.

The Perl warn statement writes to the standard error stream by default. Calls to warn may be embedded in Perl modules that you use, but over which you have no control. When Exim starts up the Perl interpreter, it arranges for output from the warn statement to be written to the Exim main log. You can change this by including appropriate Perl magic somewhere in your Perl code. For example, to discard warn output completely, you need this:

    $SIG{__WARN__} = sub { };

Whenever a warn is obeyed, the anonymous subroutine is called. In this example, the code for the subroutine is empty, so it does nothing, but you can include any Perl code that you like. The text of the warn message is passed as the first subroutine argument.

<-previous Table of Contents next->