## 36.27. Shell Lockfile

Here's an efficient and portable way to create a lockfile from a shell script.[117] It's also an interesting demonstration of the way that Unix umasks and file permissions (Section 50.2) are handled.

> [117]Greg Ubben sent this idea.

A lockfile can be used when a particular program might be run more than once at the same time and you need to be sure that only one instance of the program can do something (modify some file, access a printer, etc.). To really do this right, the program needs to both test for the lockfile and create it (if it doesn't exist) in one atomic operation. If the test-and-set operation isn't atomic -- for instance, if a program tests for the lock file in one command and then creates the lock file in the next command -- there's a chance that another user's program could do its test at the precise moment between the first program's (non-atomic) test and set operations. The technique in this article lets you make a lockfile atomically from a shell script.

> NOTE: This technique doesn't work for scripts run as the superuser (root). It depends on the fact that a standard user can't write a file without write permisson. But root can write any file, whether it has write permission or not. If there's a chance that root might run your script, you might want to add a test of the UID -- by running the id command, for instance -- and be sure that the UID isn't 0 (the superuser's).

Let's say you have a script called edmaster; it edits a master configuration file named config. To be sure that two users can't modify the config file at the same time, the first edmaster checks whether the lockfile exists. If the lockfile doesn't exist, edmaster creates it and modifies the config file. When it's done editing, it removes the lockfile. If someone tries to run a second edmaster process, it sees the lockfile from the first edmaster, waits, and checks every few seconds to see if the lockfile is gone. Once the first edmaster removes the lockfile, the second edmaster can create the lockfile and do its editing of config. (Note that some editors -- for instance, nvi-1.79 under Linux -- automatically get a write and/or read lock before you to edit a file.)

Here are pieces of a script that check the lock, create it, and (later) remove it:

`2>` Section 36.16, `/dev/null` Section 43.12, `set` Section 35.25

>     # set name of this program's lockfile:
>     myname=`basename $0`
>     LOCKFILE=/tmp/lock.$myname
>        ...
>     # Loop until we get a lock:
>     until (umask 222; echo $$ >$LOCKFILE) 2>/dev/null   # test & set

>     do
>        # Optional message - show lockfile owner and creation time:
>        set x `ls -l $LOCKFILE`
>        echo "Waiting for user $4 (working since $7 $8 $9)..."

>        sleep 5
>     done

>     # Do whatever we need exclusive access to do...
>        ...
>     rm -f $LOCKFILE            # unlock

If another user tried to run edconfig, and jpeek had run edconfig first, she might see:

>     % edconfig
>     Waiting for user jpeek (working since Aug 23 14:05)...
>        ...a 5-second pause
>     Waiting for user jpeek (working since Aug 23 14:05)...
>        another 5-second pause...
>        ...then jpeek finishes and she can edit the file.

How does it work? Almost all the action is in the first line of the loop. A umask of 222 creates files that are read-only (mode `r--r--r--`). Because the umask 222 command is run in a subshell (Section 24.4), it affects only the lockfile that's created in the subshell at the top of the loop. The rest of the shell script keeps its normal umask. And if the redirection fails (because the lock file exists), only the subshell will abort -- not the parent shell running the script.

If the lockfile already exists (because another process has created it), the loop executes `sleep 5`; five seconds later, it tries to create the lock. If the lockfile exists, it will be read-only -- so the command `echo $$ >$LOCKFILE` will return a nonzero status. A nonzero status is what keeps an until loop (Section 35.15) running. Once the other process (which has the lock) removes the lockfile, the echo command in the subshell writes the shell's process ID number into the lockfile, and the until loop terminates.

But if the lockfile is read-only, how can it ever be created? That's the other interesting part of this technique. The umask applies to the file only as it's created; if the file doesn't exist, the umask doesn't apply to it (yet) and the file can be created. In fact, you can create a file with mode 000 by typing:

>     $ (umask 666; echo hi > afile)
>     $ ls -l afile
>     ----------  1 jpeek   wheel   3 Aug 23 14:08 afile
>     $ touch afile
>     -rw-rw-r--  1 jpeek   wheel   3 Aug 23 14:10 afile

--JP

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