Annotation of coherent/g/usr/lib/uucp/tay104/unix/lock.c, revision 1.1.1.1

1.1       root        1: /* lock.c
                      2:    Lock and unlock a file name.
                      3: 
                      4:    Copyright (C) 1991, 1992 Ian Lance Taylor
                      5: 
                      6:    This file is part of the Taylor UUCP package.
                      7: 
                      8:    This program is free software; you can redistribute it and/or
                      9:    modify it under the terms of the GNU General Public License as
                     10:    published by the Free Software Foundation; either version 2 of the
                     11:    License, or (at your option) any later version.
                     12: 
                     13:    This program is distributed in the hope that it will be useful, but
                     14:    WITHOUT ANY WARRANTY; without even the implied warranty of
                     15:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                     16:    General Public License for more details.
                     17: 
                     18:    You should have received a copy of the GNU General Public License
                     19:    along with this program; if not, write to the Free Software
                     20:    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
                     21: 
                     22:    The author of the program may be contacted at [email protected] or
                     23:    c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
                     24:    */
                     25: 
                     26: #include "uucp.h"
                     27: 
                     28: #if USE_RCS_ID
                     29: const char lock_rcsid[] = "$Id: lock.c,v 1.1 93/07/30 08:02:37 bin Exp Locker: bin $";
                     30: #endif
                     31: 
                     32: #include "uudefs.h"
                     33: #include "sysdep.h"
                     34: #include "system.h"
                     35: 
                     36: #include <errno.h>
                     37: 
                     38: #if HAVE_FCNTL_H
                     39: #include <fcntl.h>
                     40: #else
                     41: #if HAVE_SYS_FILE_H
                     42: #include <sys/file.h>
                     43: #endif
                     44: #endif
                     45: 
                     46: #ifndef O_RDONLY
                     47: #define O_RDONLY 0
                     48: #define O_WRONLY 1
                     49: #define O_RDWR 2
                     50: #endif
                     51: 
                     52: #ifndef O_NOCTTY
                     53: #define O_NOCTTY 0
                     54: #endif
                     55: 
                     56: #ifndef SEEK_SET
                     57: #define SEEK_SET 0
                     58: #endif
                     59: 
                     60: /* Lock something.  If the fspooldir argument is TRUE, the argument is
                     61:    a file name relative to the spool directory; otherwise the argument
                     62:    is a simple file name which should be created in the system lock
                     63:    directory (under HDB this is /etc/locks).  */
                     64: 
                     65: boolean
                     66: fsdo_lock (zlock, fspooldir, pferr)
                     67:      const char *zlock;
                     68:      boolean fspooldir;
                     69:      boolean *pferr;
                     70: {
                     71:   char *zfree;
                     72:   const char *zpath, *zslash;
                     73:   size_t cslash;
                     74:   pid_t ime;
                     75:   char *ztempfile;
                     76:   char abtempfile[sizeof "TMP1234567890"];
                     77:   int o;
                     78: #if HAVE_V2_LOCKFILES
                     79:   int i;
                     80: #else
                     81:   char ab[12];
                     82: #endif
                     83:   int cwrote;
                     84:   const char *zerr;
                     85:   boolean fret;
                     86: 
                     87:   if (pferr != NULL)
                     88:     *pferr = TRUE;
                     89: 
                     90:   if (fspooldir)
                     91:     {
                     92:       zfree = NULL;
                     93:       zpath = zlock;
                     94:     }
                     95:   else
                     96:     {
                     97:       zfree = zsysdep_in_dir (zSlockdir, zlock);
                     98:       zpath = zfree;
                     99:     }
                    100: 
                    101:   ime = getpid ();
                    102: 
                    103:   /* We do the actual lock by creating a file and then linking it to
                    104:      the final file name we want.  This avoids race conditions due to
                    105:      one process checking the file before we have finished writing it,
                    106:      and also works even if we are somehow running as root.
                    107: 
                    108:      First, create the file in the right directory (we must create the
                    109:      file in the same directory since otherwise we might attempt a
                    110:      cross-device link).  */
                    111:   zslash = strrchr (zpath, '/');
                    112:   if (zslash == NULL)
                    113:     cslash = 0;
                    114:   else
                    115:     cslash = zslash - zpath + 1;
                    116: 
                    117:   sprintf (abtempfile, "TMP%010lx", (unsigned long) ime);
                    118:   ztempfile = zbufalc (cslash + sizeof abtempfile);
                    119:   memcpy (ztempfile, zpath, cslash);
                    120:   memcpy (ztempfile + cslash, abtempfile, sizeof abtempfile);
                    121: 
                    122:   o = creat (ztempfile, IPUBLIC_FILE_MODE);
                    123:   if (o < 0)
                    124:     {
                    125:       if (errno == ENOENT)
                    126:        {
                    127:          if (! fsysdep_make_dirs (ztempfile, FALSE))
                    128:            {
                    129:              ubuffree (zfree);
                    130:              ubuffree (ztempfile);
                    131:              return FALSE;
                    132:            }
                    133:          o = creat (ztempfile, IPUBLIC_FILE_MODE);
                    134:        }
                    135:       if (o < 0)
                    136:        {
                    137:          ulog (LOG_ERROR, "creat (%s): %s", ztempfile, strerror (errno));
                    138:          ubuffree (zfree);
                    139:          ubuffree (ztempfile);
                    140:          return FALSE;
                    141:        }
                    142:     }
                    143: 
                    144: #if HAVE_V2_LOCKFILES
                    145:   i = ime;
                    146:   cwrote = write (o, &i, sizeof i);
                    147: #else
                    148:   sprintf (ab, "%10d\n", (int) ime);
                    149:   cwrote = write (o, ab, strlen (ab));
                    150: #endif
                    151: 
                    152:   zerr = NULL;
                    153:   if (cwrote < 0)
                    154:     zerr = "write";
                    155:   if (close (o) < 0)
                    156:     zerr = "close";
                    157:   if (zerr != NULL)
                    158:     {
                    159:       ulog (LOG_ERROR, "%s (%s): %s", zerr, ztempfile, strerror (errno));
                    160:       (void) remove (ztempfile);
                    161:       ubuffree (zfree);
                    162:       ubuffree (ztempfile);
                    163:       return FALSE;
                    164:     }
                    165: 
                    166:   /* Now try to link the file we just created to the lock file that we
                    167:      want.  If it fails, try reading the existing file to make sure
                    168:      the process that created it still exists.  We do this in a loop
                    169:      to make it easy to retry if the old locking process no longer
                    170:      exists.  */
                    171:   fret = TRUE;
                    172:   if (pferr != NULL)
                    173:     *pferr = FALSE;
                    174:   o = -1;
                    175:   zerr = NULL;
                    176: 
                    177:   while (link (ztempfile, zpath) != 0)
                    178:     {
                    179:       int cgot;
                    180:       int ipid;
                    181:       boolean freadonly;
                    182: 
                    183:       fret = FALSE;
                    184: 
                    185:       if (errno != EEXIST)
                    186:        {
                    187:          ulog (LOG_ERROR, "link (%s, %s): %s", ztempfile, zpath,
                    188:                strerror (errno));
                    189:          if (pferr != NULL)
                    190:            *pferr = TRUE;
                    191:          break;
                    192:        }
                    193: 
                    194:       freadonly = FALSE;
                    195:       o = open ((char *) zpath, O_RDWR | O_NOCTTY, 0);
                    196:       if (o < 0)
                    197:        {
                    198:          if (errno == EACCES)
                    199:            {
                    200:              freadonly = TRUE;
                    201:              o = open ((char *) zpath, O_RDONLY, 0);
                    202:            }
                    203:          if (o < 0)
                    204:            {
                    205:              if (errno == ENOENT)
                    206:                {
                    207:                  /* The file was presumably removed between the link
                    208:                     and the open.  Try the link again.  */
                    209:                  fret = TRUE;
                    210:                  continue;
                    211:                }
                    212:              zerr = "open";
                    213:              break;
                    214:            }
                    215:        }
                    216: 
                    217:       /* The race starts here.  See below for a discussion.  */
                    218: 
                    219: #if HAVE_V2_LOCKFILES
                    220:       cgot = read (o, &i, sizeof i);
                    221: #else
                    222:       cgot = read (o, ab, sizeof ab - 1);
                    223: #endif
                    224: 
                    225:       if (cgot < 0)
                    226:        {
                    227:          zerr = "read";
                    228:          break;
                    229:        }
                    230: 
                    231: #if HAVE_V2_LOCKFILES
                    232:       ipid = i;
                    233: #else
                    234:       ab[cgot] = '\0';
                    235:       ipid = strtol (ab, (char **) NULL, 10);
                    236: #endif
                    237: 
                    238:       /* On NFS, the link might have actually succeeded even though we
                    239:         got a failure return.  This can happen if the original
                    240:         acknowledgement was lost or delayed and the operation was
                    241:         retried.  In this case the pid will be our own.  This
                    242:         introduces a rather improbable race condition: if a stale
                    243:         lock was left with our process ID in it, and another process
                    244:         just did the kill, below, but has not yet changed the lock
                    245:         file to hold its own process ID, we could start up and make
                    246:         it all the way to here and think we have the lock.  I'm not
                    247:         going to worry about this possibility.  */
                    248:       if (ipid == ime)
                    249:        {
                    250:          fret = TRUE;
                    251:          break;
                    252:        }
                    253: 
                    254:       /* If the process still exists, we will get EPERM rather than
                    255:         ESRCH.  We then return FALSE to indicate that we cannot make
                    256:         the lock.  */
                    257:       if (kill (ipid, 0) == 0 || errno == EPERM)
                    258:        break;
                    259: 
                    260:       ulog (LOG_ERROR, "Found stale lock %s held by process %d",
                    261:            zpath, ipid);
                    262: 
                    263:       /* This is a stale lock, created by a process that no longer
                    264:         exists.
                    265: 
                    266:         Now we could remove the file (and, if the file mode disallows
                    267:         writing, that's what we have to do), but we try to avoid
                    268:         doing so since it causes a race condition.  If we remove the
                    269:         file, and are interrupted any time after we do the read until
                    270:         we do the remove, another process could get in, open the
                    271:         file, find that it was a stale lock, remove the file and
                    272:         create a new one.  When we regained control we would remove
                    273:         the file the other process just created.
                    274: 
                    275:         These files are being generated partially for the benefit of
                    276:         cu, and it would be nice to avoid the race however cu avoids
                    277:         it, so that the programs remain compatible.  Unfortunately,
                    278:         nobody seems to know how cu avoids the race, or even if it
                    279:         tries to avoid it at all.
                    280: 
                    281:         There are a few ways to avoid the race.  We could use kernel
                    282:         locking primitives, but they may not be available.  We could
                    283:         link to a special file name, but if that file were left lying
                    284:         around then no stale lock could ever be broken (Henry Spencer
                    285:         would think this was a good thing).
                    286: 
                    287:         Instead I've implemented the following procedure: seek to the
                    288:         start of the file, write our pid into it, sleep for five
                    289:         seconds, and then make sure our pid is still there.  Anybody
                    290:         who checks the file while we're asleep will find our pid
                    291:         there and fail the lock.  The only race will come from
                    292:         another process which has done the read by the time we do our
                    293:         write.  That process will then have five seconds to do its
                    294:         own write.  When we wake up, we'll notice that our pid is no
                    295:         longer in the file, and retry the lock from the beginning.
                    296: 
                    297:         This relies on the atomicity of write(2).  If it possible for
                    298:         the writes of two processes to be interleaved, the two
                    299:         processes could livelock.  POSIX unfortunately leaves this
                    300:         case explicitly undefined; however, given that the write is
                    301:         of less than a disk block, it's difficult to imagine an
                    302:         interleave occurring.
                    303: 
                    304:         Note that this is still a race.  If it takes the second
                    305:         process more than five seconds to do the kill, the lseek, and
                    306:         the write, both processes will think they have the lock.
                    307:         Perhaps the length of time to sleep should be configurable.
                    308:         Even better, perhaps I should add a configuration option to
                    309:         use a permanent lock file, which eliminates any race and
                    310:         forces the installer to be aware of the existence of the
                    311:         permanent lock file.
                    312: 
                    313:         We stat the file after the sleep, to make sure some other
                    314:         program hasn't deleted it for us.  */
                    315:       if (freadonly)
                    316:        {
                    317:          (void) close (o);
                    318:          o = -1;
                    319:          (void) remove (zpath);
                    320:          continue;
                    321:        }
                    322: 
                    323:       if (lseek (o, (off_t) 0, SEEK_SET) != 0)
                    324:        {
                    325:          zerr = "lseek";
                    326:          break;
                    327:        }
                    328: 
                    329: #if HAVE_V2_LOCKFILES
                    330:       i = ime;
                    331:       cwrote = write (o, &i, sizeof i);
                    332: #else
                    333:       sprintf (ab, "%10d\n", (int) ime);
                    334:       cwrote = write (o, ab, strlen (ab));
                    335: #endif
                    336: 
                    337:       if (cwrote < 0)
                    338:        {
                    339:          zerr = "write";
                    340:          break;
                    341:        }
                    342: 
                    343:       (void) sleep (5);
                    344: 
                    345:       if (lseek (o, (off_t) 0, SEEK_SET) != 0)
                    346:        {
                    347:          zerr = "lseek";
                    348:          break;
                    349:        }
                    350: 
                    351: #if HAVE_V2_LOCKFILES
                    352:       cgot = read (o, &i, sizeof i);
                    353: #else
                    354:       cgot = read (o, ab, sizeof ab - 1);
                    355: #endif
                    356: 
                    357:       if (cgot < 0)
                    358:        {
                    359:          zerr = "read";
                    360:          break;
                    361:        }
                    362: 
                    363: #if HAVE_V2_LOCKFILES
                    364:       ipid = i;
                    365: #else
                    366:       ab[cgot] = '\0';
                    367:       ipid = strtol (ab, (char **) NULL, 10);
                    368: #endif
                    369: 
                    370:       if (ipid == ime)
                    371:        {
                    372:          struct stat sfile, sdescriptor;
                    373: 
                    374:          /* It looks like we have the lock.  Do the final stat
                    375:             check.  */
                    376:          if (stat ((char *) zpath, &sfile) < 0)
                    377:            {
                    378:              if (errno != ENOENT)
                    379:                {
                    380:                  zerr = "stat";
                    381:                  break;
                    382:                }
                    383:              /* Loop around and try again.  */
                    384:            }
                    385:          else
                    386:            {
                    387:              if (fstat (o, &sdescriptor) < 0)
                    388:                {
                    389:                  zerr = "fstat";
                    390:                  break;
                    391:                }
                    392: 
                    393:              if (sfile.st_ino == sdescriptor.st_ino
                    394:                  && sfile.st_dev == sdescriptor.st_dev)
                    395:                {
                    396:                  /* Close the file before assuming we've succeeded to
                    397:                     pick up any trailing errors.  */
                    398:                  if (close (o) < 0)
                    399:                    {
                    400:                      zerr = "close";
                    401:                      break;
                    402:                    }
                    403: 
                    404:                  o = -1;
                    405: 
                    406:                  /* We have the lock.  */
                    407:                  fret = TRUE;
                    408:                  break;
                    409:                }
                    410:            }
                    411:        }
                    412: 
                    413:       /* Loop around and try the lock again.  We keep doing this until
                    414:         the lock file holds a pid that exists.  */
                    415:       (void) close (o);
                    416:       o = -1;
                    417:       fret = TRUE;
                    418:     }
                    419: 
                    420:   if (zerr != NULL)
                    421:     {
                    422:       ulog (LOG_ERROR, "%s (%s): %s", zerr, zpath, strerror (errno));
                    423:       if (pferr != NULL)
                    424:        *pferr = TRUE;
                    425:     }
                    426: 
                    427:   if (o >= 0)
                    428:     (void) close (o);
                    429: 
                    430:   ubuffree (zfree);
                    431: 
                    432:   /* It would be nice if we could leave the temporary file around for
                    433:      future calls, but considering that we create lock files in
                    434:      various different directories it's probably more trouble than
                    435:      it's worth.  */
                    436:   if (remove (ztempfile) != 0)
                    437:     ulog (LOG_ERROR, "remove (%s): %s", ztempfile, strerror (errno));
                    438: 
                    439:   ubuffree (ztempfile);
                    440: 
                    441:   return fret;
                    442: }
                    443: 
                    444: /* Unlock something.  The fspooldir argument is as in fsdo_lock.  */
                    445: 
                    446: boolean
                    447: fsdo_unlock (zlock, fspooldir)
                    448:      const char *zlock;
                    449:      boolean fspooldir;
                    450: {
                    451:   char *zfree;
                    452:   const char *zpath;
                    453: 
                    454:   if (fspooldir)
                    455:     {
                    456:       zfree = NULL;
                    457:       zpath = zlock;
                    458:     }
                    459:   else
                    460:     {
                    461:       zfree = zsysdep_in_dir (zSlockdir, zlock);
                    462:       zpath = zfree;
                    463:     }
                    464: 
                    465:   if (remove (zpath) == 0
                    466:       || errno == ENOENT)
                    467:     {
                    468:       ubuffree (zfree);
                    469:       return TRUE;
                    470:     }
                    471:   else
                    472:     {
                    473:       ulog (LOG_ERROR, "remove (%s): %s", zpath, strerror (errno));
                    474:       ubuffree (zfree);
                    475:       return FALSE;
                    476:     }
                    477: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.