II. Build and Install

This section describes how to build and install ZMailer.


Consider joining the ZMailer user-community email list, or at least reading/searching its archive. It is the place to meet the Gurus, in case you have problems. See the Overview file in the source distribution for more information.

Chapter 3. Build and Install

3.1. Environment Issues

The cornerstone of everything in busy Internet email routing is a well-working DNS server, and modern resolver library. If you use the BIND name-server, you should be using (or install) a recent version, As of this writing (January 2006), BIND server developers recommend version 9. They also strongly recommend, that you do not let zone data masters (either masters, or slave copies) to do any recursive resolving, and do recursive resolvings with servers that do not have locally mastered data.

For performance reasons you should have local instance of recursively resolving caching name-server. (And for security reasons it should not do any local DNS zone masterings.)

You may also want to support any of following facilities by pre-installing them into your system (before compiling and installing ZMailer, that is):

  • OpenSSL for in- and outbound encrypted SMTP sessions

  • TCP-Wrapper (can also do without it)

  • LDAP client library (if necessary)

  • Private replacement library for getpwnam(), and/or for zgetpwnam()

  • Whoson service to tie in with e.g. POP and IMAP servers for authenticating SMTP relaying. (But the use of SMTP authentication is definitely preferred instead of using external hacks like "POP-before-Post".)

3.2. Auto-configuration

The zmailer.conf file carries various so called “ZMailer Environment” configuration variables.

In following we refer to those often in style of:

Which essentially means expanding in Bourne-shell like manner given ZENV-variable from this file.

The location of this file is defined at system configuration, originally it was at: $MAILSHARE/zmailer.conf, but these days more often at the $MAILVAR/.

