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