]>
git.saurik.com Git - apple/shell_cmds.git/blob - shlock/shlock.c
1 /* $NetBSD: shlock.c,v 1.3 1998/01/09 08:06:11 perry Exp $ */
4 ** Program to produce reliable locks for shell scripts.
5 ** Algorithm suggested by Peter Honeyman, January 1984,
6 ** in connection with HoneyDanBer UUCP.
8 ** I tried extending this to handle shared locks in November 1987,
9 ** and ran into to some fundamental problems:
11 ** Neither 4.3 BSD nor System V have an open(2) with locking,
12 ** so that you can open a file and have it locked as soon as
13 ** it's real; you have to make two system calls, and there's
16 ** When removing dead process id's from a list in a file,
17 ** you need to truncate the file (you don't want to create a
18 ** new one; see above); unfortunately for the portability of
19 ** this program, only 4.3 BSD has ftruncate(2).
21 ** Erik E. Fair <fair@ucbarpa.berkeley.edu>, November 8, 1987
23 ** Extensions for UUCP style locks (i.e. pid is an int in the file,
24 ** rather than an ASCII string). Also fix long standing bug with
25 ** full file systems and temporary files.
27 ** Erik E. Fair <fair@apple.com>, November 12, 1989
29 ** ANSIfy the code somewhat to make gcc -Wall happy with the code.
32 ** Erik E. Fair <fair@clock.org>, May 20, 1997
35 #include <sys/types.h>
37 #include <fcntl.h> /* Needed on hpux */
58 char *USAGE
= "%s: USAGE: shlock -f file -p pid [-d][-u]\n";
59 char *E_unlk
= "%s: unlink(%s): %s\n";
60 char *E_open
= "%s: open(%s): %s\n";
62 #define dprintf if (Debug) printf
65 ** Prototypes to make the ANSI compilers happy
66 ** Didn't lint used to do type and argument checking?
67 ** (and wasn't that sufficient?)
71 /* the following is in case you need to make the prototypes go away. */
74 char *xtmpfile
_P((char *, pid_t
, int));
75 int p_exists
_P((pid_t
));
76 int cklock
_P((char *, int));
77 int mklock
_P((char *, pid_t
, int));
78 void bad_usage
_P((void));
79 int main
_P((int, char **));
83 ** Create a temporary file, all ready to lock with.
84 ** The file arg is so we get the filename right, if he
85 ** gave us a full path, instead of using the current directory
86 ** which might not be in the same filesystem.
89 xtmpfile(file
, pid
, uucpstyle
)
96 char *cp
, buf
[BUFSIZ
];
97 static char tempname
[BUFSIZ
];
99 sprintf(buf
, "shlock%d", getpid());
100 if ((cp
= strrchr(strcpy(tempname
, file
), '/')) != (char *)NULL
) {
102 (void) strcat(tempname
, buf
);
104 (void) strcpy(tempname
, buf
);
105 dprintf("%s: temporary filename: %s\n", Pname
, tempname
);
107 sprintf(buf
, "%d\n", pid
);
110 if ((fd
= open(tempname
, O_RDWR
|O_CREAT
|O_EXCL
, 0644)) < 0) {
113 dprintf("%s: file %s exists already.\n",
115 if (unlink(tempname
) < 0) {
116 fprintf(stderr
, E_unlk
,
117 Pname
, tempname
, strerror(errno
));
118 return((char *)NULL
);
125 fprintf(stderr
, E_open
,
126 Pname
, tempname
, strerror(errno
));
127 return((char *)NULL
);
132 ** Write the PID into the temporary file before attempting to link
133 ** to the actual lock file. That way we have a valid lock the instant
134 ** the link succeeds.
137 (write(fd
, &pid
, sizeof(pid
)) != sizeof(pid
)) :
138 (write(fd
, buf
, len
) < 0))
140 fprintf(stderr
, "%s: write(%s,%d): %s\n",
141 Pname
, tempname
, pid
, strerror(errno
));
143 if (unlink(tempname
) < 0) {
144 fprintf(stderr
, E_unlk
,
145 Pname
, tempname
, strerror(errno
));
147 return((char *)NULL
);
154 ** Does the PID exist?
155 ** Send null signal to find out.
161 dprintf("%s: process %d is ", Pname
, pid
);
163 dprintf("invalid\n");
166 if (kill(pid
, 0) < 0) {
170 return(FALSE
); /* pid does not exist */
173 return(TRUE
); /* pid exists */
175 dprintf("state unknown: %s\n", strerror(errno
));
176 return(TRUE
); /* be conservative */
180 return(TRUE
); /* pid exists */
184 ** Check the validity of an existing lock file.
186 ** Read the PID out of the lock
187 ** Send a null signal to determine whether that PID still exists
188 ** Existence (or not) determines the validity of the lock.
190 ** Two bigs wins to this algorithm:
192 ** o Locks do not survive crashes of either the system or the
193 ** application by any appreciable period of time.
195 ** o No clean up to do if the system or application crashes.
199 cklock(file
, uucpstyle
)
203 int fd
= open(file
, O_RDONLY
);
208 dprintf("%s: checking extant lock <%s>\n", Pname
, file
);
211 fprintf(stderr
, E_open
, Pname
, file
, strerror(errno
));
212 return(TRUE
); /* might or might not; conservatism */
216 ((len
= read(fd
, &pid
, sizeof(pid
))) != sizeof(pid
)) :
217 ((len
= read(fd
, buf
, sizeof(buf
))) <= 0))
220 dprintf("%s: lock file format error\n", Pname
);
225 return(p_exists(uucpstyle
? pid
: atoi(buf
)));
229 mklock(file
, pid
, uucpstyle
)
237 dprintf("%s: trying lock <%s> for process %d\n", Pname
, file
, pid
);
238 if ((tmp
= xtmpfile(file
, pid
, uucpstyle
)) == (char *)NULL
)
242 if (link(tmp
, file
) < 0) {
245 dprintf("%s: lock <%s> already exists\n", Pname
, file
);
246 if (cklock(file
, uucpstyle
)) {
247 dprintf("%s: extant lock is valid\n", Pname
);
250 dprintf("%s: lock is invalid, removing\n",
252 if (unlink(file
) < 0) {
253 fprintf(stderr
, E_unlk
,
254 Pname
, file
, strerror(errno
));
259 ** I hereby profane the god of structured programming,
264 fprintf(stderr
, "%s: link(%s, %s): %s\n",
265 Pname
, tmp
, file
, strerror(errno
));
269 dprintf("%s: got lock <%s>\n", Pname
, file
);
272 if (unlink(tmp
) < 0) {
273 fprintf(stderr
, E_unlk
, Pname
, tmp
, strerror(errno
));
281 fprintf(stderr
, USAGE
, Pname
);
291 char *file
= (char *)NULL
;
293 int uucpstyle
= FALSE
; /* indicating UUCP style locks */
294 int only_check
= TRUE
; /* don't make a lock */
296 Pname
= ((Pname
= strrchr(av
[0], '/')) ? Pname
+ 1 : av
[0]);
298 for(x
= 1; x
< ac
; x
++) {
299 if (av
[x
][0] == '-') {
308 if (strlen(av
[x
]) > 2) {
309 pid
= atoi(&av
[x
][2]);
316 only_check
= FALSE
; /* wants one */
319 if (strlen(av
[x
]) > 2) {
329 fprintf(stderr
, USAGE
, Pname
);
335 if (file
== (char *)NULL
|| (!only_check
&& pid
<= 0)) {
340 exit(cklock(file
, uucpstyle
) ? LOCK_GOOD
: LOCK_BAD
);
343 exit(mklock(file
, pid
, uucpstyle
) ? LOCK_SET
: LOCK_FAIL
);