This system uses several preferably separate partitions for different things:

  • Software binaries, and databases: $MAILBIN/ (site shared, read-only), $MAILSHARE/ (site shared, read-only), $MAILVAR/ (node local db's, r/w)

  • The mailbox spool: $MAILBOX/ (/var/mail)

  • The postoffice spool: $POSTOFFICE/ (/var/spool/postoffice/)

  • The log directory: $LOGDIR/ (/var/log/mail)


A filesystem without following two properties is not suitable for ZMailer's $POSTOFFICE/:

  • Files must succeed to be link(2):ed in between directories within the filesystem without copying them.

  • The file i-node numbers must not change with rename(2) or link(2) calls applied to them.

Most of the $POSTOFFICE/ directory must be a single mounted filesystem within which files can be link(2)ed from one directory to another, as well as moved around with rename(2)

However, the $POSTOFFICE/transport/ subdirectory can be separate filesystem mounted under $POSTOFFICE/! Such arrangement can (under some situations) result in additional system performance, as transport agents need to modify (write locks) files in the $POSTOFFICE/transport/ subdirectory, while they only read (without locks) files in $POSTOFFICE/queue/ subdirectory.

Adding system reliability in form of having directory data committed to disk at the time of the directory modifying operations returning with success is a nice bonus, although in normal UFS-like cases that taxes system performance heavily.

E.g. running fast-and-loose with async metadata updates in Linux EXT2 filesystem gives you performance, but in case the system crashes, your postoffice directory may be in shambles, and important email may have been lost.

How exactly you can combat the problem is yours to choose. Most filesystems for UNIX have lots of different options at mount-time, and also by-directory attributes can be set to control these things. Check yours after your decide on what kind of data loss threat you can tolerate at the expence of what speed reduction. (E.g. 300+ day straight uptime with power surges during a thunderstorm at the end of it toasting your machine along with its disks and filesystems, but trouble-free running until then ?)

The GNU autoconf mechanism is used, however, you still may need to touch on some files after that system has run through: You MUST define --prefix= so that ZMailer components end up in reasonable places. The $MAILBIN/ (and $MAILSHARE/, and $MAILVAR/) variable values are derived from the --prefix=, which can cause surprises if you do make install with GNU autoconf defaults.

When choosing your prefix, do try to keep is fairly short, as there are a few scripts which concatenate string-components of:

  "#! "+prefix+"/bin/router -f"
and usually systems have a limit of 32 characters for that, which gives at most 15 characters for your prefix!

Also, if the $MAILSHARE/zmailer.conf file exists[1], it is read to initialize several different environment paths (including $MAILBIN/, et.al.!)

# ./configure                             \
  --prefix=/opt/mail                      \
  --with-postoffice=/var/spool/postoffice \
  --with-mailbox=/var/mail                \

Or an example from my development machine:

  $ ./configure --prefix=/opt/mail
  creating cache ./config.cache
  *** You can set  ZCONFIG  environment variable to define
  *** the location of the (default) /opt/mail/zmailer.conf -file
  *** (You can use also   --with-zconfig=  -parameter)
  *** Consider also setting following parameters:
  ***   --mandir=DIR     -- for man-pages
  ***   --libdir=DIR     -- for libzmailer(3)
  ***   --includedir=DIR -- for libzmailer(3)
  *** (They can be outside the --prefix=DIR -tree)
  *** You can set CC, and CFLAGS  environment variables to
  *** choose the C-compiler, and its options, especially at
  *** systems where there are multiple choices to use...

You can also go into a sub-directory, and configure and compile there: (But it may need GNU make as system make!)

  $ mkdir myhost ; cd myhost
  $ ../configure …
  $ make

See if SiteConfig makes sense now, if not, you can tune most of the values with various --with-*= keywords:

  $ ./configure --help
Explanations about these configuration options are listed at chapter Chapter 6.

Those options that you can't tune, you can edit at the SiteConfig.in file. (Redo the configure with new parameters, if that looks to be necessary approach.)

Additional examples:

  • DEC OSF/1 at nic.funet.fi with DECs best compiler…

      $ CFLAGS="-O -g3 -std1" CC="cc -migrate" \
        ./configure --prefix=/l/mail

  • Sun Solaris 2.5 at mailhost.utu.fi, SunSoft CC

      $ CC="cc -O" ./configure --prefix=/opt/mail

  • Sun Solaris 2.5 at mailhost.utu.fi, gcc-2.7.2

      $ ./configure --prefix=/opt/mail --with-gcc

  • Sun Solaris 8 at marsu.funet.fi, gcc-2.95.2 (egcs-1.1.2)

      $ 'CC='gcc' CFLAGS='-g -O'  ./configure    \
           --prefix=/opt/mail --with-ipv6        \ 

  • Linux-2.0.x/libc-5.4.2 at mea.cc.utu.fi, gcc-2.7.2

      $ ./configure --prefix=/l/mail

3.3. Compilation

At the top-level, run

  $ make
or perhaps:
  $ make clean all
which at first cleans everything, and then makes — great if you changed some configuration parameters.

This should compile everything, and leave a zmailer.Config file in the top-level directory. Nothing outside the source area will be touched at this point.

(If your system make lets your shell SHELL environment affect its own execution environment, it may be that non-sh/ksh/zsh users detect weird phenomena, and failures. Beware!)

3.4. Installing and Upgrading

This section describes how to install or upgrade ZMailer.

3.4.1. Install Preparation

If you are currently running a zmailer, kill off all mailer processes using

  $ zmailer kill
and save the state of your system. This includes any active contents of the $POSTOFFICE/, as well as database files and anything else in the installation areas you want to be sure to keep. This is just paranoia, the installation should not overwrite precious files, and will save old versions of distribution files in bak/ sub-directories.

The interface in between the commonly used sendmail, and ZMailer is a “compatibility program”, which is to replace the /usr/lib/sendmail (a.k.a. /usr/sbin/sendmail on some systems). The system attempts to automate the replacement, but it may present a cry for help if your system does not have functioning symlinks. Also if test -h $SENDMAILPATH does fault in mysterious ways, the reason may be that your system does not have symlinks.

If you are currently running Sendmail, kill your SMTP server and drain the Sendmail queue. There is no automatic method to re-queue Sendmail messages under ZMailer. If you later want to back out to Sendmail, all you need to do is move the former version of the sendmail (on /usr/lib/sendmail.bak, for example) binary back to /usr/lib/sendmail.

(You may also need to do some magics with system start-up scripts in case you are running SysV-style init. BSD /etc/rc.local does need its own gymnastics too. Sample SysV-init script is at file: proto/zmailer.init.sh)

A sort of method to quickly handle your sendmail queue is to start ZMailer's SMTP server, reconfigure the old sendmail to use smart-host, which happens to be at the same machine. (Or at an adjacent machine if you moved the queue, or …) Anyway the point is to get the sendmail to send its queue via SMTP to the ZMailer. An equally valid option is to drain the sendmail's queue by running sendmail in queue drain mode only, although /usr/lib/sendmail points to ZMailer's sendmail.”

3.4.2. Installation

Once you are safe, run:

  # make install
  # $MAILBIN/post-install -MD5
(Substitute $MAILBIN with the path where your binaries go)

This installs all binaries and the default configuration and database files, as well as creates $POSTOFFICE/ directories. The configurations will still need editing! See below.

The post-install handles important activity in tracking the base versions of configuration files by storing MD5 check-sums of original files with .md5 suffix tagged to them into their original location (proto/ sub-directories). This way when sysadmin changes something, the new run of post-install will detect the changes and not write over such file.

There exists also a way to do the installation into a “parallel universe” by means of install-time “prefix” environment variable:

  # . SiteConfig
  # DESTDIR=/var/tmp/build
  # make install DESTDIR=$DESTDIR
  # $DESTDIR$MAILBIN/post-install --destdir $DESTDIR
which of course expects to have /var/tmp/build/ directory in existence, and possibly some others under it, but you will soon see, what it needs. (But post-install does not support that, and so it needs to be used at the last stage of packaged ZMailer's installation)

3.4.3. Installing the Manual Pages.

Because for a long time the installation location of ZMailer's man-pages has not had an obvious destination location, normal “make install” run at the top-level does not install them!

Go into the man/ directory, and install the manual pages by hand:

  # cd man
  # make
This will tell what you can order it doing, and what is the default for MANDIR is at the moment. If the default is right:
  # make install
or in case the default guessing didn't get it right:
  # make install MANDIR=/our/manpages

Chapter 4. System Configuring

4.1. Run-time files

This section describes the configuration in short. More detailed information can be found in Administration and Reference parts.

4.1.1. zmailer.conf

The zmailer.conf file carries various so called “ZMailer Environment” configuration variables.

In following we refer to those often in style of:

Which essentially means expanding in Bourne-shell like manner given ZENV-variable from this file.

The location of this file is defined at system configuration, originally it was at: $MAILSHARE/zmailer.conf, but these days more often at the $MAILVAR/.

This system uses several preferably separate partitions for different things:

  • Software binaries, and databases: $MAILBIN/ (site shared, read-only), $MAILSHARE/ (site shared, read-only), $MAILVAR/ (node local db's, r/w)

  • The mailbox spool: $MAILBOX/ (/var/mail)

  • The postoffice spool: $POSTOFFICE/ (/var/spool/postoffice/)

  • The log directory: $LOGDIR/ (/var/log/mail)

4.1.2. /etc/group

The default configuration also expects to find names of trusted users listed at /etc/group entry zmailer. Users (unames) listed there will be able to claim any addresses at the message headers, etc. (See $MAILSHARE/cf/trusted.cf for its usage there.)

The usual minimal set is: root,daemon,uucp. (Note: At some machines “daemon” is called “daemons”; It must be on that group for the smtpserver to be able to work properly!)


SECURITY ITEM: Those users at zmailer group must not contain nobody!

The nobody is used to prevent externally given inputs from being able to execute arbitrary programs at the system, or from writing to arbitrary files.

4.1.3. /etc/services

Add the following line to /etc/services in the section for host-specific services:

mailq   174/tcp  # Mailer transport queue
Indeed this isn't quite mandatory, as the scheduler subsystem can be configured to use different IPC socket. See more about this at Scheduler's PARAM entries: Section 22.1.1.

4.2. The Router subsystem

4.2.1. The Router Configuration File ($MAILSHARE/router.cf).

You must now pick a top-level router configuration file. The default is provided in proto/cf/SMTP.cf(.in). (The post-install places it into $MAILSHARE/router.cf).

You need to verify $MAILSHARE/router.cf.

Some real-life samples of router.cf are at the proto/ directory in the source tree.

4.2.2. $MAILVAR/mail.conf

If you are using the default configuration setup, the router.cf file expects to find a $MAILVAR/mail.conf file containing three variable definitions:

  # Where am I?
  # Who am I?
  # Who do I claim to be?

For example:


Create $MAILVAR/mail.conf with appropriate contents. If you are a multi-host site, determining these things can be automated according to your local policies and conventions. See the files specific to the University of Toronto (UT*.cf) for examples of this.

Location of this file is written in $MAILSHARE/router.cf. By editing that entry you can alter it.


Note that “hostname=” entry above is not alone sufficient for getting the system to know all of the domains it should consider as local. See below about localnames database.

4.2.3. Verifying That the Router Starts

At this point, you should be able to start the router process in interactive mode. Run:

  # $MAILBIN/router -i
  # /usr/lib/sendmail -bt

You should see something like:

  ZMailer router (2.99.55 #4: Tue Feb 22 15:24:09 EET 2001)
  Copyright 1992 Rayan S. Zachariassen
  Copyright 1992-2001 Matti Aarnio
  Configured with command: 'CC='gcc -Wall' CFLAGS='-g -O' ../configure …'

If there are errors in the configuration file, you will be told here. The “z#” is the interactive prompt for root. It is unlikely you can do anything useful before setting up the data files, so get out of this by hitting EOF, or type exit.

4.2.4. The Router Database Files

Now you should merge, replace, and very least check the default database and forms files against your previous setup.

In older systems users had fixed choices on which databases to have at the router subsystem for which looked up relation. Newer systems have $MAILBAR/db/dbases.conf configuration file to tell the association of database to look-up relations, and also the source files. $MAILBIN/zmailer script

You may want to add a symbolic link from some directory in your path to $MAILBIN/zmailer, if you don't already have this. I put this link in /usr/local/sbin.

This script is skeleton driver for lots of things in the ZMailer, including the not so small a feat of acting as SysV-Init's init-script as well. $MAILVAR/db/dbases.conf file

As mentioned above, this file configures (when exists) database look-up functions versus actual back-end databases.

With this file one can have e.g. multiple aliases databases which are bound together in pre-determined query order (first matcher wins).

This configuration file is used to translate a semi-vague idea about what database sources (in what forms) are mapped together under which look-up names, and what format they are, etc…

This is used by zmailer newdb command to generate all databases described here, and to produce relevant .zmsh scripts for the router to use things. The zmailer newdb invocation does not mandate router restart in case the database definitions have not changed; reverse is true: If definitions are added/modified/removed, the router must be restarted!

When you use zmailer newdb command, you recompile all databases defined in the controlling configuration file $MAILVAR/dbases.conf -- or if you don't have that file, then all databases listed below, as creation/update of those are the system defaults.

For more complete example, see the default boilerplate version of this file.

Figure 4-1. Sample of $MAILVAR/db/dbases.conf file

#|     relation-name
#|         dbtype(,subtype)
#|              dbpriv control data (or "-")
#|                  newdb_compile_options (-a for aliases!)
#|                     dbfile (or "-")
#|                         dbflags (or "-") ...
#| The  dbtype  can be "magic" '$DBTYPE', or any other valid database
#| type for the Router.  Somewhat magic treatment (newdb runs) are
#| done when the dbtype is any of: *DBTYPE/dbm/gdbm/ndbm/btree
#| The "dbfile" need not be located underneath of $MAILVAR, as long as
#| it is in system local file-system (for performance reasons.)  E.g.
#| one can place one of e.g. aliases files to some persons directory.

aliases     $DBTYPE 0:0:644    -la $MAILVAR/db/aliases     -lm
aliases     $DBTYPE majordomo:0:644 -la /opt/Majordomo/md-aliases -lm

fqdnaliases $DBTYPE root:0:644 -la $MAILVAR/db/fqdnaliases -lm%
userdb      $DBTYPE root:0:644 -la $MAILVAR/db/userdb      -lm

routesdb    $DBTYPE -    -l  $MAILVAR/db/routes      -lm% -d pathalias
thishost    $DBTYPE -    -l  $MAILVAR/db/localnames  -lm  -d pathalias $MAILVAR/db/aliases file

The provided skeleton aliases file on purpose contains syntax errors, so you are reminded to change the contents.

Choose one of the following methods to rebuild the database:

  # $MAILBIN/newaliases
  # $MAILBIN/zmailer newaliases
  # /usr/lib/sendmail -bi
  # /usr/bin/newaliases
  # $MAILBIN/zmailer newdb

If there are errors, correct them in the aliases file, and repeat the command until the alias database has been initialized. The final message should look something like:

 319 aliases, longest 209 bytes, 16695 bytes total
exact numbers vary, of course…

See also IETF's RFC 2142: “Mailbox Names for Common Services, Roles and Functions” (file doc/rfc/rfc2142.txt) for other suggested aliases you may need. Alias expansion

Read the notes on alias expansion in the file doc/guides/aliases and on mailing list maintenance in Section 13.2.2, Mailing Lists and ~/.forward. $MAILVAR/db/fqdnaliases file

The fqdnaliases database is for mapping fully-qualified user addresses to others — for example your machine has a set of domain-names for it to consider local, but you want to have separate people to be postmasters for each of them as shown at Figure 4-2.

Figure 4-2. Sample of fqdnaliases file

  postmaster@domain1: person1
  postmaster@domain2: person2
  postmaster@domain3: person3, person4

It is also possible to shunt all recipient addresses for given domain to some arbitrary addresses as shown at Figure 4-3.

Figure 4-3. Second sample of fqdnaliases file

  @domain4:  person4
  @domain5:  %1@domain6

This facility is always in stand-by — just add the file, and you have it at the next router start-up.

The %1 local part is special (and experimental, as of 21-Feb-2001) substitution pattern where local part (user) can be replaced into the looked up data. More details at Section 21.5.16 (dblookup), and at Section 21.5.50 (relation declaration).

You may even handle just a few users for each of those domains, and then have the routes entry (see below at Figure 4-5) to declare something suitable:

  .domain1  error!nosuchuser
  .domain1  error!nosuchuser!%0
which combined with the fqdnalias method will let postmaster@domain1 to exist, and report error on all others.

Choose one of the following methods to rebuild the database:

  # $MAILBIN/newfqdnaliases
or either of:
  # $MAILBIN/zmailer newfqdnaliases
  # $MAILBIN/zmailer newdb

If there are errors, correct them in the fqdnaliases file, and repeat the command until the alias database has been initialized. The final message looks similar to that of the ordinary aliases case.

If you have multiple fqdnaliases databases defined at the dbases.conf, you must use the zmailer newdb. (See Section 24.1.8.) $MAILVAR/db/localnames file

Add the host-names you want ZMailer to do local delivery for, to the $MAILVAR/db/localnames file. Due to my own belief in Murphy, I usually add partially qualified domain names and nicknames in addition to canonized names. If you want to do local delivery for mail clients, put their names in here too. You may use pathalias style .domain names in this file, to indicate everything under some subdomain.

With the sample config files for ZMailer-2.98, and latter, this localnames is actually a mapping of those various names to the desired forms of the canonical name, thus an example as seen in figure Figure 4-4.

Figure 4-4. Sample of localnames file

# Left:  input name
# Right: what is wanted to be shown out
# List here all names for the system
astro.utu.fi         astro.utu.fi
oj287                astro.utu.fi
oj287.astro.utu.fi   oj287.astro.utu.fi
oj287.utu.fi         astro.utu.fi
sirius               sirius.utu.fi
sirius.astro.utu.fi  sirius.utu.fi
sirius.utu.fi        sirius.utu.fi

In certain cases the router is able to deduce some of the names, however smtpserver anti-relay policy compiler will not be able to do so, and needs this data!

THUS: All names that the host may ever have are best listed in here! It reminds you of them, and makes sure a message destined into the host really is accepted.

Compile this into run-time binary database with command:

  # zmailer newdb
(fall-back method is sequential re-scan of the text file) $MAILVAR/db/routes file

Add any UUCP neighbours or other special cases to this file. For an example see Figure 4-5.

You can compile the file into binary database with command:

  # zmailer newdb

Figure 4-5. Sample of routes file

  # “routes” mapping file
  .toronto.ca      error!err.wrongname
  .toronto.cdn     error!err.wrongname
  alberta          uucp!alberta
  atina            smtp![]
  calgary          smtp!cs-sun-fsa.cpsc.ucalgary.ca
  icnucevm.bitnet  smtp!icnucevm.cnuce.cnr.it UUCP Node Names

If your hostname and UUCP node name are not identical, put your UUCP node name in the file /etc/name.uucp (or /etc/uucpname).

4.2.5. Checking the Routing

At this point, you should be able to start the router again in interactive mode, and ask it to route addresses. Try either of:

  # /usr/lib/sendmail -bt
  # $MAILBIN/router -i
at the prompt:
  z# router you
(where “you” is your login-id, naturally) should print out:
  (((local you you default_attributes)))

Keep playing around with various addresses until you get a feel for it. Modify the configuration file if your setup requires it.

To give more feeling of what goes on during the route-command, you can give command rtrace before trying to use route.”

4.3. The Smtpserver subsystem

The smtpserver implements RFC-821 server along with lots of latter extensions.

Configurable subsystems are:

  • Generic server parametrization with smtpserver.conf file.

  • Relaying policy control via smtp-policy database.

  • Optional message content analysis via “contentfilter” mechanism.

  • Optional PAM authentication framework for SMTP AUTH extension.

  • Optional externally driven program to autenticate users; command line contains username, and STDIN gets the user supplied password.

See the Administration Chapter 12 for further details.

4.3.1. The smtpserver.conf, and smtp-policy databases

These take care of such a things as preventing relay-hijack type of abuse of your system.

Basically you want to install the boilerplates and the tool scripts, edit them a bit, and run policy-builder.sh script. For further details on this, see chapter Section 12.2.

In smtpserver front you may need to lower the strict standards of the basic RFC-821 SMTP protocol and allow acceptance of non-qualified addresses — ones without any sort of domain name in them.

Another thing to allow is (sigh) MS-Windows-CE 1.0/2.0 gadgets with their totally broken SMTP sending system.

Both of these things are handled by “EHLO-style options” described at chapter Section 12.1.2.

4.3.2. Testing smtpserver operationality

The smtpserver can be tested fully with fairly simple method -- as long as input databases are readable by the test runner:

  $ $MAILBIN/smtpserver -i -d 1 -T '[]'
  $ $MAILBIN/smtpserver -i -d 1 -T '[ipv6.11::33]'

Above the bracketed dotted decimal address literal is source address used at policy function testing, and one should vary there systems which are allowed to relay thru the server, and also systems which are not allowed to relay thru the server.

Do testing by issuing normal SMTP protocol transactions, and observing the results:

 000- Lots of debug information
 220 some greeting
 EHLO foobar
 000- Lots of debug information
 250-local.host.name Hello foobar
 250 HELP
 000- Lots of debug information
 250 Ok …
 RCPT TO:<user@some.where>
 000- Lots of debug information
 250 Ok …

If you want to do testing without excessive amount of debug information, do leave out “-d 1” part of the start arguments.

4.4. The Scheduler subsystem

4.4.1. Checking the Scheduler

The location of the scheduler.conf on running system is $MAILSHARE/scheduler.conf

For normal operations of the system the current sample of scheduler.conf file is quite sufficient, but in case you want to do something unusual, like using procmail for local delivery, do read on.

The default scheduler.conf contains also linkage to scheduler.auth (see Section 22.3), which is access-control for interacting with the scheduler from external programs, like mailq.

In Figure 4-6 there are some salient points about tuning the local channel behaviour.

Figure 4-6. Sample of scheduler.conf passage for local/* selector

    # want 20 channel slots, but only one HOST
    # Do MIME text/plain; Quoted-Printable -> text/plain; 8BIT
    # conversion on flight!
    command="mailbox -8"
    # Or with PROCMAIL as the local delivery agent:
    #command="sm -8c $channel procm"
    # Or with CYRUS server the following might do:
    #command="sm -8c $channel cyrus"

There are three variants of the command= entry:

command="mailbox -8"

The normal ZMailer mailbox(8) channel program.

command="sm -8c $channel procm"

Variant for running procmail.

command="sm -8c $channel cyrus"

Variant for using CMU Cyrus message store server.

For more information regarding scheduler configuration language, see Section 22.1.

4.4.2. Checking scheduler.auth file

Access-control to the Scheduler's internal state data is defined at file scheduler.auth, which should be usable in its default form.

For more information about this, see Administration Section 14.5, and Reference Section 22.3.

4.4.3. Checking sm.conf file

For some uses the scheduler runs sm(8) program — called “sendmail-like mailer”.

This supports most of sendmail's M-entry flags, at least flags with versions previous to 8.11(.0)

The ZMailer sm(8) channel program is used to create support for things like:

  • uucp transmits

  • procmail as local delivery agent

  • supporting CMU Cyrus message store as local delivery agent

For more information, see Section 23.4.1.

4.4.4. Customizing ZMailer Messages

Edit several of the canned error messages and programs (scripts) to reflect your local configuration: $MAILSHARE/forms/ files and $MAILBIN/ta/usenet (injected message).

Normally the boilerplate messages looks something like these:

HDR From:    The Post Office <postmaster>
HDR Sender:  mailer-daemon
SUB Subject: Errors: No such user(s)
ADR Bcc:     <postmaster>

This is a collection of reports about email delivery
process concerning a message you originated:
In these, “ADR” lines define header lines which are to be analyzed for recipient addresses, while “HDR” lines can carry anything which doesn't get output as envelope address. The “ADR” line contained addresses must be in brackets, and there can be only one address per such header. If there are more, only the first one is picked.

More details at Scheduler Administration Section 14.7.1, and at Scheduler Reference Section 22.7.

4.5. Boot-up Scripts

Add something like the following lines to boot-up scripts (/etc/rc.local or /etc/rc2.d/S99local or similar):

  if [ -r /etc/zmailer.conf ]; then
    . /etc/zmailer.conf
    if [ ${MAILSERVER-NONE} = NONE -a
         -x $MAILBIN/zmailer ]; then
      $MAILBIN/zmailer bootclean
      $MAILBIN/zmailer && (echo -n ' zmailer') >/dev/console

For SysV-init environments, see source-tree file: utils/zmailer.init.sh. You may want to comment out startup of the Sendmail daemon, if you have it to begin with.

4.6. Checking the Log Files

Start ZMailer:

  # $MAILBIN/zmailer

Keep an eye on the log files ($LOGDIR/router, $LOGDIR/scheduler), the $POSTOFFICE/postman/ directory for malformed message files, and $POSTOFFICE/deferred/ in case of resource problems.

4.7. Crontab

See Figure 4-7 for three crontab entires for the root to run. Those are:

  1. This will resubmit messages that have been deferred with no useful processing possible at time of deferral. Adjust the re-submission interval to suit your environment. Having files in “deferred” state is a sign of troubles! Always investigate!

  2. This cleanup is to regularly clean out the $POSTOFFICE/public/, and $POSTOFFICE/postman/ directories.

  3. The automatic log-file trimmer/rotater is a good idea to have, but you need to customize it for your environment. More of that below.

You may want to hard-wire the location of the zmailer script.

Figure 4-7. ZMailer related crontab entries for root user

  # Two ZMailer related root's CRONTAB entries:
  28 0,8,16 * * * . /etc/zmailer.conf ; $MAILBIN/zmailer resubmit
  7  4      * * * . /etc/zmailer.conf ; $MAILBIN/zmailer cleanup
  # This third one will not per default be installed into your system
  0  0      * * * . /etc/zmailer.conf ; $MAILBIN/rotate-logs.sh

4.8. Trim-down of Logging

Once satisfied that things appear to work, you may want to trim down logging: there are four kinds of logging to deal with:

  • Router logs:

    Usually kept in $LOGDIR/router. This is the stdout and stderr output of the router daemon. If you wish to turn it off, see $MAILSHARE/cf/standard.cf for routine dribble()}, and especially its invocations! Alternatively use -L /dev/null to divert everything to the “dev null.”

  • Scheduler logs:

    Usually kept in $LOGDIR/scheduler. Same as router. The scheduler prints there only when it feels bad about something.[2]

  • Syslog Control ZENV variable:

    ZENV variable $SYSLOGFLG contains a set of single-character flags: “S”, “C”, “R”, and/or “T”. FIXME! FIXME! Explain Smtpserver/sCheduler/Router/Transport agents!

  • General Mail Logs:

    Usually kept in syslog files, depending on how you have configured the syslog utility (/etc/syslog.conf). All ZMailer programs log using the LOG_MAIL facility code for normal messages. You can deal with this specifically in your syslog configuration file on systems with a 4.3bsd-based syslog. The following reflects the recommended configuration on SunOS 4.0:

      mail.crit   /var/log/syslog
      mail.debug  /var/log/mail/mail.syslog

    For pre-4.3bsd-based syslogs, you may want the syslog log file to be just for important messages (e.g. LOG_NOTICE and higher priority), and have a separate file for informational messages (LOG_DEBUG and up).

  • By default, the postmaster will not receive a copy of all bounced mail; this can be turned on selectively by simply editing the various canned forms used to create the error messages. These forms are located in the FORMSDIR (proto/forms in the distribution, or $MAILSHARE/forms when installed). You should review these in any case to make sure the text is appropriate for your site.

Chapter 5. Installation to Clients

This section describes the installation at clients.

5.1. Required Files

The following files/programs are needed on clients:


The $MAILSERVER variable may be set to the mail server host's name. This is not required as mailq will usually be able to discover this by itself.


to submit mail


should be installed in the site's local bin so people can query the mail server. (Remember to update /etc/services)


This directory from the server should be mounted and writable.

5.2. Mounting $MAILBOXes and/or $POSTOFFICE/ Hierarchies via NFS

This is mostly for client machines, but the NFS may plaque you also at servers.

If you for some obscure reason are mounting $MAILBOXes and/or $POSTOFFICE hierarchies via NFS, do it with options to disable various attribute caches:

    alias:    noac

The best advice is to NOT to mount anything over NFS, but some people can't be persuaded…

Lots of things are done where file attributes play important role, and they are extremely important to be in sync! (Sure, the noac slows down the system, but avoids errors caused by bad attribute caches.)

If you are mounting people's home directories (~/.forward et. al.) via NFS, consider the same rule!

Often if the mail folder directory is shared, also one of following (depending upon the system):


Chapter 6. ./configure options

configure options of ZMailer package, per version 2.99.55.

The configure script has three kinds of parameters for it:

6.1. Used environment variables

User environment variables


Using this is alternate for using --with-zconfig=../ option. Not needed if the --prefix= derived $MAILSHARE/zmailer.conf is used.

CC="command", CFLAGS="options"

Obvious ones, compiler, and possible “-g -O” flags…


Not normally needed — builds dependencies


If you want to pre-define where your inews program is — for possible use of usenet channel.

Recycled ZENV variables (from $ZCONFIG file):

  For these see  SiteConfig(.in)  file


6.2. Options for various facilities

Options for various facilities


The only really mandatory option, gives actually defaults for ZENV variables: $MAILSHARE, $MAILVAR, $MAILBIN.


Compile with GCC even when you have “cc” around.


Where the run-time zmailer.conf file is located at (and with what name). This is the only hard-coded info within libraries and thus programs using them. Everything else is run-time relocatable by means of using “variables” listed in this file.

Default: $MAILSHARE/zmailer.conf

Lots of ZCONFIG environment variables are pre-set from values present in pre-existing file.

When environment variable ZCONFIG is set and exported to the ./configure script, use of --with-zconfig=no will set the location of the file, but prevents pre-load of various values from it to the auto-configuration environment.


Without this option the configuration will load ZENV variable values from possibly existing ZCONFIG file.

Default: values will be preloaded without this option.


Overrides system-dependent location of the user mail-boxes. Defaults are looked up thru list of directories:

First found directory will be the default — or then system yields /usr/spool/mail.


Overrides system-dependent location of the $POSTOFFICE directory under which system stores queued email. Will try directories /{usr,var}/spool/postoffice/ to see, if previously installed directory tree exists. Default will be /var/spool/postoffice/ in case there is no previously created postoffice directory.

--with-mailshare=/DIR/PATH, --with-mailvar=/DIR/PATH, --with-mailbin=/DIR/PATH

These are overrides for values derived from --prefix=/DIR option, or possibly pre-loaded from ZCONFIG file.



Explicit value to replace $LOGDIR ZENV value and/or to override default value of: /var/log/mail/


If you want to use usenet channel, you need to name NNTP server into which you feed news with NNTP.


Overrides for default location(s) of sendmail program. ZENV variable $SENDMAILPATH can be overridden with this.


Overrides for default location(s) of rmail program. ZENV variable $RMAILPATH can be overridden with this.


Obsolete option regarding providing into in ZENV variable to yield system internal names auto-magically for the SMTP transport channel uses, and also for the router to see, if destination IP address is local at the system.

Usage of this option may become necessary at load-balance clusters, but even then, setting the value to the ZCONFIG file is easier than pre-configuring them.


Use system malloc() library, don't compile own: Alternate for using: --with-libmalloc=system This is default.


Where “LIBNAME” is one of:


System malloc() as is.


Bundled "libmalloc" without debugging things.


Bundled "libmalloc" with debugging things.


Want to use YP, and has it at default locations

--with-yp-lib='-L... -lyp'

If needed to define linking-time options to find the YP-library.


If UMich/NetScape LDAP are available thru DIRPREFIX/include/ and DIRPREFIX/lib/ locations, this is a short-hand to find the interface — with files in the system primary include and lib locations, /usr is a special value which will be ignored. There is no default value for DIRPREFIX.


Special over-rider for compilation include directory of LDAP


Special over-rider for linkage library directory of LDAP


Disable PAM(3) facility from becoming auto-configured even when its API headers and libraries are present.


At systems where the local file-system is log-based/journaling, doing fsync() is wasteful. This disables fsync() in cases where it is not needed. (In others it may boost your system performance by about 20% — with dangers… On the other hand, once a system disk(?) fault which hang mailer at spool directory access did cause severe damage all over, and probably use of this option would not have made any difference; fsck was mighty unhappy.)


If your system is not very modern, you may consider using this option to compile in a resolver from bind-4.9.4-REL. On the other hand, if your system is modern, it may have even newer resolver in it. At such time, don't use this!


Use IPv6 at things where it is supported. This is often highly experimental, although many subsystems in ZMailer are built with getnameinfo() et.al. interfaces, which works both on IPv4 and IPv6.


If the system needs more support for user-space IPv6 things, this generates those.


Don't use maillock(3) even if system has it. (Solaris maillock(3) is ok, some early Linux versions weren't…)


Some systems dislike getting RFC-822 headers with form of:

   "Headername: <TAB> value"
With this option, no TABs are used and instead “ordinary” space character is used.

In real life, this feature is superseded by the router to always TABifying all headers, and the transport-agent header write-out to untabifying them, if ZENV variable RFC822TABS= has value “0”.

--with-tcp-wrappers, --with-tcp-wrappers=/DIR/PATH

Optional =/DIR/PATH value gives directory where there are tcpd.h and libwrap.a files. Without value this option looks for several common locations for those files, and if finds them, yields compile and linking hooks,


On some systems with good mmap(2) with MAP_FILE|MAP_SHARED,” and well behaving munmap() it does make sense to replace read()/write() thru a file-descriptor to the file with mmap() — however that requires munmap() not to scrub away in-mapped blocks any more actively, than the buffer-cache works at read()/write() blocks.

This was default for a while, however most systems don't have really well-behaved munmap()s :-/ (Perhaps IBM AIX is the only exception ?)


Set 'CHECK_MB_SIZE' #define for mailbox.c compilation, and expect checkmbsize() function to be found via --with-generic-lib= referred library.


Use private/ sub-directory in a part of smtpserver program compilation.


Use private/ sub-directory in a part of mailbox program compilation.

--with-getpwnam-library="-L... -l..."

Certain sites have expressed wishes to use their own libraries to replace the standard getpwnam() (and possibly getpwuid()) routines. These are used in router, scheduler, mailbox, hold, and vacation programs.

These programs use getpwnam() libary call to look up various customer user-names to whatever the system needs them for.

For ZMailer needs the library must support user-ids:

  • root

  • daemon, or daemons

  • nobody

and whatever others your local system magic needs.


This parameter allows ubiquitous -I/... options to be used in all program compilations throughout the package.


This parameter allows ubiquitous -L/... options to be used in all program linkages throughout the package.

--with-openssl, --with-openssl-prefix="/dir/prefix", --with-openssl-include="/dir/incl", --with-openssl-lib="/dir/lib"

Search for, and use OpenSSL, if it can be found. (For optional in and outbound SMTP traffic encryption on the Internet.)

--with-whoson, --with-whoson="/dir/prefix"

This does explicit integration with whoson server; see the whoson-*.tar.gz file in the contrib/ sub-directory.

6.3. Runtime ZENV Variables

Runtime ZENV Variables


ZCONFIG is the pathname of the configuration file specifying all the other host-dependent information needed by ZMailer programs. This file is created from the Config file in the distribution (the file you are reading right now), and contains variable assignments in an sh-compatible format.


MAILBIN is the directory hierarchy containing all ZMailer binaries. Usually /usr/lib/mail/bin or /local/lib/mail.


MAILSHARE is the directory hierarchy containing site-wide configuration files and databases. Usually /usr/lib/mail or /local/share/mail.


MAILVAR is the directory that will contain machine-specific configuration files and databases. Usually /usr/lib/mail or /var/db/mail or /local/share/mail.


MAILBOX is the directory containing all the user mailboxes. This is defaulted inside the mailbox.c program (currently /var/mail) and may be overridden here. Usually /usr/spool/mail or /var/mail.


POSTOFFICE is the directory hierarchy used to manipulate message files, where runtime activity takes places. Usually /usr/spool/postoffice or /var/spool/postoffice.


Multiple LOWER priorities on message routing can be defined by creating $POSTOFFICE/<component-of-$ROUTERDIRS> -directories. Routers process first $POSTOFFICE/router/ -directory, and once it is empty, files from subsequent dirs. See mail(3) mail_priority These can be only under the $POSTOFFICE.



When defined, ROUTERDIRHASH submits messages immediately into the 'A' thru 'Z' subdirectory of whatever directory (e.g. see ROUTERDIRS). IF NO SUCH "hash subdirs" EXIST, MESSAGE SUBMISSION WILL FAIL! (The value must be "1" for this to take effect!)


LOGDIR is the directory where log files will appear. Usually /usr/spool/log or /var/log.


MANDIR is the top of the manual directory hierarchy where manual pages for the ZMailer programs are installed. Usually /usr/man or /local/man.


# LIBRARYDIR is the place for storing libzmailer.a, which can be used to # create programs which use Zmailer's zmailer(3) (aka: mail(3)) -library. LIBRARYDIR= @libdir@


# INCLUDEDIR is the place for storing zmailer.h -- a copy of include/mail.h # and it is used in conjunction with the libzmailer.a .. INCLUDEDIR= @includedir@


# SMTPOPTIONS are command line options given to the smtpserver when started # from the zmailer shell script. The intent is that if you want non-default # address verification options they can be specified here. The default # value is "-sve". This is also used, when invoking ``sendmail'' with # "-bs" option. #SMTPOPTIONS= "-l ${LOGDIR}/smtpserver" SMTPOPTIONS= @SMTPOPTIONS@


# ALLOWSOURCEROUTE (when present) stops the system from ignoring # the old RFC821/822 source routes of type: @a,@b:c@d; By "ignoring" # we mean here that system chops away "@a,@b:" and uses only: c@d # This is done at all input portals; smtpserver, and at sendmail/rmail. #ALLOWSOURCEROUTE=


# SCHEDULEROPTIONS are command line options given to the scheduler when # started from the zmailer shell script. The intent is that if you want # non-default logging options, the can be specified here. The default # value is "" # #SCHEDULEROPTIONS= "-l ${LOGDIR}/scheduler.perflog -S -H" SCHEDULEROPTIONS= @SCHEDULEROPTIONS@


# The SCHEDULERDIRHASH is magic thing to tell to the router that it # should move resulting files directly into hash subdir(s) of the # scheduler subsystem, and not only to the main-level. # Existence of this variable also overrides -H option(s) to # the scheduler. Value is the number of -H options. # If these hash subdirectories don't exist, system failure happens! SCHEDULERDIRHASH=1


# The SCHEDULERNOTIFY defines, where is a socket at which the scheduler # listens for PF_UNIX/SOCK_DGRAM messages telling paths to new jobs. # The router(s) inform the scheduler of new jobs. SCHEDULERNOTIFY=@POSTOFFICE@/.scheduler.notify


# The ROUTERNOTIFY defines, where is a socket at which the router # listens for PF_UNIX/SOCK_DGRAM messages telling paths to new jobs. # The injection library informs the router queuing subsystem of new jobs. ROUTERNOTIFY=@POSTOFFICE@/.router.notify


# ROUTEROPTIONS are command line options given to the router when started # from the zmailer shell script. The default values are "-dkn 4" #ROUTEROPTIONS= "-dkn 4" ROUTEROPTIONS= @ROUTEROPTIONS@


# MAILSERVER is the hostname of the remote machine where the postoffice is # located. This value is only needed in an environment with distributed file # systems, and if it exists will be used by the mail queue querying program # as the default name of the host to query. It is a way of overriding the # algorithm used by mailq in an NFS environment, or when you are running a # different kind of DFS. Usually undefined or a hostname. #MAILSERVER= neat.cs


# PUNTHOST is where mail that is supposed to go to a local address, but # no such address exists, is punted to. #PUNTHOST= relay.cs


# FORCEPUNT is for cases when the local machine under no circumstances # is to store any email locally, but send all such to this given address # (local host is a member on a "cluster" whose message store is at some # other cluster server, and said node handles "local" delivery for all # cluster members... *including* running pipes..) #FORCEPUNT= mailhost


# SMARTHOST is where mail that cannot be resolved or routed is punted to. # There used to be a variable for this, now a better way is to use 'routes' # database at which you put line: . smtp!smart.host.name # (That is: dot, white-space(s), "smtp!smart.host.name" )


# NOBODY is the unprivileged UID value. # This is absolutely necessary if setuid() will fail on your "nobody" account # uid (if it is -2, for example). Make sure that whatever value you give # here will work with setuid(). Values between 1 and 29999 will usually work. # BE CAREFULL WITH THIS! THE SYSTEM RELIES ON IT VERY MUCH IN DEED! # (On SunOS 4.1.x, the value of "-2" works the best, on Solaris the default # for nobody is 60001! If your system has "nobody" "account", use here the # name instead of number -- it should (usually) work) # -- Use a mapping via /etc/passwd, this is most generic.. NOBODY=nobody


# LOGLEVEL may be set to restrict the log output of the router to entries # whose tags are found in the specified string value. The currently known # tags are: # address: deferred: file: header_defer: info: recipient: #LOGLEVEL= "file: recipient:" LOGLEVEL= "deferred: file: header_defer:"


# Builtin USENET channel uses NNTPSERVER variable (depending upon your # inews ..) to send the artickle to.. NNTPSERVER= @NNTPSERVER@


# Builtin USENET channel uses NNTPSERVER variable (depending upon your # inews ..) to send the artickle to.. INEWSBIN= @INEWS@


# Where the sendmail (compability one) shall be located ? SENDMAILPATH= @SENDMAILPATH@


# Where is the rmail to be located at ? RMAILPATH= @RMAILPATH@


# MAILBOX locking scheme -- no configuration option (yet) # See man-page of mailbox.8 for details; the order of key-chars # is meaningfull: # `.' Dotlock scheme for mailboxes at $MAILBOX/ directory # `F' flock() locking of files (and perhaps mailboxes) # `L' lockf() locking of files (and perhaps mailboxes) # `:' Separates the two parts of the parameter; left part # is for the mailbox locking, and right part is for # all other kinds of files. # # We use compiled-in defaults at the mailbox program! # Following examples are for flock(), and lockf() systems with their # respective defaults. ( Systems capable to use both will use lockf() ) #MBOXLOCKS=".F:F" #MBOXLOCKS=".L:L"


# The SELFADDRESSES is a comma separated list of IP address literals # listing all of our acceptable IP addresses (Comma because IPv6 uses # colon for short-hand notation..): # For usual (IPv4) universe, no addresses are needed listing, however # for IPv6 it may be necessary - likewise if you want to use cluster-mode, # you may want to list all *cluster* addresses here - nodes know only # their local ones, after all.. (See: doc/guides/etrn-cluster) #SELFADDRESSES=[],[],[ipv6.::] SELFADDRESSES=@SELFADDRESSES@


# What kind of DB type we prefer to use ? We can support several, # after all... btree/ndbm/gdbm ... (DBEXT: pag/db/dat) DBTYPE=@DBTYPE@


# What kind of DB type we prefer to use ? We can support several, # after all... btree/ndbm/gdbm ... (DBEXT: pag/db/dat) DBEXT=@DBEXT@


# What kind of DB type we prefer to use ? We can support several, # after all... btree/ndbm/gdbm ... (DBEXT: pag/db/dat) DBEXTtest=@DBEXTtest@


# The characterset to be used as a default when turning 8-bit containing # headers to MIME-2 headers -- and what to say at the default generated # "Content-Type: text/plain; charset=XXXX" -header in case the original # message was not of MIME, and still had 8-bit chars... DEFCHARSET=ISO-8859-1


# We want those nice tabs between the header field name and value # The task of generating TABs or SPACEs is at TA *writeheaders(). # Value '0' here yields expansion of possibly existing header resident # line-start TABs. There is no mechanism to turn line-start SPACEs # to TABs with any other value stored here. RFC822TABS=@RFC822TABS@


# If the following limitations are exceeded then zmailcheck # will sent an alert. # Limit on the Router Queue MAX_NR=1000 # Limit on Transport Queue MAX_NT=1000 # Load times 100 MAX_LOAD=300


# SYSLOGFLG tells which systems use syslog to log things: # Set of chars which are as follows: # S smtpserver and @SENDMAILPATH@ # R router # T transport agents # C scheduler completion of a message # #SYSLOGFLG=SRT SYSLOGFLG=RT


# Per default, ZMailer uses ``daemon'' userid when it wants to # operate in ``runastrusteduser()'' mode. Finding that userid # (or rather its numeric uid) can be a bit difficult, and if it # *fails*, apparently uid 65535 will be used. # #TRUSTEDUSER=daemon


# Use ORGDOMAIN in ZENV if the system can't generate # MIME multipart boundary string contained host/domain ids # automagically... # #ORGDOMAIN=my.local.domain


# Depending, are you running strange private customer account databases # hooked (only) into 'mailbox', or not, make sure following is non-empty # if you *are* using private databases, as then ZMailer's router won't # claim wronly userid to be nonexistent.. These shunted tests look for # HOMEDIRECTORY, which might be nonexistent thing at such funny systems... # An EMPTY string means "this is NORMAL unix": # # (A "bug" is that this isn't automatically substituted, but non-void # content gives behaviour that has been around for quite a while...) # ROUTEUSER_IN_ABNORMAL_UNIX="@ROUTEUSER_IN_ABNORMAL_UNIX@"


# Some sites (well, one FUNET site), has LISTSERV, this is for # configuring that subpart of the aliases.cf scripts: #LISTSERV=/v/net/listserv.funet.fi

Chapter 7. Verifying the System


Chapter 8. Installing Whoson Service


The need for this, and other similar "POP-before-SMTP" approaches has pretty much been obsoleted by development of SMTP AUTHENTICATION, and doing it in particular under TLS wrappers on SUBMISSION port.



Default location is $MAILSHARE/zmailer.conf, and it can be changed with --with-zconfig= option.


At least this is the theory, practice may be different, though.