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 }