1 /* dotlock.c - dotfile locking
2 * Copyright (C) 1998, 2000, 2001, 2003, 2004,
3 * 2005, 2006 Free Software Foundation, Inc.
5 * This file is part of JNLIB.
7 * JNLIB is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
12 * JNLIB is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
30 #ifndef HAVE_DOSISH_SYSTEM
31 #include <sys/utsname.h>
33 #include <sys/types.h>
39 #include "libjnlib-config.h"
42 #if !defined(DIRSEP_C) && !defined(EXTSEP_C) \
43 && !defined(DIRSEP_S) && !defined(EXTSEP_S)
44 #ifdef HAVE_DOSISH_SYSTEM
60 struct dotlock_handle
*next
;
61 char *tname
; /* Name of lockfile template. */
62 size_t nodename_off
; /* Offset in TNAME of the nodename part. */
63 size_t nodename_len
; /* Length of the nodename part. */
64 char *lockname
; /* Name of the real lockfile. */
65 int locked
; /* Lock status. */
66 int disable
; /* When true, locking is disabled. */
70 static volatile DOTLOCK all_lockfiles
;
71 static int never_lock
;
73 static int read_lockfile (DOTLOCK h
, int *same_node
);
82 * Create a lockfile with the given name and return an object of
83 * type DOTLOCK which may be used later to actually do the lock.
84 * A cleanup routine gets installed to cleanup left over locks
85 * or other files used together with the lock mechanism.
86 * Although the function is called dotlock, this does not necessarily
87 * mean that real lockfiles are used - the function may decide to
88 * use fcntl locking. Calling the function with NULL only install
89 * the atexit handler and maybe used to assure that the cleanup
90 * is called after all other atexit handlers.
92 * Notes: This function creates a lock file in the same directory
93 * as file_to_lock with the name "file_to_lock.lock"
94 * A temporary file ".#lk.<hostname>.pid[.threadid] is used.
95 * This function does nothing for Windoze.
98 create_dotlock( const char *file_to_lock
)
100 static int initialized
;
104 const char *nodename
;
107 #ifndef HAVE_DOSISH_SYSTEM
108 struct utsname utsbuf
;
113 atexit( dotlock_remove_lockfiles
);
117 return NULL
; /* Only initialization was requested. */
119 h
= jnlib_xcalloc ( 1, sizeof *h
);
124 /* fixme: aquire mutex on all_lockfiles */
126 h
->next
= all_lockfiles
;
131 #ifndef HAVE_DOSISH_SYSTEM
132 sprintf (pidstr
, "%10d\n", (int)getpid() );
133 /* fixme: add the hostname to the second line (FQDN or IP addr?) */
135 /* Create a temporary file. */
136 if ( uname ( &utsbuf
) )
137 nodename
= "unknown";
139 nodename
= utsbuf
.nodename
;
143 char *iter
= (char *) nodename
;
144 for (; iter
[0]; iter
++)
148 #endif /* __riscos__ */
150 if ( !(dirpart
= strrchr ( file_to_lock
, DIRSEP_C
)) )
157 dirpartlen
= dirpart
- file_to_lock
;
158 dirpart
= file_to_lock
;
162 /* fixme: aquire mutex on all_lockfiles */
164 h
->next
= all_lockfiles
;
167 h
->tname
= jnlib_xmalloc ( dirpartlen
+ 6+30+ strlen(nodename
) + 11 );
168 h
->nodename_len
= strlen (nodename
);
170 sprintf (h
->tname
, "%.*s/.#lk%p.", dirpartlen
, dirpart
, h
);
171 h
->nodename_off
= strlen (h
->tname
);
172 sprintf (h
->tname
+h
->nodename_off
, "%s.%d", nodename
, (int)getpid ());
173 #else /* __riscos__ */
174 sprintf (h
->tname
, "%.*s.lk%p/", dirpartlen
, dirpart
, h
);
175 h
->nodename_off
= strlen (h
->tname
);
176 sprintf (h
->tname
+h
->nodename_off
, "%s/%d", nodename
, (int)getpid () );
177 #endif /* __riscos__ */
182 fd
= open (h
->tname
, O_WRONLY
|O_CREAT
|O_EXCL
,
183 S_IRUSR
|S_IRGRP
|S_IROTH
|S_IWUSR
);
185 while (fd
== -1 && errno
== EINTR
);
189 all_lockfiles
= h
->next
;
190 log_error ( "failed to create temporary file `%s': %s\n",
191 h
->tname
, strerror(errno
));
192 jnlib_free(h
->tname
);
196 if ( write (fd
, pidstr
, 11 ) != 11 )
198 if ( write (fd
, nodename
, strlen (nodename
) ) != strlen (nodename
) )
200 if ( write (fd
, "\n", 1 ) != 1 )
208 #endif /* !HAVE_DOSISH_SYSTEM */
209 h
->lockname
= jnlib_xmalloc ( strlen (file_to_lock
) + 6 );
210 strcpy (stpcpy(h
->lockname
, file_to_lock
), EXTSEP_S
"lock");
213 all_lockfiles
= h
->next
;
215 /* fixme: release mutex */
217 log_error ( "error writing to `%s': %s\n", h
->tname
, strerror(errno
) );
220 jnlib_free(h
->tname
);
227 destroy_dotlock ( DOTLOCK h
)
229 #ifndef HAVE_DOSISH_SYSTEM
234 /* First remove the handle from our global list of all locks. */
235 for (hprev
=NULL
, htmp
=all_lockfiles
; htmp
; hprev
=htmp
, htmp
=htmp
->next
)
239 hprev
->next
= htmp
->next
;
241 all_lockfiles
= htmp
->next
;
246 /* Second destroy the lock. */
249 if (h
->locked
&& h
->lockname
)
250 unlink (h
->lockname
);
253 jnlib_free (h
->tname
);
254 jnlib_free (h
->lockname
);
258 #endif /*!HAVE_DOSISH_SYSTEM*/
264 maybe_deadlock( DOTLOCK h
)
268 for ( r
=all_lockfiles
; r
; r
= r
->next
)
270 if ( r
!= h
&& r
->locked
)
277 * Do a lock on H. A TIMEOUT of 0 returns immediately, -1 waits
278 * forever (hopefully not), other values are reserved (should then be
279 * timeouts in milliseconds). Returns: 0 on success
282 make_dotlock( DOTLOCK h
, long timeout
)
284 #ifdef HAVE_DOSISH_SYSTEM
288 const char *maybe_dead
="";
293 return 0; /* Locks are completely disabled. Return success. */
298 log_debug("oops, `%s' is already locked\n", h
->lockname
);
299 #endif /* !__riscos__ */
306 if ( !link(h
->tname
, h
->lockname
) )
308 /* fixme: better use stat to check the link count */
312 if ( errno
!= EEXIST
)
314 log_error( "lock not made: link() failed: %s\n", strerror(errno
) );
317 #else /* __riscos__ */
318 if ( !renamefile(h
->tname
, h
->lockname
) )
323 if ( errno
!= EEXIST
)
325 log_error( "lock not made: rename() failed: %s\n", strerror(errno
) );
328 #endif /* __riscos__ */
330 if ( (pid
= read_lockfile (h
, &same_node
)) == -1 )
332 if ( errno
!= ENOENT
)
334 log_info ("cannot read lockfile\n");
337 log_info( "lockfile disappeared\n");
340 else if ( pid
== getpid() && same_node
)
342 log_info( "Oops: lock already held by us\n");
346 else if ( same_node
&& kill (pid
, 0) && errno
== ESRCH
)
349 log_info ("removing stale lockfile (created by %d)", pid
);
350 unlink (h
->lockname
);
352 #else /* __riscos__ */
353 /* Under RISCOS we are *pretty* sure that the other task
354 is dead and therefore we remove the stale lock file. */
355 maybe_dead
= " - probably dead - removing lock";
357 #endif /* __riscos__ */
362 /* Wait until lock has been released. */
365 log_info ("waiting for lock (held by %d%s) %s...\n",
366 pid
, maybe_dead
, maybe_deadlock(h
)? "(deadlock?) ":"");
369 /* We can't use sleep, cause signals may be blocked. */
370 tv
.tv_sec
= 1 + backoff
;
372 select(0, NULL
, NULL
, NULL
, &tv
);
380 #endif /* !HAVE_DOSISH_SYSTEM */
386 * Returns: 0 := success
389 release_dotlock( DOTLOCK h
)
391 #ifdef HAVE_DOSISH_SYSTEM
396 /* To avoid atexit race conditions we first check whether there are
397 any locks left. It might happen that another atexit handler
398 tries to release the lock while the atexit handler of this module
399 already ran and thus H is undefined. */
408 log_debug("oops, `%s' is not locked\n", h
->lockname
);
412 pid
= read_lockfile (h
, &same_node
);
415 log_error( "release_dotlock: lockfile error\n");
418 if ( pid
!= getpid() || !same_node
)
420 log_error( "release_dotlock: not our lock (pid=%d)\n", pid
);
424 if ( unlink( h
->lockname
) )
426 log_error( "release_dotlock: error removing lockfile `%s'",
430 #else /* __riscos__ */
431 if ( renamefile(h
->lockname
, h
->tname
) )
433 log_error( "release_dotlock: error renaming lockfile `%s' to `%s'",
434 h
->lockname
, h
->tname
);
437 #endif /* __riscos__ */
438 /* fixme: check that the link count is now 1 */
441 #endif /* !HAVE_DOSISH_SYSTEM */
446 Read the lock file and return the pid, returns -1 on error. True
447 will be stored at SAME_NODE if the lock file has been created on
451 read_lockfile (DOTLOCK h
, int *same_node
)
453 #ifdef HAVE_DOSISH_SYSTEM
456 char buffer_space
[10+1+70+1]; /* 70 is just an estimated value; node
457 name are usually shorter. */
464 expected_len
= 10 + 1 + h
->nodename_len
+ 1;
465 if ( expected_len
>= sizeof buffer_space
)
466 buffer
= jnlib_xmalloc (expected_len
);
468 buffer
= buffer_space
;
470 if ( (fd
= open (h
->lockname
, O_RDONLY
)) == -1 )
473 log_info ("error opening lockfile `%s': %s\n",
474 h
->lockname
, strerror(errno
) );
475 if (buffer
!= buffer_space
)
477 errno
= e
; /* Need to return ERRNO here. */
485 res
= read (fd
, p
, expected_len
- nread
);
486 if (res
== -1 && errno
== EINTR
)
490 log_info ("error reading lockfile `%s'", h
->lockname
);
492 if (buffer
!= buffer_space
)
494 errno
= 0; /* Do not return an inappropriate ERRNO. */
500 while (res
&& nread
!= expected_len
);
505 log_info ("invalid size of lockfile `%s'", h
->lockname
);
506 if (buffer
!= buffer_space
)
508 errno
= 0; /* Do not return an inappropriate ERRNO. */
512 if (buffer
[10] != '\n'
513 || (buffer
[10] = 0, pid
= atoi (buffer
)) == -1
516 #else /* __riscos__ */
517 || (!pid
&& riscos_getpid())
518 #endif /* __riscos__ */
521 log_error ("invalid pid %d in lockfile `%s'", pid
, h
->lockname
);
522 if (buffer
!= buffer_space
)
528 if (nread
== expected_len
529 && !memcmp (h
->tname
+h
->nodename_off
, buffer
+11, h
->nodename_len
)
530 && buffer
[11+h
->nodename_len
] == '\n')
533 if (buffer
!= buffer_space
)
541 dotlock_remove_lockfiles()
543 #ifndef HAVE_DOSISH_SYSTEM
547 all_lockfiles
= NULL
;