1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /* Copyright (c) 1988 AT&T */
22 /* All Rights Reserved */
23 /*
24 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27 /*
28 * Copyright 2006-2020 J. Schilling
29 *
30 * @(#)lockit.c 1.23 20/09/06 J. Schilling
31 */
32 #if defined(sun)
33 #pragma ident "@(#)lockit.c 1.23 20/09/06 J. Schilling"
34 #endif
35 /*
36 * @(#)lockit.c 1.20 06/12/12
37 */
38
39 #if defined(sun)
40 #pragma ident "@(#)lockit.c"
41 #pragma ident "@(#)sccs:lib/mpwlib/lockit.c"
42 #endif
43
44 /*
45 * Process semaphore.
46 * Try repeatedly (`count' times) to create `lockfile' mode 444.
47 * Sleep 10 seconds between tries.
48 * If `lockfile' is successfully created, write the process ID
49 * `pid' in `lockfile' (in binary)and return 0.
50 *
51 * If `lockfile' exists and it hasn't been modified within the last
52 * minute, and either the file is empty or the process ID contained
53 * in the file is not the process ID of any existing process,
54 * `lockfile' is removed and it tries again to make `lockfile'.
55 * After `count' tries, or if the reason for the create failing
56 * is something other than EACCES, return xmsg().
57 *
58 * Unlockit will return 0 if the named lock exists, contains
59 * the given pid, and is successfully removed; -1 otherwise.
60 */
61
62 #define NEED_PRINTF_J /* Need defines for js_snprintf()? */
63 #include <defines.h>
64 #include <i18n.h>
65 #include <schily/utsname.h>
66 #include <ccstypes.h>
67 #include <signal.h>
68 #include <limits.h>
69
70 #ifndef SYS_NMLN
71 #define SYS_NMLN 257
72 #endif
73 #ifndef O_DSYNC
74 #define O_DSYNC 0
75 #endif
76
77 #define nodenamelength SYS_NMLN
78
79 static int onelock __PR((pid_t pid, char *uuname, char *lockfile));
80
81 int
lockit(lockfile,count,pid,uuname)82 lockit(lockfile, count, pid, uuname)
83 char *lockfile; /* The z.file path (e.g. SCCS/z.foo) */
84 int count; /* # of retries to get lock (e.g. 4) */
85 pid_t pid; /* The pid of the holding process. */
86 char *uuname; /* The hostname for this machine. */
87 {
88 int fd;
89 int dosleep = 0;
90 pid_t opid;
91 char ouuname[nodenamelength];
92 long ltime, omtime, onsecs;
93 #ifdef needed
94 char uniqfilename[PATH_MAX+48];
95 char tempfile[PATH_MAX];
96 int uniq_nmbr;
97
98 copy(lockfile, tempfile);
99 snprintf(uniqfilename, sizeof (uniqfilename),
100 "%s/%ju.%s%jd", dname(tempfile),
101 (UIntmax_t)pid, uuname, (Intmax_t)time((time_t *)0));
102 if (length(uniqfilename) >= PATH_MAX) {
103 uniq_nmbr = (int)pid + (int)time((time_t *)0);
104 copy(lockfile, tempfile);
105 sprintf(uniqfilename, "%s/%X", dname(tempfile),
106 uniq_nmbr&0xffffffff);
107 uniqfilename[PATH_MAX-1] = '\0';
108 }
109 /*
110 * Former SCCS implementations later renamed this file to the lock file
111 * using link() and unlink(), but we like to live without link().
112 * We here only check whether we are able to create a file in the
113 * desired directory.
114 */
115 fd = open(uniqfilename, O_WRONLY|O_CREAT|O_BINARY, 0666);
116 if (fd < 0) {
117 return (-1);
118 } else {
119 (void) close(fd);
120 (void) unlink(uniqfilename);
121 }
122 #endif
123 if (lockfile == NULL)
124 return (-1);
125
126 for (++count; --count; dosleep ? sleep(10) : 0) {
127 dosleep = 1;
128 if (onelock(pid, uuname, lockfile) == 0)
129 return (0);
130 if (errno == EACCES)
131 return (-1);
132 if (!exists(lockfile))
133 continue;
134 omtime = Statbuf.st_mtime;
135 onsecs = stat_mnsecs(&Statbuf);
136 if ((fd = open(lockfile, O_RDONLY|O_BINARY)) < 0)
137 continue;
138 opid = pid; /* In case file is empty */
139 ouuname[0] = '\0';
140 (void) read(fd, (char *)&opid, sizeof (opid));
141 (void) read(fd, ouuname, nodenamelength);
142 (void) close(fd);
143 /*
144 * If lockfile is from this host, check for pid.
145 * If lockfile is empty, ouuname and uuname are not equal.
146 */
147 if (equal(ouuname, uuname)) {
148 if (opid == pid) /* Recursive lock attempt */
149 return (-2);
150 if (opid == getppid()) /* Lock held by our parent */
151 return (-3);
152 if (kill((int) opid, 0) == -1 && errno == ESRCH) {
153 if ((exists(lockfile)) &&
154 (omtime == Statbuf.st_mtime) &&
155 (onsecs == stat_mnsecs(&Statbuf))) {
156 (void) unlink(lockfile);
157 dosleep = 0;
158 continue;
159 }
160 }
161 }
162 /*
163 * Lock file is empty, hold by other host or hold by an
164 * existing process on this host.
165 * Wait in case that file is younger than 60 seconds.
166 */
167 if ((ltime = time((time_t *)0) - Statbuf.st_mtime) < 60L) {
168 if (ltime >= 0 && ltime < 60) {
169 (void) sleep((unsigned) (60 - ltime));
170 } else {
171 (void) sleep(60);
172 }
173 }
174 /*
175 * Lock file is at least 60 seconds old.
176 * If lock file is still empty and did not change, we may
177 * remove it as we check for long delays between creation
178 * and writing the content in onelock().
179 * Since we only unlink() the file in case it is empty, we
180 * do not affect lock files from different NFS participants.
181 * If an operation takes more than 60 seconds, it is thus
182 * important to refresh the timestamps from the lock file
183 * after less than 60 seconds, to make NFS collaboration work.
184 */
185 if (exists(lockfile) &&
186 Statbuf.st_size == 0 &&
187 (omtime == Statbuf.st_mtime) &&
188 (onsecs == stat_mnsecs(&Statbuf))) {
189 (void) unlink(lockfile);
190 dosleep = 0;
191 }
192 }
193 errno = EEXIST; /* We failed due to exhausted retry count */
194 return (-1);
195 }
196
197 int
lockrefresh(lockfile)198 lockrefresh(lockfile)
199 char *lockfile; /* The z.file path (e.g. SCCS/z.foo) */
200 {
201 /*
202 * Do we need to check whether this is still our old lock?
203 */
204 return (utimens(lockfile, NULL));
205 }
206
207 int
unlockit(lockfile,pid,uuname)208 unlockit(lockfile, pid, uuname)
209 char *lockfile;
210 pid_t pid;
211 char *uuname;
212 {
213 /*
214 * Do nothing if just called from a clean up handler that does
215 * not yet have a lockfile name.
216 */
217 if (lockfile == NULL || lockfile[0] == '\0')
218 return (-1);
219
220 if (mylock(lockfile, pid, uuname))
221 return (unlink(lockfile));
222 else
223 return (-1);
224 }
225
226 /*
227 * Create a lockfile using O_CREAT|O_EXC and write our identification
228 * pid/uuname into the file.
229 */
230 static int
onelock(pid,uuname,lockfile)231 onelock(pid, uuname, lockfile)
232 pid_t pid;
233 char *uuname;
234 char *lockfile;
235 {
236 int fd;
237 char lock[sizeof (pid_t) + nodenamelength];
238 char *p;
239 int i;
240 time_t otime;
241
242 /*
243 * Copy pid and nodename together and write it in a single write() call
244 * to make sure we are not interrupted with a partially written lock
245 * file.
246 * A previous implementation wrote into the file under a temporary
247 * name and then linked it to lockname, but not all platforms support
248 * hard links.
249 */
250 for (i = 0, p = (char *)&pid; i < sizeof (pid_t); i++)
251 lock[i] = *p++;
252 strncpy(&lock[sizeof (pid_t)], uuname, nodenamelength);
253
254 /*
255 * Old SunOS versions from this file used O_DSYNC as open() flag.
256 * If we do this, the write() call below becomes expensive.
257 * Approx. 20ms on Solaris and approx. 50ms on Linux. Since with the
258 * project mode, we need two locks, this would be too much. The only
259 * advantage, O_DSYNC gives is on-disk consistence and since a reboot
260 * makes the process ID in the lock file void, it seems that the in
261 * core view of the file is sufficient for the integrity of the lock.
262 */
263 otime = time((time_t *)0);
264 if ((fd = open(lockfile,
265 O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0444)) >= 0) {
266 /*
267 * One write, so the file is either empty or written completely
268 */
269 if (write(fd, lock, sizeof (lock)) != sizeof (lock)) {
270 (void) close(fd);
271 (void) unlink(lockfile);
272 return (xmsg(lockfile, NOGETTEXT("lockit")));
273 }
274 close(fd);
275 /*
276 * In case of a long sleep, check whether the current lock file
277 * is ours or whether it has been replaced by a faster process.
278 * If the current lock file is not from us, allow to try again
279 * to create the lock.
280 */
281 if ((time((time_t *)0) - otime) > 2)
282 if (!mylock(lockfile, pid, uuname))
283 return (-1);
284 return (0);
285 }
286 if ((errno == ENFILE) || (errno == EACCES) || (errno == EEXIST)) {
287 /*
288 * This is a retryable error.
289 */
290 return (-1);
291 } else {
292 /*
293 * Give up for other problems.
294 */
295 return (xmsg(lockfile, NOGETTEXT("lockit")));
296 }
297 }
298
299 /*
300 * Check whether a lock in lockfile exists and whether pid/uuname are equal
301 * to what's in the file.
302 *
303 * Returns:
304 *
305 * -2 Lock hold by a currently active process on this machine.
306 *
307 * -1 No lock file is present (ENOENT).
308 *
309 * 0 Cannot open lock file, empty file or pid/uuname do not match.
310 * This includes a currently active lock on a different machine.
311 *
312 * 1 Lock file is present and pid/uuname match with the file.
313 */
314 int
ismylock(lockfile,pid,uuname)315 ismylock(lockfile, pid, uuname)
316 char *lockfile;
317 pid_t pid;
318 char *uuname;
319 {
320 int fd;
321 int n;
322 pid_t opid;
323 char ouuname[nodenamelength];
324
325 if ((fd = open(lockfile, O_RDONLY|O_BINARY)) < 0) {
326 if (errno == ENOENT) /* There was no lock file */
327 return (-1);
328 return (0);
329 }
330 n = read(fd, (char *)&opid, sizeof (opid));
331 (void) read(fd, ouuname, nodenamelength);
332 (void) close(fd);
333 if (n == sizeof (opid) && opid == pid && (equal(ouuname, uuname))) {
334 return (1);
335 } else {
336 if (n == sizeof (opid) && (equal(ouuname, uuname))) {
337 /*
338 * The case of a legally long lasting lock is finally
339 * handled in lockit().
340 */
341 if (kill(opid, 0) == 0) {
342 return (-2); /* Lock is still active */
343 }
344 }
345 return (0);
346 }
347 }
348
349 /*
350 * Check whether a lock in lockfile exists and can be opened and whether
351 * pid/uuname are equal to what's in the file.
352 *
353 * Returns:
354 *
355 * FALSE Cannot open lock file, or pid/uuname do not match.
356 *
357 * TRUE Lock file is present and pid/uuname match with the file.
358 */
359 int
mylock(lockfile,pid,uuname)360 mylock(lockfile, pid, uuname)
361 char *lockfile;
362 pid_t pid;
363 char *uuname;
364 {
365 return (ismylock(lockfile, pid, uuname) > 0);
366 }
367
368 /*
369 * Generic lock file error routine.
370 */
371 void
lockfatal(lockfile,pid,uuname)372 lockfatal(lockfile, pid, uuname)
373 char *lockfile;
374 pid_t pid;
375 char *uuname;
376 {
377 if (ismylock(lockfile, pid, uuname) > 0)
378 fatal(gettext("recursive dead lock attempt (cm23)"));
379
380 if (ismylock(lockfile, getppid(), uuname) > 0)
381 fatal(gettext("parent dead lock attempt (cm24)"));
382
383 efatal(gettext("cannot create lock file (cm4)"));
384 }
385