Example #1
0
/****************
 * Do a lock on H. A TIMEOUT of 0 returns immediately,
 * -1 waits forever (hopefully not), other
 * values are timeouts in milliseconds.
 * Returns: 0 on success
 */
int
make_dotlock( DOTLOCK h, long timeout )
{
#if defined (HAVE_DOSISH_SYSTEM)
    return 0;
#else
    int  pid;
    const char *maybe_dead="";
    int backoff=0;

    if( h->disable ) {
	return 0;
    }

    if( h->locked ) {
#ifndef __riscos__
	log_debug("oops, `%s' is already locked\n", h->lockname );
#endif /* !__riscos__ */
	return 0;
    }

    for(;;) {
#ifndef __riscos__
	if( !link(h->tname, h->lockname) ) {
	    /* fixme: better use stat to check the link count */
	    h->locked = 1;
	    return 0; /* okay */
	}
	if( errno != EEXIST ) {
	    log_error( "lock not made: link() failed: %s\n", strerror(errno) );
	    return -1;
	}
#else /* __riscos__ */
        if( !renamefile(h->tname, h->lockname) ) {
            h->locked = 1;
            return 0; /* okay */
        }
        if( errno != EEXIST ) {
	    log_error( "lock not made: rename() failed: %s\n", strerror(errno) );
	    return -1;
        }
#endif /* __riscos__ */
	if( (pid = read_lockfile(h->lockname)) == -1 ) {
	    if( errno != ENOENT ) {
		log_info("cannot read lockfile\n");
		return -1;
	    }
	    log_info( "lockfile disappeared\n");
	    continue;
	}
	else if( pid == getpid() ) {
	    log_info( "Oops: lock already held by us\n");
	    h->locked = 1;
	    return 0; /* okay */
	}
	else if( kill(pid, 0) && errno == ESRCH ) {
#ifndef __riscos__
	    maybe_dead = " - probably dead";
	 #if 0 /* we should not do this without checking the permissions */
	       /* and the hostname */
	    log_info( "removing stale lockfile (created by %d)", pid );
	 #endif
#else /* __riscos__ */
            /* we are *pretty* sure that the other task is dead and therefore
               we remove the other lock file */
            maybe_dead = " - probably dead - removing lock";
            unlink(h->lockname);
#endif /* __riscos__ */
	}
	if( timeout == -1 ) {
	    struct timeval tv;
	    log_info( "waiting for lock (held by %d%s) %s...\n",
		      pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":"");


	    /* can't use sleep, cause signals may be blocked */
	    tv.tv_sec = 1 + backoff;
	    tv.tv_usec = 0;
	    select(0, NULL, NULL, NULL, &tv);
	    if( backoff < 10 )
		backoff++ ;
	}
	else
	    return -1;
    }
    /*not reached */
#endif
}
Example #2
0
/* Unix specific code of make_dotlock.  Returns 0 on success and -1 on
   error.  */
static int
dotlock_take_unix (dotlock_t h, long timeout)
{
  int wtime = 0;
  int sumtime = 0;
  int pid;
  int lastpid = -1;
  int ownerchanged;
  const char *maybe_dead="";
  int same_node;

 again:
  if (h->use_o_excl)
    {
      /* No hardlink support - use open(O_EXCL).  */
      int fd;

      do
        {
          jnlib_set_errno (0);
          fd = open (h->lockname, O_WRONLY|O_CREAT|O_EXCL,
                     S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
        }
      while (fd == -1 && errno == EINTR);

      if (fd == -1 && errno == EEXIST)
        ; /* Lock held by another process.  */
      else if (fd == -1)
        {
          my_error_2 ("lock not made: open(O_EXCL) of `%s' failed: %s\n",
                      h->lockname, strerror (errno));
          return -1;
        }
      else
        {
          char pidstr[16];

          snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid());
          if (write (fd, pidstr, 11 ) == 11
              && write (fd, h->tname + h->nodename_off,h->nodename_len)
              == h->nodename_len
              && write (fd, "\n", 1) == 1
              && !close (fd))
            {
              h->locked = 1;
              return 0;
            }
          /* Write error.  */
          my_error_2 ("lock not made: writing to `%s' failed: %s\n",
                      h->lockname, strerror (errno));
          close (fd);
          unlink (h->lockname);
          return -1;
        }
    }
  else /* Standard method:  Use hardlinks.  */
    {
      struct stat sb;

      link (h->tname, h->lockname);

      if (stat (h->tname, &sb))
        {
          my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n",
                      strerror (errno));
          /* In theory this might be a severe error: It is possible
             that link succeeded but stat failed due to changed
             permissions.  We can't do anything about it, though.  */
          return -1;
        }

      if (sb.st_nlink == 2)
        {
          h->locked = 1;
          return 0; /* Okay.  */
        }
    }

  /* Check for stale lock files.  */
  if ( (pid = read_lockfile (h, &same_node)) == -1 )
    {
      if ( errno != ENOENT )
        {
          my_info_0 ("cannot read lockfile\n");
          return -1;
        }
      my_info_0 ("lockfile disappeared\n");
      goto again;
    }
  else if ( pid == getpid() && same_node )
    {
      my_info_0 ("Oops: lock already held by us\n");
      h->locked = 1;
      return 0; /* okay */
    }
  else if ( same_node && kill (pid, 0) && errno == ESRCH )
    {
      /* Note: It is unlikley that we get a race here unless a pid is
         reused too fast or a new process with the same pid as the one
         of the stale file tries to lock right at the same time as we.  */
      my_info_1 (_("removing stale lockfile (created by %d)\n"), pid);
      unlink (h->lockname);
      goto again;
    }

  if (lastpid == -1)
    lastpid = pid;
  ownerchanged = (pid != lastpid);

  if (timeout)
    {
      struct timeval tv;

      /* Wait until lock has been released.  We use increasing retry
         intervals of 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s
         but reset it if the lock owner meanwhile changed.  */
      if (!wtime || ownerchanged)
        wtime = 50;
      else if (wtime < 800)
        wtime *= 2;
      else if (wtime == 800)
        wtime = 2000;
      else if (wtime < 8000)
        wtime *= 2;

      if (timeout > 0)
        {
          if (wtime > timeout)
            wtime = timeout;
          timeout -= wtime;
        }

      sumtime += wtime;
      if (sumtime >= 1500)
        {
          sumtime = 0;
          my_info_3 (_("waiting for lock (held by %d%s) %s...\n"),
                     pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):"");
        }


      tv.tv_sec = wtime / 1000;
      tv.tv_usec = (wtime % 1000) * 1000;
      select (0, NULL, NULL, NULL, &tv);
      goto again;
    }

  jnlib_set_errno (EACCES);
  return -1;
}