xref: /freebsd-src/contrib/tcsh/dotlock.c (revision 9122aeeaa60ee2a1381ea935d749194b32940e7a)
1*cc698b49SBrooks Davis /*	NetBSD: dotlock.c,v 1.11 2009/10/21 01:07:46 snj Exp	*/
219d2e3deSDmitry Chagin 
319d2e3deSDmitry Chagin /*
419d2e3deSDmitry Chagin  * Copyright (c) 1996 Christos Zoulas.  All rights reserved.
519d2e3deSDmitry Chagin  *
619d2e3deSDmitry Chagin  * Redistribution and use in source and binary forms, with or without
719d2e3deSDmitry Chagin  * modification, are permitted provided that the following conditions
819d2e3deSDmitry Chagin  * are met:
919d2e3deSDmitry Chagin  * 1. Redistributions of source code must retain the above copyright
1019d2e3deSDmitry Chagin  *    notice, this list of conditions and the following disclaimer.
1119d2e3deSDmitry Chagin  * 2. Redistributions in binary form must reproduce the above copyright
1219d2e3deSDmitry Chagin  *    notice, this list of conditions and the following disclaimer in the
1319d2e3deSDmitry Chagin  *    documentation and/or other materials provided with the distribution.
1419d2e3deSDmitry Chagin  *
1519d2e3deSDmitry Chagin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1619d2e3deSDmitry Chagin  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1719d2e3deSDmitry Chagin  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1819d2e3deSDmitry Chagin  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1919d2e3deSDmitry Chagin  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2019d2e3deSDmitry Chagin  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2119d2e3deSDmitry Chagin  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2219d2e3deSDmitry Chagin  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2319d2e3deSDmitry Chagin  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2419d2e3deSDmitry Chagin  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2519d2e3deSDmitry Chagin  */
2619d2e3deSDmitry Chagin #include "sh.h"
2719d2e3deSDmitry Chagin 
2819d2e3deSDmitry Chagin #include <stdio.h>
2919d2e3deSDmitry Chagin #ifndef O_SYNC
3019d2e3deSDmitry Chagin #define O_SYNC	0
3119d2e3deSDmitry Chagin #endif
3219d2e3deSDmitry Chagin 
3319d2e3deSDmitry Chagin #include "dotlock.h"
3419d2e3deSDmitry Chagin 
3519d2e3deSDmitry Chagin static int create_exclusive(const char *);
3619d2e3deSDmitry Chagin /*
3719d2e3deSDmitry Chagin  * Create a unique file. O_EXCL does not really work over NFS so we follow
3819d2e3deSDmitry Chagin  * the following trick: [Inspired by  S.R. van den Berg]
3919d2e3deSDmitry Chagin  *
4019d2e3deSDmitry Chagin  * - make a mostly unique filename and try to create it.
4119d2e3deSDmitry Chagin  * - link the unique filename to our target
4219d2e3deSDmitry Chagin  * - get the link count of the target
4319d2e3deSDmitry Chagin  * - unlink the mostly unique filename
4419d2e3deSDmitry Chagin  * - if the link count was 2, then we are ok; else we've failed.
4519d2e3deSDmitry Chagin  */
4619d2e3deSDmitry Chagin static int
create_exclusive(const char * fname)4719d2e3deSDmitry Chagin create_exclusive(const char *fname)
4819d2e3deSDmitry Chagin {
4919d2e3deSDmitry Chagin 	char path[MAXPATHLEN], hostname[MAXHOSTNAMELEN + 1];
5019d2e3deSDmitry Chagin 	const char *ptr;
5119d2e3deSDmitry Chagin 	struct timeval tv;
5219d2e3deSDmitry Chagin 	pid_t pid;
5319d2e3deSDmitry Chagin 	size_t ntries, cookie;
5419d2e3deSDmitry Chagin 	int fd, serrno;
5519d2e3deSDmitry Chagin 	struct stat st;
5619d2e3deSDmitry Chagin 
5719d2e3deSDmitry Chagin 	(void)gettimeofday(&tv, NULL);
5819d2e3deSDmitry Chagin 	(void)gethostname(hostname, sizeof(hostname));
5919d2e3deSDmitry Chagin 	hostname[sizeof(hostname) - 1] = '\0';
6019d2e3deSDmitry Chagin 	pid = getpid();
6119d2e3deSDmitry Chagin 
6219d2e3deSDmitry Chagin 	cookie = pid ^ tv.tv_usec;
6319d2e3deSDmitry Chagin 
6419d2e3deSDmitry Chagin 	/*
6519d2e3deSDmitry Chagin 	 * We generate a semi-unique filename, from hostname.(pid ^ usec)
6619d2e3deSDmitry Chagin 	 */
6719d2e3deSDmitry Chagin 	if ((ptr = strrchr(fname, '/')) == NULL)
6819d2e3deSDmitry Chagin 		ptr = fname;
6919d2e3deSDmitry Chagin 	else
7019d2e3deSDmitry Chagin 		ptr++;
7119d2e3deSDmitry Chagin 
7219d2e3deSDmitry Chagin 	(void)snprintf(path, sizeof(path), "%.*s.%s.%lx",
7319d2e3deSDmitry Chagin 	    (int)(ptr - fname), fname, hostname, (u_long)cookie);
7419d2e3deSDmitry Chagin 
7519d2e3deSDmitry Chagin 	/*
7619d2e3deSDmitry Chagin 	 * We try to create the unique filename.
7719d2e3deSDmitry Chagin 	 */
7819d2e3deSDmitry Chagin 	for (ntries = 0; ntries < 5; ntries++) {
7919d2e3deSDmitry Chagin 		fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_SYNC, 0);
8019d2e3deSDmitry Chagin 		if (fd != -1) {
8119d2e3deSDmitry Chagin 			(void)close(fd);
8219d2e3deSDmitry Chagin 			break;
8319d2e3deSDmitry Chagin 		}
8419d2e3deSDmitry Chagin 		else if (errno == EEXIST)
8519d2e3deSDmitry Chagin 			continue;
8619d2e3deSDmitry Chagin 		else
8719d2e3deSDmitry Chagin 			return -1;
8819d2e3deSDmitry Chagin 	}
8919d2e3deSDmitry Chagin 
9019d2e3deSDmitry Chagin 	/*
9119d2e3deSDmitry Chagin 	 * We link the path to the name
9219d2e3deSDmitry Chagin 	 */
9319d2e3deSDmitry Chagin 	if (link(path, fname) == -1)
9419d2e3deSDmitry Chagin 		goto bad;
9519d2e3deSDmitry Chagin 
9619d2e3deSDmitry Chagin 	/*
9719d2e3deSDmitry Chagin 	 * Note that we stat our own exclusively created name, not the
9819d2e3deSDmitry Chagin 	 * destination, since the destination can be affected by others.
9919d2e3deSDmitry Chagin 	 */
10019d2e3deSDmitry Chagin 	if (stat(path, &st) == -1)
10119d2e3deSDmitry Chagin 		goto bad;
10219d2e3deSDmitry Chagin 
10319d2e3deSDmitry Chagin 	(void)unlink(path);
10419d2e3deSDmitry Chagin 
10519d2e3deSDmitry Chagin 	/*
10619d2e3deSDmitry Chagin 	 * If the number of links was two (one for the unique file and one
10719d2e3deSDmitry Chagin 	 * for the lock), we've won the race
10819d2e3deSDmitry Chagin 	 */
10919d2e3deSDmitry Chagin 	if (st.st_nlink != 2) {
11019d2e3deSDmitry Chagin 		errno = EEXIST;
11119d2e3deSDmitry Chagin 		return -1;
11219d2e3deSDmitry Chagin 	}
11319d2e3deSDmitry Chagin 	return 0;
11419d2e3deSDmitry Chagin 
11519d2e3deSDmitry Chagin bad:
11619d2e3deSDmitry Chagin 	serrno = errno;
11719d2e3deSDmitry Chagin 	(void)unlink(path);
11819d2e3deSDmitry Chagin 	errno = serrno;
11919d2e3deSDmitry Chagin 	return -1;
12019d2e3deSDmitry Chagin }
12119d2e3deSDmitry Chagin 
12219d2e3deSDmitry Chagin /*
12319d2e3deSDmitry Chagin  * fname -- Pathname to lock
12419d2e3deSDmitry Chagin  * pollinterval -- Interval (miliseconds) to check for lock, -1 return
12519d2e3deSDmitry Chagin  */
12619d2e3deSDmitry Chagin int
dot_lock(const char * fname,int pollinterval)12719d2e3deSDmitry Chagin dot_lock(const char *fname, int pollinterval)
12819d2e3deSDmitry Chagin {
12919d2e3deSDmitry Chagin 	char path[MAXPATHLEN];
13019d2e3deSDmitry Chagin 	sigset_t nset, oset;
13119d2e3deSDmitry Chagin 	int retval;
13219d2e3deSDmitry Chagin 
13319d2e3deSDmitry Chagin 	(void)sigemptyset(&nset);
13419d2e3deSDmitry Chagin 	(void)sigaddset(&nset, SIGHUP);
13519d2e3deSDmitry Chagin 	(void)sigaddset(&nset, SIGINT);
13619d2e3deSDmitry Chagin 	(void)sigaddset(&nset, SIGQUIT);
13719d2e3deSDmitry Chagin 	(void)sigaddset(&nset, SIGTERM);
13819d2e3deSDmitry Chagin 	(void)sigaddset(&nset, SIGTTIN);
13919d2e3deSDmitry Chagin 	(void)sigaddset(&nset, SIGTTOU);
14019d2e3deSDmitry Chagin 	(void)sigaddset(&nset, SIGTSTP);
14119d2e3deSDmitry Chagin 	(void)sigaddset(&nset, SIGCHLD);
14219d2e3deSDmitry Chagin 
14319d2e3deSDmitry Chagin 	(void)snprintf(path, sizeof(path), "%s.lock", fname);
14419d2e3deSDmitry Chagin 
14519d2e3deSDmitry Chagin 	retval = -1;
14619d2e3deSDmitry Chagin 	for (;;) {
14719d2e3deSDmitry Chagin 		handle_pending_signals();
14819d2e3deSDmitry Chagin 		(void)sigprocmask(SIG_BLOCK, &nset, &oset);
14919d2e3deSDmitry Chagin 		if (create_exclusive(path) != -1) {
15019d2e3deSDmitry Chagin 			(void)sigprocmask(SIG_SETMASK, &oset, NULL);
15119d2e3deSDmitry Chagin 			retval = 0;
15219d2e3deSDmitry Chagin 			break;
15319d2e3deSDmitry Chagin 		}
15419d2e3deSDmitry Chagin 		else
15519d2e3deSDmitry Chagin 			(void)sigprocmask(SIG_SETMASK, &oset, NULL);
15619d2e3deSDmitry Chagin 
15719d2e3deSDmitry Chagin 		if (errno != EEXIST)
15819d2e3deSDmitry Chagin 			break;
15919d2e3deSDmitry Chagin 
16019d2e3deSDmitry Chagin 		if (pollinterval) {
16119d2e3deSDmitry Chagin 			if (pollinterval == -1) {
16219d2e3deSDmitry Chagin 				errno = EEXIST;
16319d2e3deSDmitry Chagin 				break;
16419d2e3deSDmitry Chagin 			}
16519d2e3deSDmitry Chagin 			(void)usleep((unsigned int)pollinterval * 1000);
16619d2e3deSDmitry Chagin 		}
16719d2e3deSDmitry Chagin 	}
16819d2e3deSDmitry Chagin 	handle_pending_signals();
16919d2e3deSDmitry Chagin 	return retval;
17019d2e3deSDmitry Chagin }
17119d2e3deSDmitry Chagin 
17219d2e3deSDmitry Chagin void
dot_unlock(const char * fname)17319d2e3deSDmitry Chagin dot_unlock(const char *fname)
17419d2e3deSDmitry Chagin {
17519d2e3deSDmitry Chagin 	char path[MAXPATHLEN];
17619d2e3deSDmitry Chagin 
17719d2e3deSDmitry Chagin 	(void)snprintf(path, sizeof(path), "%s.lock", fname);
17819d2e3deSDmitry Chagin 	(void)unlink(path);
17919d2e3deSDmitry Chagin }
180