A Form Letter Utility for UNIX
Paul A. Sand
A considerable amount of electronic mail sent by system
administrators
is repetitious: answers to frequently asked questions,
notification
of account security problems, gentle reminders concerning
proper system
use, requests for information, thank-you notes, and
so on. To lessen
the tedium involved in retyping such email messages,
a useful tool
would be an electronic analog of form letters: common
messages that
can be quickly sent to one or more addresses with a
minimum of effort.
This article presents a form letter utility written
in Perl for UNIX
systems. It can be used to send form letters to one
or more recipients
with a single command, greatly decreasing the drudgery
involved in
composing messages from scratch each time. Since form
letters can
be composed in advance and revised at leisure, it is
easy to ensure
that the language they contain remains precisely worded,
complete,
consistent, accurate, and as courteous and professional
as is appropriate.
The utility also allows form letters to contain dynamic
text generated
when the letter is mailed.
Design Goals and User Interface
A form letter utility should offer significant advantages
over what
can be obtained by normal UNIX redirection. For example,
if you need
to notify a couple of users that they have disk quota
problems and
(perhaps) offer some general hints on how they could
use disk space
more efficiently, you could create a file named overquota,
and mail it to the users with a command like
% mail dmr bwk < overquota
The overquota file could be saved for reuse later,
of course, so that you would not need to retype it the
next time a
similar situation arises.
The utility presented here can be invoked equally as
simply:
% formletter overquota dmr bwk
This will send the form letter specified in the "template
file" overquota to the users dmr and bwk.
The differences between this and the simple UNIX redirection:
The mail will have a descriptive "Subject":
line in the header, specified in the template file.
The mail will be sent separately to the two users. Neither
user will be aware of the other's quota problems --
not, at least,
by reading the mail. Each will be under the impression
that he or
she was the sole recipient.
The mail will have a personalized greeting line, typically
the recipient's first name, if that can be looked up
in the password
file. For example:
Brian --
If that's unsuitable, the greeting can be customized
on an individual basis, or deleted.
The mail can include other specific information that
might be relevant to to the recipient; in this example,
it could include
information about the user's current disk quota, his
or her current
disk usage, the names and sizes of the user's biggest
files, and so
on. The information is obtained by running a Perl script
contained
in the template file itself; this powerful mechanism
allows the mail
to contain just about any information that the sender
has permission
to access, in addition to the unchanging text.
The mail will include the sender's "signature"
at the end: either the contents of a preset signature
file or, if
that's not present, the sender's name and address.
None of these benefits is available with the simple
redirection scheme
(at least not without making it significantly less simple).
The formletter
utility also offers an organizational benefit: the program
itself
knows where the templates it uses are stored, so the
user doesn't
have to remember where they are (this is not the case
with the redirection
scheme).
The formletter program is invoked with the syntax:
formletter template address ...
which will send the form letter specified in template
to one or more specified addresses. If the template
is absent, the
program produces a usage message describing the command
syntax and
listing all templates currently on file.
Form letter template files are assumed to be stored
in the sender's
~/lib/formletters directory. Listing 1 shows a simple
template
file containing a subject specification and the text
of the form letter
to be sent. (In this case, it's the notice I send when
the COPS program
suite detects that a user has a .netrc file that others
can
read.)
There are only two things necessary to know in order
to create such
simple templates:
Rule 1 -- The subject specification is a
single line in the template file with the syntax:
Subject:[subject line]
and the only placement requirement is that such a line
precede the formletter text specification. If the subject
line
contains double quotes, they should be preceded with
a backslash.
Rule 2 -- The text specification starts
with the single line
Text:
All lines following this line to the end of the template
file are treated as text to be included in the form
letter.
Alternate Greetings
By default, the utility adds a "greeting"
line at the top
of the sent message consisting of the recipient's first
name, followed
by a couple of hyphens. It guesses the first name by
looking up the
recipient's ID in the password file and splitting off
the first word
in the GCOS ("real name") field -- unless,
of course,
the recipient's specified address contains an @, in
which case
it assumes the recipient is located at another site,
and would not
be present in the local password file.
This first-name informality is usually appropriate at
my educational
site, where most communication is with students, faculty,
and co-workers.
Even so, it quickly became clear that it would be desirable
to override
the default greeting in certain cases. For example,
the user wfc
is in the password file as "William F. Costa,"
but I would
prefer that my mail refer to him as "Bill."
At the opposite
end of the formality spectrum, the University's president
is user
dfn, real name "Dale F. Nitzschke"; just to
be on the
safe side, I would prefer that he be greeted not as
"Dale"
but as "President Nitzschke."
Overriding greetings are set up in the optional file
~/lib/formletters/.greetings,
which, if present, contains any number of lines in the
form:
recipient:greeting
For example:
wfc:Bill
dfn:President Nitzschke
The .greetings file can also be used to specify
greetings for non-local recipients who aren't looked
up in the password
file.
Advanced Form Letters
In addition to subject and text specifications, form
letter templates
may optionally include eval sections containing Perl
code that
is to be executed once for each recipient. Any Perl
code can be specified
in the eval section; in the typical case, the code will
set
values for one or more Perl variables. Any occurrence
of those variable
names in the template's text section will be replaced
by the variable's
value in the outgoing mail. This simple scheme allows
very general
modification and personalization of the form letter's
content in addition
to the static text.
The eval section adds a third syntax rule for form letter
templates
to the two seen previously:
Rule 3 -- The eval section starts
with the single line
Eval:
and is terminated with a blank line. All lines between
the Eval: line and the blank line are treated as Perl
code
to be executed once per recipient. The eval section
must precede
the text section. The code in the eval section can use
the
Perl variable $whoto, which contains the address of
the user
to whom the form letter is being sent.
As a relatively simple example, Listing 2 shows a form
letter template
that warns a user of the imminent deletion of his or
her account.
In composing this letter, I wanted to warn the user
a couple of weeks
ahead of time, and have the letter mention the specific
deletion date.
The Perl statements in the eval section permit that
by computing,
via Perl's time and localtime functions, a list (@in2wks)
containing the components of the future date. (For C
programmers:
Perl's localtime function is analogous to ANSI C's localtime()
function; it returns the values identical to those specified
in C's
struct tm structure.) The routine uses the @moname and
@dname lists to convert the month and day-of-week numbers
returned
by localtime into the corresponding words.
The template's text section contains references to the
variables set
in the eval section; in the mail sent to the recipient,
$nukedate
will be replaced by the actual target deletion date.
Listing 3 shows a more complex example. This form letter
is sent to
inexperienced users who have (probably unwittingly)
stopped one or
more of their processes by typing Control-Z. (A number
of our
users come from environments where Control-Z does something
different, so this is a pretty common occurrence.) The
eval
section in the template runs the command ps -axuww which,
on
our system, outputs information concerning all processes
for all users.
A couple of calls to Perl's grep function filter out
only the
stopped processes for the recipient; the commands that
invoked the
stopped processes are extracted from this list and inserted
in the
mail sent to the user. (Portability note: this code
depends strongly
on the way the ps command is invoked on our system --
ULTRIX,
a BSD-like UNIX variant -- and makes assumptions about
the placement
of the user commands and process status in the output
from the ps
command. It may well not be portable to other systems
without modification.)
Perl Implementation
The formletter program is shown in Listing 4. Users
wishing
to install it at their location may want or need to
change the values
of some variables initialized near the top of the code.
Currently,
the code assumes:
form letter templates and the alternate greeting data
file are stored in the directory ~/lib/formletters;
change
the definition of $letterdict to use something else.
the sender's (optional) signature is in the file ~/.mailsig;
change the definition of $sigfile to use something else.
the underlying mailer is /usr/ucb/mail; change
$mailer if you want to use another one.
the program sleeps 30 seconds between mailings to multiple
users (our system performance degrades if too many messages
are sent
near-simultaneously); change the variable $sleepytime
if your
system behaves better, or worse.
the alternate greeting file is named .greetings;
change $altgreetfile if you prefer another name.
The program itself is relatively straightforward Perl
code. The key
to getting variable substitution in the text relies
on Perl's powerful
eval function, which executes the Perl code passed to
it as
an argument. The eval function is used in two ways:
(1) to
run the code contained in the eval section of the form
letter
template; (2) to do the variable substitution necessary
to convert
each line of the text section into the message which
is actually mailed.
Suggestions
The topics and text of form letters will vary greatly
from site to
site, depending on the nature of the user community,
system policies,
and other environmental factors. At this location, I've
used the utility
in the following general areas:
To notify users of security problems with their accounts
(as in Listing 1): poor passwords, bad directory or
file permissions,
and so on. (Depending on the seriousness of the problem,
the form
letter may ask them to fix the problem, or notify them
that I've fixed
the problem.)
To notify users of system-use problems (as in Listing 3
): runaway or stopped processes, too much disk space
used in the
spool or temporary directories, .forward files that
cause their
mail to bounce, and so on.
Administrative tasks (as in Listing 2): letting users
know of changes to their accounts, changes to system
software directly
affecting them, and so on.
Answers to frequently-asked questions. ("I just
got a 9600-baud modem, what phone number do I call?"
"Who
does my friend see to apply for an account?" "What
happens
to my account over the summer?" And so on.)
About the Author
Paul Sand is a system administrator for academic UNIX
systems
at the University of New Hampshire. He has worked with
UNIX systems
since 1984. He may be contacted via email as pas@unh.edu.
|