/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include /* errno */ #include static void file_lock_error(char *msg, char *file, const char *str, char *arg1, char * arg2); #define BLOCK_INTERUPTS sigfillset(&newset) ; \ sigprocmask(SIG_SETMASK, &newset, &oldset) #define UNBLOCK_INTERUPTS \ sigprocmask(SIG_SETMASK, &oldset, &newset) /* * This code stolen from the NSE library and changed to not depend * upon any NSE routines or header files. * * Simple file locking. * Create a symlink to a file. The "test and set" will be * atomic as creating the symlink provides both functions. * * The timeout value specifies how long to wait for stale locks * to disappear. If the lock is more than 'timeout' seconds old * then it is ok to blow it away. This part has a small window * of vunerability as the operations of testing the time, * removing the lock and creating a new one are not atomic. * It would be possible for two processes to both decide to blow * away the lock and then have process A remove the lock and establish * its own, and then then have process B remove the lock which accidentily * removes A's lock rather than the stale one. * * A further complication is with the NFS. If the file in question is * being served by an NFS server, then its time is set by that server. * We can not use the time on the client machine to check for a stale * lock. Therefore, a temp file on the server is created to get * the servers current time. * * Returns an error message. NULL return means the lock was obtained. * * 12/6/91 Added the parameter "file_locked". Before this parameter * was added, the calling procedure would have to wait for file_lock() * to return before it sets the flag. If the user interrupted "make" * between the time the lock was acquired and the time file_lock() * returns, make wouldn't know that the file has been locked, and therefore * it wouldn' remove the lock. Setting the flag right after locking the file * makes this window much smaller. */ int file_lock(char *name, char *lockname, int *file_locked, int timeout) { int counter = 0; static char msg[MAXPATHLEN+1]; int printed_warning = 0; int r; struct stat statb; sigset_t newset; sigset_t oldset; *file_locked = 0; if (timeout <= 0) { timeout = 120; } for (;;) { BLOCK_INTERUPTS; r = symlink(name, lockname); if (r == 0) { *file_locked = 1; UNBLOCK_INTERUPTS; return 0; /* success */ } UNBLOCK_INTERUPTS; if (errno != EEXIST) { file_lock_error(msg, name, "symlink(%s, %s)", name, lockname); fprintf(stderr, "%s", msg); return errno; } counter = 0; for (;;) { sleep(1); r = lstat(lockname, &statb); if (r == -1) { /* * The lock must have just gone away - try * again. */ break; } if ((counter > 5) && (!printed_warning)) { /* Print waiting message after 5 secs */ (void) getcwd(msg, MAXPATHLEN); fprintf(stderr, gettext( "file_lock: file %s is already locked.\n"), name); fprintf(stderr, gettext( "file_lock: will periodically check the " "lockfile %s for two minutes.\n"), lockname); fprintf(stderr, gettext( "Current working directory %s\n"), msg); printed_warning = 1; } if (++counter > timeout ) { /* * Waited enough - return an error.. */ return EEXIST; } } } /* NOTREACHED */ } /* * Format a message telling why the lock could not be created. */ static void file_lock_error(char *msg, char *file, const char *str, char *arg1, char *arg2) { int len, err; char *ptr; sprintf(msg, gettext("Could not lock file `%s'; "), file); len = strlen(msg); sprintf(&msg[len], str, arg1, arg2); strcat(msg, gettext(" failed - ")); err = errno; errno = 0; ptr = strerror(err); if (errno != EINVAL) { strcat(msg, ptr); } else { len = strlen(msg); sprintf(&msg[len], "errno %d", err); } }