Annotation of coherent/g/usr/lib/uucp/tay104/unix/lock.c, revision 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.