Does this site look plain?

This site uses advanced css techniques

[postfix logo] We've been happy users of the excellent Postfix mail server for some time -- it's so much of a nicer experience than Sendmail. In the process we've developed a minor technique for managing the various small database files that Postfix uses, and we've been asked to elaborate on them. Hence this Tech Tip.

BUT - this is not a tutorial on Postfix, nor is it designed to provide configuration advice. This is exclusively about how to manage the configuration that you've come to on your own.

Building .db files from text files

The postfix main.cf configuration file can specify the location of several small database files (the "maps"):

virtual_alias_maps =
        hash:/etc/postfix/virtual

transport_maps =
        hash:/etc/postfix/transport

relay_domains =
        $mydestination,
        $mydomain,
        hash:/etc/postfix/relays
...

and so on. These are "hash" maps that are that are maintained in Berkeley DB format (there are other map formats, but we're not addressing them here). The files are maintained in regular text format, then "compiled" into the DB format with the postmap command, which is provided with Postfix.

Building any given map file is easy enough, and we'll use the transport file as an example:

# cd /etc/postfix

# vi transport
example.com              smtp:[mail.example.com]:25
...

# ls -l transport*
-rw-r--r--    1 root     root         7292 Nov 30  2002 transport

# postmap transport        creates "transport.db" from "transport"

# ls -l transport*
-rw-r--r--    1 root     root         7292 Nov 30  2002 transport
-rw-r--r--    1 root     root        12288 Sep 26 14:24 transport.db

Subsequent edits of this file simply require running postmap transport command again to rebuild the .db file.

But as the number of these .db files increases, it gets harder to keep track of which ones must be rebuilt after a handful of edits: configuration changes are often made in groups. It's possible to create a small shell script to do this:

# cd /etc/postfix

# cat rebuild-maps
cd /etc/postfix
newaliases              the "aliases" file is a special case
postmap transport
postmap relays
postmap virtual
...

# chmod +x rebuild-maps

# ./rebuild-maps

Though this is simple, it rebuilds files even when not necessary. We have used the make command for so long that employing it here feels really natural to us. It's one of the oldest parts of the UNIX software-development toolkit.

Building a makefile

A "makefile" is a small text file that describes the dependency relationships between various files, as well as instructions on how to rebuild target files from source files. That's exactly what we're doing here: in the example we showed earlier, "transport" is the source file and "transport.db" is the object file.

 1:    MAPS = relays.db aliases.db transport.db relocated.db \
 2:            virtual.db sender_checks.db rejected_recips.db \
 3:            helo_access.db
 4:
 5:    all : $(MAPS)
 6:
 7:    aliases.db : aliases
 8:    tab     newaliases
 9:
10:    %.db : %
11:    tab     postmap $*

We'll review this makefile, though not strictly in top-to-bottom order. We use GNU Make exclusively - older makes don't have quite the same syntax for all the parts.

Lines 1..3
Define a make variable which lists all the database files we're building: use of $(MAPS) later in the file refers to the whole list as a string.
Line 5
This defines an actual dependency: the "target" (on the left side of the colon) is all, and it depends on everything on the right side of the colon. This means that typing make all requires that all the elements in the $(MAPS) list be made up to date.
Lines 7..8
This defines a specific rule to build a target. If the target (aliases.db) is either missing, or is older than aliases, the actions that follow are run one command as a time. Though most of the maps used by Postfix are built with the postmap command, the aliases file is a special case.
Lines 10..11
This defines a generic rule that converts from FILE (the dependency) to FILE.db (the target) in the general case, and the action - the tab-indented line following - is a command that helps bring this about. Here, it's the postmap command, and $* is a built-in variable that refers to the dependency.

When the make command is run, it finds the first target in the file and uses it as the overall goal. In this case, it's all. This isn't a "real" target (in the sense of actually creating a file with that name), but it's a common catch-all virtual rule.

The dependencies are all the files on the right side - the elements of the $(MAPS) macro - and each one goes through the same process. The list of all rules is consulted, and a "best match" is found if possible. A more-specific rule always matches over a less-specific rule: even though the lines 10.11 rule could build aliases.db from aliases with the postmap command, the most specific rule just above it is used instead.

Once the makefile is constructed, one simply edits the text files as needed, then type make.

IMPORTANT - where a tab is indicated, it must be present - use of spaces is not equivalent. Mistakenly using spaces instead of a tab is one of the leading causes of broken makefiles. This is not obvious.

More tidbits

We are not at all anxious to get into the "make tutorial" business, but there are a few additional notes we'll touch on.

Other resources