diff options
author | Werner Koch <[email protected]> | 2000-01-24 12:05:50 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2000-01-24 12:05:50 +0000 |
commit | 54b141f63e3fda38b6e780d0f47c192c6542a49b (patch) | |
tree | e80be78a69a8dc5c9286da4d4590ec7491c63b39 /jnlib/dotlock.c | |
parent | See ChangeLog: Mon Jan 24 13:04:28 CET 2000 Werner Koch (diff) | |
download | gnupg-54b141f63e3fda38b6e780d0f47c192c6542a49b.tar.gz gnupg-54b141f63e3fda38b6e780d0f47c192c6542a49b.zip |
Add files to new directory.
This is mainly stuff moved from ../util
to here.
Diffstat (limited to 'jnlib/dotlock.c')
-rw-r--r-- | jnlib/dotlock.c | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/jnlib/dotlock.c b/jnlib/dotlock.c new file mode 100644 index 000000000..8e61f7a03 --- /dev/null +++ b/jnlib/dotlock.c @@ -0,0 +1,346 @@ +/* dotlock.c - dotfile locking + * Copyright (C) 1998,2000 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#ifndef HAVE_DOSISH_SYSTEM +#include <sys/utsname.h> +#endif +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <signal.h> + +#include "libjnlib-config.h" +#include "dotlock.h" + +struct dotlock_handle { + struct dotlock_handle *next; + char *tname; /* name of lockfile template */ + char *lockname; /* name of the real lockfile */ + int locked; /* lock status */ +}; + + +static DOTLOCK all_lockfiles; + +static int read_lockfile( const char *name ); +static void remove_lockfiles(void); + +/**************** + * Create a lockfile with the given name and return an object of + * type DOTLOCK which may be used later to actually do the lock. + * A cleanup routine gets installed to cleanup left over locks + * or other files used together with the lockmechanism. + * Althoug the function is called dotlock, this does not necessarily + * mean that real lockfiles are used - the function may decide to + * use fcntl locking. Calling the function with NULL only install + * the atexit handler and maybe used to assure that the cleanup + * is called after all other atexit handlers. + * + * Notes: This function creates a lock file in the same directory + * as file_to_lock with the name "file_to_lock.lock" + * A temporary file ".#lk.<hostname>.pid[.threadid] is used. + * This function does nothing for Windoze. + */ +DOTLOCK +create_dotlock( const char *file_to_lock ) +{ + static int initialized; + DOTLOCK h; + int fd = -1; + char pidstr[16]; + #ifndef HAVE_DOSISH_SYSTEM + struct utsname utsbuf; + #endif + const char *nodename; + const char *dirpart; + int dirpartlen; + + if( !initialized ) { + atexit( remove_lockfiles ); + initialized = 1; + } + if( !file_to_lock ) + return NULL; + + h = jnlib_xcalloc( 1, sizeof *h ); +#ifndef HAVE_DOSISH_SYSTEM + sprintf( pidstr, "%10d\n", (int)getpid() ); + /* fixme: add the hostname to the second line (FQDN or IP addr?) */ + + /* create a temporary file */ + if( uname( &utsbuf ) ) + nodename = "unknown"; + else + nodename = utsbuf.nodename; + + if( !(dirpart = strrchr( file_to_lock, '/' )) ) { + dirpart = "."; + dirpartlen = 1; + } + else { + dirpartlen = dirpart - file_to_lock; + dirpart = file_to_lock; + } + + #ifdef _REENTRANT + /* fixme: aquire mutex on all_lockfiles */ + #endif + h->next = all_lockfiles; + all_lockfiles = h; + + h->tname = jnlib_xmalloc( dirpartlen + 6+30+ strlen(nodename) + 11 ); + sprintf( h->tname, "%.*s/.#lk%p.%s.%d", + dirpartlen, dirpart, h, nodename, (int)getpid() ); + + do { + errno = 0; + fd = open( h->tname, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR ); + } while( fd == -1 && errno == EINTR ); + if( fd == -1 ) { + all_lockfiles = h->next; + log_error( "failed to create temporary file `%s': %s\n", + h->tname, strerror(errno)); + jnlib_free(h->tname); + jnlib_free(h); + return NULL; + } + if( write(fd, pidstr, 11 ) != 11 ) { + all_lockfiles = h->next; + #ifdef _REENTRANT + /* release mutex */ + #endif + log_fatal( "error writing to `%s': %s\n", h->tname, strerror(errno) ); + close(fd); + unlink(h->tname); + jnlib_free(h->tname); + jnlib_free(h); + return NULL; + } + if( close(fd) ) { + all_lockfiles = h->next; + #ifdef _REENTRANT + /* release mutex */ + #endif + log_error( "error closing `%s': %s\n", h->tname, strerror(errno)); + unlink(h->tname); + jnlib_free(h->tname); + jnlib_free(h); + return NULL; + } + + #ifdef _REENTRANT + /* release mutex */ + #endif +#endif /* !HAVE_DOSISH_SYSTEM */ + h->lockname = jnlib_xmalloc( strlen(file_to_lock) + 6 ); + strcpy(stpcpy(h->lockname, file_to_lock), ".lock"); + return h; +} + +static int +maybe_deadlock( DOTLOCK h ) +{ + DOTLOCK r; + + for( r=all_lockfiles; r; r = r->next ) { + if( r != h && r->locked ) + return 1; + } + return 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 ) +{ +#ifdef HAVE_DOSISH_SYSTEM + return 0; +#else + int pid; + const char *maybe_dead=""; + int backoff=0; + + if( h->locked ) { + log_debug("oops, `%s' is already locked\n", h->lockname ); + return 0; + } + + for(;;) { + 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; + } + 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 hold by us\n"); + h->locked = 1; + return 0; /* okay */ + } + else if( kill(pid, 0) && errno == ESRCH ) { + 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 + } + if( timeout == -1 ) { + struct timeval tv; + log_info( "waiting for lock (hold 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 /* !HAVE_DOSISH_SYSTEM */ +} + + +/**************** + * release a lock + * Returns: 0 := success + */ +int +release_dotlock( DOTLOCK h ) +{ +#ifdef HAVE_DOSISH_SYSTEM + return 0; +#else + int pid; + + if( !h->locked ) { + log_debug("oops, `%s' is not locked\n", h->lockname ); + return 0; + } + + pid = read_lockfile( h->lockname ); + if( pid == -1 ) { + log_error( "release_dotlock: lockfile error\n"); + return -1; + } + if( pid != getpid() ) { + log_error( "release_dotlock: not our lock (pid=%d)\n", pid); + return -1; + } + if( unlink( h->lockname ) ) { + log_error( "release_dotlock: error removing lockfile `%s'", + h->lockname); + return -1; + } + /* fixme: check that the link count is now 1 */ + h->locked = 0; + return 0; +#endif /* !HAVE_DOSISH_SYSTEM */ +} + + +/**************** + * Read the lock file and return the pid, returns -1 on error. + */ +static int +read_lockfile( const char *name ) +{ + #ifdef HAVE_DOSISH_SYSTEM + return 0; + #else + int fd, pid; + char pidstr[16]; + + if( (fd = open(name, O_RDONLY)) == -1 ) { + int e = errno; + log_debug("error opening lockfile `%s': %s\n", name, strerror(errno) ); + errno = e; + return -1; + } + if( read(fd, pidstr, 10 ) != 10 ) { /* Read 10 digits w/o newline */ + log_debug("error reading lockfile `%s'", name ); + close(fd); + errno = 0; + return -1; + } + pidstr[10] = 0; /* terminate pid string */ + close(fd); + pid = atoi(pidstr); + if( !pid || pid == -1 ) { + log_error("invalid pid %d in lockfile `%s'", pid, name ); + errno = 0; + return -1; + } + return pid; + #endif +} + + +static void +remove_lockfiles() +{ + #ifndef HAVE_DOSISH_SYSTEM + DOTLOCK h, h2; + + h = all_lockfiles; + all_lockfiles = NULL; + + while( h ) { + h2 = h->next; + if( h->locked ) + unlink( h->lockname ); + unlink(h->tname); + jnlib_free(h->tname); + jnlib_free(h->lockname); + jnlib_free(h); + h = h2; + } + #endif +} + |