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