## 24.13. 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 (Section 35.17) 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 such as 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 reread 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 reread 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.[73] 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 (Section 45.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 (Section 1.21) command. (You could change it to write to the system's syslog by calling logger(1) instead of write. Or use xmessage (Section 36.26) to pop a notice window onto someone's X Window System console. Etc., etc.)

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

The script uses numbers (0, 1, 15) instead of signal names (EXIT, HUP, TERM). This is for portability to older Unix shells that don't understand names in trap commands. But if you write a script like this on a newer system, use signal names if you can.

Go to http://examples.oreilly.com/upt3 for more information on: watchq

`/dev/null` Section 43.12

>     #! /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

Now let's run the script. 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 reread:

`kill` Section 24.12

>     % echo office main lobby > /usr/local/lib/watchqs
>     % watchq &
>     [1] 4363
>        ...
>     % echo main lobby > /usr/local/lib/watchqs
>     % kill -HUP 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 -HUP (or 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 (Section 24.6). Also, the error messages that egrep (Section 13.4) searches for may not catch all problems, and they are system-dependent. If you use xmessage, a user who's away from his workstation could come back to tens or hundreds of windows; you might want to make the script pause until the user acknowledges a window. But this script is just a demonstration -- to show a great way to write a quick-and-dirty daemon.

-- JP

Copyright © 2003 O'Reilly & Associates. All rights reserved.