1 /* This file is part of the Zebra server.
2 Copyright (C) 1994-2010 Index Data
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 #include <sys/types.h>
29 #include <sys/locking.h>
35 #include <idzebra/flock.h>
36 #include <zebra-lock.h>
37 #include <yaz/xmalloc.h>
40 /** have this module (mutex) been initialized? */
41 static int initialized = 0;
43 /** whether fcntl locks are shared for all threads in a process (POSIX) */
44 static int posix_locks = 1;
46 /** mutex for lock_list below */
47 Zebra_mutex lock_list_mutex;
49 /** our list of file locked files */
50 static struct zebra_lock_info *lock_list = 0;
52 /** the internal handle, with a pointer to each lock file info */
53 struct zebra_lock_handle {
55 /** so we can call zebra_lock_rdwr_wunlock or zebra_lock_lock_runlock */
58 struct zebra_lock_info *p;
61 struct zebra_lock_info {
62 /** file descriptor */
64 /** full path (xmalloc'ed) */
66 /** reference counter: number of zebra_lock_handles pointing to us */
69 /** number of file write locks/read locks */
70 int no_file_write_lock;
71 int no_file_read_lock;
72 Zebra_lock_rdwr rdwr_lock;
73 Zebra_mutex file_mutex;
75 /** next in lock list */
76 struct zebra_lock_info *next;
79 static int log_level = 0;
81 char *zebra_mk_fname(const char *dir, const char *name)
83 int dlen = dir ? strlen(dir) : 0;
84 char *fname = xmalloc(dlen + strlen(name) + 3);
89 int last_one = dir[dlen-1];
91 if (!strchr("/\\:", last_one))
92 sprintf(fname, "%s\\%s", dir, name);
94 sprintf(fname, "%s%s", dir, name);
97 sprintf(fname, "%s", name);
101 int last_one = dir[dlen-1];
103 if (!strchr("/", last_one))
104 sprintf(fname, "%s/%s", dir, name);
106 sprintf(fname, "%s%s", dir, name);
109 sprintf(fname, "%s", name);
114 ZebraLockHandle zebra_lock_create(const char *dir, const char *name)
116 char *fname = zebra_mk_fname(dir, name);
117 struct zebra_lock_info *p = 0;
118 ZebraLockHandle h = 0;
122 zebra_mutex_lock(&lock_list_mutex);
123 /* see if we have the same filename in a global list of "lock files" */
127 for (p = lock_list; p ; p = p->next)
128 if (!strcmp(p->fname, fname))
133 { /* didn't match (or we didn't want it to match! */
134 p = (struct zebra_lock_info *) xmalloc(sizeof(*p));
138 p->fd = open(name, O_BINARY|O_RDONLY);
140 p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
142 p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
147 yaz_log(YLOG_WARN | YLOG_ERRNO,
148 "zebra_lock_create fail fname=%s", fname);
154 fname = 0; /* fname buffer now owned by p->fname */
157 zebra_lock_rdwr_init(&p->rdwr_lock);
159 zebra_mutex_init(&p->file_mutex);
160 p->no_file_write_lock = 0;
161 p->no_file_read_lock = 0;
169 /* we have lock info so we can make a handle pointing to that */
171 h = (ZebraLockHandle) xmalloc(sizeof(*h));
176 yaz_log(log_level, "zebra_lock_create fd=%d p=%p fname=%s",
177 h->p->fd, h, p->fname);
179 zebra_mutex_unlock(&lock_list_mutex);
180 xfree(fname); /* free it - if it's still there */
185 void zebra_lock_destroy(ZebraLockHandle h)
189 yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s",
190 h->p->fd, h, h->p->fname);
191 zebra_mutex_lock(&lock_list_mutex);
192 yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s refcount=%d",
193 h->p->fd, h, h->p->fname, h->p->ref_count);
194 assert(h->p->ref_count > 0);
196 if (h->p->ref_count == 0)
198 /* must remove shared info from lock_list */
199 struct zebra_lock_info **hp = &lock_list;
211 yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s remove",
212 h->p->fd, h, h->p->fname);
216 zebra_lock_rdwr_destroy(&h->p->rdwr_lock);
217 zebra_mutex_destroy(&h->p->file_mutex);
225 zebra_mutex_unlock(&lock_list_mutex);
229 static int unixLock(int fd, int type, int cmd)
234 area.l_whence = SEEK_SET;
235 area.l_len = area.l_start = 0L;
237 yaz_log(log_level, "fcntl begin type=%d fd=%d", type, fd);
238 r = fcntl(fd, cmd, &area);
240 yaz_log(YLOG_WARN|YLOG_ERRNO, "fcntl FAIL type=%d fd=%d", type, fd);
242 yaz_log(log_level, "fcntl type=%d OK fd=%d", type, fd);
248 int zebra_lock_w(ZebraLockHandle h)
252 yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s begin",
253 h->p->fd, h, h->p->fname);
256 while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
260 zebra_lock_rdwr_wlock(&h->p->rdwr_lock);
262 zebra_mutex_lock(&h->p->file_mutex);
263 if (h->p->no_file_write_lock == 0)
265 h->p->no_file_write_lock++;
268 /* if there is already a read lock.. upgrade to write lock */
269 r = unixLock(h->p->fd, F_WRLCK, F_SETLKW);
275 zebra_mutex_unlock(&h->p->file_mutex);
279 yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s end",
280 h->p->fd, h, h->p->fname);
285 int zebra_lock_r(ZebraLockHandle h)
290 yaz_log(log_level, "zebra_lock_r fd=%d p=%p fname=%s begin",
291 h->p->fd, h, h->p->fname);
293 while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
297 zebra_lock_rdwr_rlock(&h->p->rdwr_lock);
299 zebra_mutex_lock(&h->p->file_mutex);
300 if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
302 h->p->no_file_read_lock++;
305 /* only read lock if no write locks already */
306 r = unixLock(h->p->fd, F_RDLCK, F_SETLKW);
312 zebra_mutex_unlock(&h->p->file_mutex);
316 yaz_log(log_level, "zebra_lock_r fd=%d p=%p fname=%s end",
317 h->p->fd, h, h->p->fname);
321 int zebra_unlock(ZebraLockHandle h)
324 yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s begin",
325 h->p->fd, h, h->p->fname);
327 r = _locking(h->p->fd, _LK_UNLCK, 1);
329 zebra_mutex_lock(&h->p->file_mutex);
332 if (h->p->no_file_write_lock > 0)
333 h->p->no_file_write_lock--;
337 if (h->p->no_file_read_lock > 0)
338 h->p->no_file_read_lock--;
340 if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
341 r = unixLock(h->p->fd, F_UNLCK, F_SETLKW);
348 zebra_mutex_unlock(&h->p->file_mutex);
353 zebra_lock_rdwr_wunlock(&h->p->rdwr_lock);
355 zebra_lock_rdwr_runlock(&h->p->rdwr_lock);
358 yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s end",
359 h->p->fd, h, h->p->fname);
363 /** \brief see if the fcntl locking is not POSIX
365 * The default posix_locks=1 is assumed.. This function sets posix_locks
366 * to zero if linuxthreads is in use.
368 static int check_for_linuxthreads(void)
371 #ifdef _CS_GNU_LIBPTHREAD_VERSION
373 size_t r = confstr(_CS_GNU_LIBPTHREAD_VERSION, conf_buf, sizeof(conf_buf));
376 yaz_log(YLOG_WARN|YLOG_ERRNO, "confstr failed");
379 if (strncmp(conf_buf, "linuxthreads", 12) == 0)
380 posix_locks = 0; /* Using linuxthreads.. */
382 posix_locks = 0; /* Old GLIBC on Linux. Assume linuxthreads */
388 void zebra_flock_init()
393 log_level = yaz_log_module_level("flock");
394 yaz_log(log_level, "zebra_flock_init");
395 check_for_linuxthreads();
396 zebra_mutex_init(&lock_list_mutex);
397 yaz_log(log_level, "posix_locks: %d", posix_locks);
404 * c-file-style: "Stroustrup"
405 * indent-tabs-mode: nil
407 * vim: shiftwidth=4 tabstop=8 expandtab