/**************** * 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 }
/* 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; }