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