Cover V02, I03
Article
Listing 1
Listing 2
Listing 3
Listing 4

may93.tar


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.


     



  •