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 /* 22 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * @(#)lock.c 1.5 06/12/12 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <string.h> 33 #include <fcntl.h> 34 #include <sys/types.h> 35 #include <sys/param.h> 36 #include <sys/stat.h> 37 #include <sys/errno.h> 38 #include <errno.h> /* errno */ 39 40 #if defined(_LP64) 41 /* 42 * The symbols _sys_errlist and _sys_nerr are not visible in the 43 * LP64 libc. Use strerror(3C) instead. 44 */ 45 #else /* #_LP64 */ 46 extern char * sys_errlist[]; 47 extern int sys_nerr; 48 #endif /* #_LP64 */ 49 50 static void file_lock_error(); 51 52 /* 53 * This code stolen from the NSE library and changed to not depend 54 * upon any NSE routines or header files. 55 * 56 * Simple file locking. 57 * Create a symlink to a file. The "test and set" will be 58 * atomic as creating the symlink provides both functions. 59 * 60 * The timeout value specifies how long to wait for stale locks 61 * to disappear. If the lock is more than 'timeout' seconds old 62 * then it is ok to blow it away. This part has a small window 63 * of vunerability as the operations of testing the time, 64 * removing the lock and creating a new one are not atomic. 65 * It would be possible for two processes to both decide to blow 66 * away the lock and then have process A remove the lock and establish 67 * its own, and then then have process B remove the lock which accidentily 68 * removes A's lock rather than the stale one. 69 * 70 * A further complication is with the NFS. If the file in question is 71 * being served by an NFS server, then its time is set by that server. 72 * We can not use the time on the client machine to check for a stale 73 * lock. Therefore, a temp file on the server is created to get 74 * the servers current time. 75 * 76 * Returns an error message. NULL return means the lock was obtained. 77 * 78 */ 79 char * 80 file_lock(char * name, char * lockname, int timeout) 81 { 82 int r; 83 int fd; 84 struct stat statb; 85 struct stat fs_statb; 86 char tmpname[MAXPATHLEN]; 87 static char msg[MAXPATHLEN]; 88 89 if (timeout <= 0) { 90 timeout = 15; 91 } 92 for (;;) { 93 r = symlink(name, lockname); 94 if (r == 0) { 95 return (NULL); 96 } 97 if (errno != EEXIST) { 98 file_lock_error(msg, name, 99 (const char *)"symlink(%s, %s)", name, lockname); 100 return (msg); 101 } 102 for (;;) { 103 (void) sleep(1); 104 r = lstat(lockname, &statb); 105 if (r == -1) { 106 /* 107 * The lock must have just gone away - try 108 * again. 109 */ 110 break; 111 } 112 113 /* 114 * With the NFS the time given a file is the time on 115 * the file server. This time may vary from the 116 * client's time. Therefore, we create a tmpfile in 117 * the same directory to establish the time on the 118 * server and use this time to see if the lock has 119 * expired. 120 */ 121 (void) sprintf(tmpname, "%s.XXXXXX", lockname); 122 (void) mktemp(tmpname); 123 fd = creat(tmpname, 0666); 124 if (fd != -1) { 125 (void) close(fd); 126 } else { 127 file_lock_error(msg, name, 128 (const char *)"creat(%s)", tmpname); 129 return (msg); 130 } 131 if (stat(tmpname, &fs_statb) == -1) { 132 file_lock_error(msg, name, 133 (const char *)"stat(%s)", tmpname); 134 return (msg); 135 } 136 (void) unlink(tmpname); 137 if (statb.st_mtime + timeout < fs_statb.st_mtime) { 138 /* 139 * The lock has expired - blow it away. 140 */ 141 (void) unlink(lockname); 142 break; 143 } 144 } 145 } 146 /* NOTREACHED */ 147 } 148 149 /* 150 * Format a message telling why the lock could not be created. 151 */ 152 /* VARARGS4 */ 153 static void 154 file_lock_error(char * msg, char * file, const char * str, char * arg1, 155 char * arg2) 156 { 157 int len; 158 159 (void) sprintf(msg, "Could not lock file `%s'; ", file); 160 len = strlen(msg); 161 (void) sprintf(&msg[len], str, arg1, arg2); 162 (void) strcat(msg, " failed - "); 163 #if defined(_LP64) 164 /* Needs to be changed to use strerror(3C) instead. */ 165 len = strlen(msg); 166 (void) sprintf(&msg[len], "errno %d", errno); 167 #else /* #_LP64 */ 168 if (errno < sys_nerr) { 169 (void) strcat(msg, sys_errlist[errno]); 170 } else { 171 len = strlen(msg); 172 (void) sprintf(&msg[len], "errno %d", errno); 173 } 174 #endif /* #_LP64 */ 175 }