1 /* $Id: flock.c,v 1.11 2006-06-30 13:02:20 adam Exp $
2 Copyright (C) 1995-2006
5 This file is part of the Zebra server.
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra. If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
29 #include <sys/types.h>
32 #include <sys/locking.h>
38 #include <idzebra/flock.h>
39 #include <zebra-lock.h>
40 #include <yaz/xmalloc.h>
43 /** whether lock file desciptors are shared within one process' threads */
44 #define ZEBRA_FLOCK_SHARE_FD_IN_THREADS 1
46 /** whether we always lock on the file - even if rd/wr locks should do */
47 #define ZEBRA_FLOCK_FILE_LOCK_ALWAYS 1
49 /** whether we should also use pthreads locking to do "extra" rd/wr locks */
50 #define ZEBRA_FLOCK_EXTRA_RDWR_LOCKS 1
52 /** whether this module should debug */
55 /** have this module (mutex) been initialized? */
56 static int initialized = 0;
58 /** mutex for lock_list below */
59 Zebra_mutex lock_list_mutex;
61 /** our list of file locked files */
62 static struct zebra_lock_info *lock_list = 0;
64 /** the internal handle, with a pointer to each lock file info */
65 struct zebra_lock_handle {
67 /** so we can call zebra_lock_rdwr_wunlock or zebra_lock_lock_runlock */
70 struct zebra_lock_info *p;
73 struct zebra_lock_info {
74 /** file descriptor */
76 /** full path (xmalloc'ed) */
78 /** reference counter: number of zebra_lock_handles pointing to us */
81 /** number of file write locks/read locks */
82 int no_file_write_lock;
83 int no_file_read_lock;
84 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
85 Zebra_lock_rdwr rdwr_lock;
87 Zebra_mutex file_mutex;
89 /** next in lock list */
90 struct zebra_lock_info *next;
93 static int log_level = 0;
95 char *zebra_mk_fname(const char *dir, const char *name)
97 int dlen = dir ? strlen(dir) : 0;
98 char *fname = xmalloc(dlen + strlen(name) + 3);
103 int last_one = dir[dlen-1];
105 if (!strchr("/\\:", last_one))
106 sprintf(fname, "%s\\%s", dir, name);
108 sprintf(fname, "%s%s", dir, name);
111 sprintf(fname, "%s", name);
115 int last_one = dir[dlen-1];
117 if (!strchr("/", last_one))
118 sprintf(fname, "%s/%s", dir, name);
120 sprintf(fname, "%s%s", dir, name);
123 sprintf(fname, "%s", name);
128 ZebraLockHandle zebra_lock_create(const char *dir, const char *name)
130 char *fname = zebra_mk_fname(dir, name);
131 struct zebra_lock_info *p = 0;
132 ZebraLockHandle h = 0;
136 zebra_mutex_lock(&lock_list_mutex);
137 /* see if we have the same filename in a global list of "lock files" */
139 #if ZEBRA_FLOCK_SHARE_FD_IN_THREADS
140 for (p = lock_list; p ; p = p->next)
141 if (!strcmp(p->fname, fname))
146 { /* didn't match (or we didn't want it to match! */
147 p = (struct zebra_lock_info *) xmalloc(sizeof(*p));
151 p->fd = open(name, O_BINARY|O_RDONLY);
153 p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
155 p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
160 yaz_log(YLOG_WARN | YLOG_ERRNO,
161 "zebra_lock_create fail fname=%s", fname);
167 fname = 0; /* fname buffer now owned by p->fname */
169 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
170 zebra_lock_rdwr_init(&p->rdwr_lock);
172 zebra_mutex_init(&p->file_mutex);
173 p->no_file_write_lock = 0;
174 p->no_file_read_lock = 0;
182 /* we have lock info so we can make a handle pointing to that */
184 h = (ZebraLockHandle) xmalloc(sizeof(*h));
189 yaz_log(log_level, "zebra_lock_create fd=%d p=%p fname=%s",
190 h->p->fd, h, p->fname);
192 zebra_mutex_unlock(&lock_list_mutex);
193 xfree(fname); /* free it - if it's still there */
198 void zebra_lock_destroy(ZebraLockHandle h)
202 yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s",
203 h->p->fd, h, h->p->fname);
204 zebra_mutex_lock(&lock_list_mutex);
205 yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s refcount=%d",
206 h->p->fd, h, h->p->fname, h->p->ref_count);
207 assert(h->p->ref_count > 0);
209 if (h->p->ref_count == 0)
211 /* must remove shared info from lock_list */
212 struct zebra_lock_info **hp = &lock_list;
224 yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s remove",
225 h->p->fd, h, h->p->fname);
228 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
229 zebra_lock_rdwr_destroy(&h->p->rdwr_lock);
231 zebra_mutex_destroy(&h->p->file_mutex);
239 zebra_mutex_unlock(&lock_list_mutex);
243 static int unixLock(int fd, int type, int cmd)
248 area.l_whence = SEEK_SET;
249 area.l_len = area.l_start = 0L;
251 yaz_log(log_level, "fcntl begin type=%d fd=%d", type, fd);
252 r = fcntl(fd, cmd, &area);
254 yaz_log(YLOG_WARN|YLOG_ERRNO, "fcntl FAIL type=%d fd=%d", type, fd);
256 yaz_log(log_level, "fcntl type=%d OK fd=%d", type, fd);
262 int zebra_lock_w(ZebraLockHandle h)
265 int do_lock = ZEBRA_FLOCK_FILE_LOCK_ALWAYS;
266 yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s begin",
267 h->p->fd, h, h->p->fname);
270 while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
273 zebra_mutex_lock(&h->p->file_mutex);
274 if (h->p->no_file_write_lock == 0)
276 h->p->no_file_write_lock++;
277 zebra_mutex_unlock(&h->p->file_mutex);
280 yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s 2",
281 h->p->fd, h, h->p->fname);
282 /* if there is already a read lock.. upgrade to write lock */
283 r = unixLock(h->p->fd, F_WRLCK, F_SETLKW);
285 yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s 3",
286 h->p->fd, h, h->p->fname);
288 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
289 zebra_lock_rdwr_wlock(&h->p->rdwr_lock);
292 yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s end",
293 h->p->fd, h, h->p->fname);
299 int zebra_lock_r(ZebraLockHandle h)
302 int do_lock = ZEBRA_FLOCK_FILE_LOCK_ALWAYS;
304 yaz_log(log_level, "zebra_lock_r fd=%d p=%p fname=%s",
305 h->p->fd, h, h->p->fname);
307 while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
310 zebra_mutex_lock(&h->p->file_mutex);
311 if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
313 h->p->no_file_read_lock++;
314 zebra_mutex_unlock(&h->p->file_mutex);
318 /* only read lock if no write locks already */
319 r = unixLock(h->p->fd, F_RDLCK, F_SETLKW);
322 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
323 zebra_lock_rdwr_rlock(&h->p->rdwr_lock);
330 int zebra_unlock(ZebraLockHandle h)
333 int do_unlock = ZEBRA_FLOCK_FILE_LOCK_ALWAYS;
334 yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s begin",
335 h->p->fd, h, h->p->fname);
337 r = _locking(h->p->fd, _LK_UNLCK, 1);
339 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
341 zebra_lock_rdwr_wunlock(&h->p->rdwr_lock);
343 zebra_lock_rdwr_runlock(&h->p->rdwr_lock);
346 zebra_mutex_lock(&h->p->file_mutex);
348 h->p->no_file_write_lock--;
350 h->p->no_file_read_lock--;
351 if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
353 zebra_mutex_unlock(&h->p->file_mutex);
356 r = unixLock(h->p->fd, F_UNLCK, F_SETLKW);
357 yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s end",
358 h->p->fd, h, h->p->fname);
363 void zebra_flock_init()
367 log_level = yaz_log_module_level("flock");
369 log_level = YLOG_LOG|YLOG_FLUSH;
372 zebra_mutex_init(&lock_list_mutex);
374 yaz_log(log_level, "zebra_flock_init");
380 * indent-tabs-mode: nil
382 * vim: shiftwidth=4 tabstop=8 expandtab