1 /* This file is part of the Zebra server.
2 Copyright (C) 2004-2013 Index Data
3
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
7 version.
8
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
12 for more details.
13
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
17
18 */
19
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdio.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <sys/types.h>
30 #ifdef WIN32
31 #include <io.h>
32 #include <sys/locking.h>
33 #endif
34 #if HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #include <idzebra/flock.h>
39 #include <zebra-lock.h>
40 #include <yaz/xmalloc.h>
41 #include <yaz/log.h>
42
43 /** have this module (mutex) been initialized? */
44 static int initialized = 0;
45
46 /** whether fcntl locks are shared for all threads in a process (POSIX) */
47 static int posix_locks = 1;
48
49 /** mutex for lock_list below */
50 Zebra_mutex lock_list_mutex;
51
52 /** our list of file locked files */
53 static struct zebra_lock_info *lock_list = 0;
54
55 /** the internal handle, with a pointer to each lock file info */
56 struct zebra_lock_handle {
57 #ifndef WIN32
58 /** so we can call zebra_lock_rdwr_wunlock or zebra_lock_lock_runlock */
59 int write_flag;
60 #endif
61 struct zebra_lock_info *p;
62 };
63
64 struct zebra_lock_info {
65 /** file descriptor */
66 int fd;
67 /** full path (xmalloc'ed) */
68 char *fname;
69 /** reference counter: number of zebra_lock_handles pointing to us */
70 int ref_count;
71 #ifndef WIN32
72 /** number of file write locks/read locks */
73 int no_file_write_lock;
74 int no_file_read_lock;
75 Zebra_lock_rdwr rdwr_lock;
76 Zebra_mutex file_mutex;
77 #endif
78 /** next in lock list */
79 struct zebra_lock_info *next;
80 };
81
82 static int log_level = 0;
83
zebra_mk_fname(const char * dir,const char * name)84 char *zebra_mk_fname(const char *dir, const char *name)
85 {
86 int dlen = dir ? strlen(dir) : 0;
87 char *fname = xmalloc(dlen + strlen(name) + 3);
88
89 #ifdef WIN32
90 if (dlen)
91 {
92 int last_one = dir[dlen-1];
93
94 if (!strchr("/\\:", last_one))
95 sprintf(fname, "%s\\%s", dir, name);
96 else
97 sprintf(fname, "%s%s", dir, name);
98 }
99 else
100 sprintf(fname, "%s", name);
101 #else
102 if (dlen)
103 {
104 int last_one = dir[dlen-1];
105
106 if (!strchr("/", last_one))
107 sprintf(fname, "%s/%s", dir, name);
108 else
109 sprintf(fname, "%s%s", dir, name);
110 }
111 else
112 sprintf(fname, "%s", name);
113 #endif
114 return fname;
115 }
116
zebra_lock_create(const char * dir,const char * name)117 ZebraLockHandle zebra_lock_create(const char *dir, const char *name)
118 {
119 char *fname = zebra_mk_fname(dir, name);
120 struct zebra_lock_info *p = 0;
121 ZebraLockHandle h = 0;
122
123 assert(initialized);
124
125 zebra_mutex_lock(&lock_list_mutex);
126 /* see if we have the same filename in a global list of "lock files" */
127 #ifndef WIN32
128 if (posix_locks)
129 {
130 for (p = lock_list; p ; p = p->next)
131 if (!strcmp(p->fname, fname))
132 break;
133 }
134 #endif
135 if (!p)
136 { /* didn't match (or we didn't want it to match! */
137 p = (struct zebra_lock_info *) xmalloc(sizeof(*p));
138
139 p->ref_count = 0;
140 #ifdef WIN32
141 p->fd = open(name, O_BINARY|O_RDONLY);
142 if (p->fd == -1)
143 p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
144 #else
145 p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
146 #endif
147 if (p->fd == -1)
148 {
149 xfree(p);
150 yaz_log(YLOG_WARN | YLOG_ERRNO,
151 "zebra_lock_create fail fname=%s", fname);
152 p = 0;
153 }
154 else
155 {
156 p->fname = fname;
157 fname = 0; /* fname buffer now owned by p->fname */
158 #ifndef WIN32
159 if (posix_locks)
160 zebra_lock_rdwr_init(&p->rdwr_lock);
161
162 zebra_mutex_init(&p->file_mutex);
163 p->no_file_write_lock = 0;
164 p->no_file_read_lock = 0;
165 #endif
166 p->next = lock_list;
167 lock_list = p;
168 }
169 }
170 if (p)
171 {
172 /* we have lock info so we can make a handle pointing to that */
173 p->ref_count++;
174 h = (ZebraLockHandle) xmalloc(sizeof(*h));
175 h->p = p;
176 #ifndef WIN32
177 h->write_flag = 0;
178 #endif
179 yaz_log(log_level, "zebra_lock_create fd=%d p=%p fname=%s",
180 h->p->fd, h, p->fname);
181 }
182 zebra_mutex_unlock(&lock_list_mutex);
183 xfree(fname); /* free it - if it's still there */
184
185 return h;
186 }
187
zebra_lock_destroy(ZebraLockHandle h)188 void zebra_lock_destroy(ZebraLockHandle h)
189 {
190 if (!h)
191 return;
192 yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s",
193 h->p->fd, h, h->p->fname);
194 zebra_mutex_lock(&lock_list_mutex);
195 yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s refcount=%d",
196 h->p->fd, h, h->p->fname, h->p->ref_count);
197 assert(h->p->ref_count > 0);
198 --(h->p->ref_count);
199 if (h->p->ref_count == 0)
200 {
201 /* must remove shared info from lock_list */
202 struct zebra_lock_info **hp = &lock_list;
203 while (*hp)
204 {
205 if (*hp == h->p)
206 {
207 *hp = h->p->next;
208 break;
209 }
210 else
211 hp = &(*hp)->next;
212 }
213
214 yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s remove",
215 h->p->fd, h, h->p->fname);
216
217 #ifndef WIN32
218 if (posix_locks)
219 zebra_lock_rdwr_destroy(&h->p->rdwr_lock);
220 zebra_mutex_destroy(&h->p->file_mutex);
221 #endif
222 if (h->p->fd != -1)
223 close(h->p->fd);
224 xfree(h->p->fname);
225 xfree(h->p);
226 }
227 xfree(h);
228 zebra_mutex_unlock(&lock_list_mutex);
229 }
230
231 #ifndef WIN32
unixLock(int fd,int type,int cmd)232 static int unixLock(int fd, int type, int cmd)
233 {
234 struct flock area;
235 int r;
236 area.l_type = type;
237 area.l_whence = SEEK_SET;
238 area.l_len = area.l_start = 0L;
239
240 yaz_log(log_level, "fcntl begin type=%d fd=%d", type, fd);
241 r = fcntl(fd, cmd, &area);
242 if (r == -1)
243 yaz_log(YLOG_WARN|YLOG_ERRNO, "fcntl FAIL type=%d fd=%d", type, fd);
244 else
245 yaz_log(log_level, "fcntl type=%d OK fd=%d", type, fd);
246
247 return r;
248 }
249 #endif
250
zebra_lock_w(ZebraLockHandle h)251 int zebra_lock_w(ZebraLockHandle h)
252 {
253 int r = 0;
254 int do_lock = 0;
255 yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s begin",
256 h->p->fd, h, h->p->fname);
257
258 #ifdef WIN32
259 while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
260 ;
261 #else
262 if (posix_locks)
263 zebra_lock_rdwr_wlock(&h->p->rdwr_lock);
264
265 zebra_mutex_lock(&h->p->file_mutex);
266 if (h->p->no_file_write_lock == 0)
267 do_lock = 1;
268 h->p->no_file_write_lock++;
269 if (do_lock)
270 {
271 /* if there is already a read lock.. upgrade to write lock */
272 r = unixLock(h->p->fd, F_WRLCK, F_SETLKW);
273 }
274 else
275 {
276 assert(posix_locks);
277 }
278 zebra_mutex_unlock(&h->p->file_mutex);
279
280 h->write_flag = 1;
281 #endif
282 yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s end",
283 h->p->fd, h, h->p->fname);
284
285 return r;
286 }
287
zebra_lock_r(ZebraLockHandle h)288 int zebra_lock_r(ZebraLockHandle h)
289 {
290 int r = 0;
291 int do_lock = 0;
292
293 yaz_log(log_level, "zebra_lock_r fd=%d p=%p fname=%s begin",
294 h->p->fd, h, h->p->fname);
295 #ifdef WIN32
296 while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
297 ;
298 #else
299 if (posix_locks)
300 zebra_lock_rdwr_rlock(&h->p->rdwr_lock);
301
302 zebra_mutex_lock(&h->p->file_mutex);
303 if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
304 do_lock = 1;
305 h->p->no_file_read_lock++;
306 if (do_lock)
307 {
308 /* only read lock if no write locks already */
309 r = unixLock(h->p->fd, F_RDLCK, F_SETLKW);
310 }
311 else
312 {
313 assert(posix_locks);
314 }
315 zebra_mutex_unlock(&h->p->file_mutex);
316
317 h->write_flag = 0;
318 #endif
319 yaz_log(log_level, "zebra_lock_r fd=%d p=%p fname=%s end",
320 h->p->fd, h, h->p->fname);
321 return r;
322 }
323
zebra_unlock(ZebraLockHandle h)324 int zebra_unlock(ZebraLockHandle h)
325 {
326 int r = 0;
327 yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s begin",
328 h->p->fd, h, h->p->fname);
329 #ifdef WIN32
330 r = _locking(h->p->fd, _LK_UNLCK, 1);
331 #else
332 zebra_mutex_lock(&h->p->file_mutex);
333 if (h->write_flag)
334 {
335 if (h->p->no_file_write_lock > 0)
336 h->p->no_file_write_lock--;
337 }
338 else
339 {
340 if (h->p->no_file_read_lock > 0)
341 h->p->no_file_read_lock--;
342 }
343 if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
344 r = unixLock(h->p->fd, F_UNLCK, F_SETLKW);
345 else
346 {
347 r = 0;
348 assert(posix_locks);
349 }
350
351 zebra_mutex_unlock(&h->p->file_mutex);
352
353 if (posix_locks)
354 {
355 if (h->write_flag)
356 zebra_lock_rdwr_wunlock(&h->p->rdwr_lock);
357 else
358 zebra_lock_rdwr_runlock(&h->p->rdwr_lock);
359 }
360 #endif
361 yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s end",
362 h->p->fd, h, h->p->fname);
363 return r;
364 }
365
366 /** \brief see if the fcntl locking is not POSIX
367 *
368 * The default posix_locks=1 is assumed.. This function sets posix_locks
369 * to zero if linuxthreads is in use.
370 */
check_for_linuxthreads(void)371 static int check_for_linuxthreads(void)
372 {
373 #if __linux
374 #ifdef _CS_GNU_LIBPTHREAD_VERSION
375 char conf_buf[512];
376 size_t r = confstr(_CS_GNU_LIBPTHREAD_VERSION, conf_buf, sizeof(conf_buf));
377 if (r == 0)
378 {
379 yaz_log(YLOG_WARN|YLOG_ERRNO, "confstr failed");
380 return -1;
381 }
382 if (strncmp(conf_buf, "linuxthreads", 12) == 0)
383 posix_locks = 0; /* Using linuxthreads.. */
384 #else
385 posix_locks = 0; /* Old GLIBC on Linux. Assume linuxthreads */
386 #endif
387 #endif
388 return 0;
389 }
390
zebra_flock_init()391 void zebra_flock_init()
392 {
393 if (!initialized)
394 {
395 initialized = 1;
396 log_level = yaz_log_module_level("flock");
397 yaz_log(log_level, "zebra_flock_init");
398 check_for_linuxthreads();
399 zebra_mutex_init(&lock_list_mutex);
400 yaz_log(log_level, "posix_locks: %d", posix_locks);
401 }
402 }
403
404 /*
405 * Local variables:
406 * c-basic-offset: 4
407 * c-file-style: "Stroustrup"
408 * indent-tabs-mode: nil
409 * End:
410 * vim: shiftwidth=4 tabstop=8 expandtab
411 */
412
413