[Chapter 38] 38.11 Printer Queue Watcher: A Restartable Daemon Shell Script
Author: Jerry Peek, Tim O'Reilly & Mike Loukides
Date: 1998-08-04T21:49:47Z

#

| Chapter 38 Starting, Stopping, and Killing Processes |

## 38.11 Printer Queue Watcher: A Restartable Daemon Shell Script

[This article may not appear to have a lot to do with the subject of this chapter, but it illustrates the other side of signal handling - what a program or shell script can do when it receives a signal. Jerry's script uses the trap (44.12) command to catch several different signals, and act differently depending on whether the signal is a "hangup" (HUP, or signal 1) or a TERM (signal 15). -TOR ]

UNIX systems run "daemon" programs like cron(8) and syslogd(8) that wait in the background, looking for work to do. Many daemons read configuration files when they start up. System administrators sometimes change the configuration files and want the daemon to re-read the file. One way to do that is by terminating and restarting the program - but that's ugly and also means the daemon won't be running for a few seconds until it's restarted. So, many daemons are designed to re-read their configuration files and/or restart themselves when they get a signal (usually, the HUP signal, signal 1). System administrators do this by getting the daemon's process ID number and sending the signal with the kill command. Because the daemon "catches" the signal, the daemon isn't actually killed.

You can run a shell script as a daemon by putting it in the background. [4] Here's a simple example, a shell script named watchq. It reads a file full of printer queue names and stores it in a shell variable. Every 30 seconds, it runs lpq (43.2) on all printer queues listed. If any queues have an error, the script echoes a message and the output of lpq to a particular user with the write (1.33) command.

> [4] It's usually also a good idea to be sure that the input and outputs are redirected (13.1, 45.21) away from the terminal, maybe to the system console instead. On systems and shells that kill background jobs when you log out, use nohup (38.18).

After the script has run for a while, the printer named office goes down. I edit the watchqs file and remove that printer so the poor user lisa won't keep getting complaints about it. Then I send a signal to have the file re-read:

    /dev/null
    -

    kill
    % cat watchq
    #! /bin/sh
    # watchq - "daemon" script that watches printer queue(s) for errors
    temp=/tmp/WATCHQ$$             # Holds output of lpq
    watch=/usr/local/lib/watchqs   # Queue names to watch
    writeto=lisa                   # User who gets notices about printer
    queues="`cat $watch`"          # Put list of queue names in $queues
    trap 'queues="`cat $watch`"' 1 # Reset $queues if we get a SIGHUP
    trap 'rm -f $temp; exit' 0 15  # Clean up temp file when killed

    # Loop forever (until someone kills script):
    while :
    do
        for queue in $queues
        do
            lpq -P$queue >$temp
            if egrep '(out of paper|error|warning)' $temp >/dev/null
            then echo "PRINTER QUEUE $queue:" | cat - $temp | write $writeto
            fi
        done
        sleep 30
    done
    % echo office main lobby > /usr/local/lib/watchqs
    % watchq &
    [1] 4363
       ...
    % echo main lobby > /usr/local/lib/watchqs
    % kill -1 4363
       ...
    % kill 4363
    [1]    Exit -48             watchq

In real life, the watchq script might be started from a system file like /etc/rc.local when the system reboots. Lisa would probably edit the watchqs file herself. The username that's notified by write might also be resettable with a kill -1.

This isn't foolproof and you can run into subtle problems. For instance, the write command may not work on some UNIXes if it's running from a daemon without a controlling tty (38.6). Also, the error messages that egrep (27.5) searches for may not catch all problems and are system-dependent. But this script is just a demonstration - to show a great way to write a quick-and-dirty daemon.

- JP

38.10 Destroying Processes with kill |  | 38.12 Killing All Your Processes