Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / openldap / dist / servers / slapd / alock.c
blobd72953177c874ed4c6d0f747ba47b65ea4fd1250
1 /* alock.c - access lock library */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/alock.c,v 1.5.2.7 2008/02/11 23:26:43 kurt Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2005-2008 The OpenLDAP Foundation.
6 * Portions Copyright 2004-2005 Symas Corporation.
7 * All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
17 /* ACKNOWLEDGEMENTS:
18 * This work was initially developed by Matthew Backes at Symas
19 * Corporation for inclusion in OpenLDAP Software.
22 #include "portable.h"
24 #if SLAPD_BDB || SLAPD_HDB
26 #include "alock.h"
28 #include <ac/stdlib.h>
29 #include <ac/string.h>
30 #include <ac/unistd.h>
31 #include <ac/errno.h>
32 #include <ac/assert.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #ifdef HAVE_SYS_FILE_H
36 #include <sys/file.h>
37 #endif
38 #include <fcntl.h>
40 #ifdef _WIN32
41 #include <stdio.h>
42 #include <io.h>
43 #include <sys/locking.h>
44 #endif
47 static int
48 alock_grab_lock ( int fd, int slot )
50 int res;
52 #if defined( HAVE_LOCKF )
53 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
54 if (res == -1) return -1;
55 res = lockf (fd, F_LOCK, (off_t) ALOCK_SLOT_SIZE);
56 #elif defined( HAVE_FCNTL )
57 struct flock lock_info;
58 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
60 lock_info.l_type = F_WRLCK;
61 lock_info.l_whence = SEEK_SET;
62 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
63 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
65 res = fcntl (fd, F_SETLKW, &lock_info);
66 #elif defined( _WIN32 )
67 if( _lseek( fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET ) < 0 )
68 return -1;
70 * _lock will try for the lock once per second, returning EDEADLOCK
71 * after ten tries. We just loop until we either get the lock
72 * or some other error is returned.
74 while((res = _locking( fd, _LK_LOCK, ALOCK_SLOT_SIZE )) < 0 ) {
75 if( errno != EDEADLOCK )
76 break;
78 #else
79 # error alock needs lockf, fcntl, or _locking
80 #endif
81 if (res == -1) {
82 assert (errno != EDEADLK);
83 return -1;
85 return 0;
88 static int
89 alock_release_lock ( int fd, int slot )
91 int res;
93 #if defined( HAVE_LOCKF )
94 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
95 if (res == -1) return -1;
96 res = lockf (fd, F_ULOCK, (off_t) ALOCK_SLOT_SIZE);
97 if (res == -1) return -1;
98 #elif defined ( HAVE_FCNTL )
99 struct flock lock_info;
100 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
102 lock_info.l_type = F_UNLCK;
103 lock_info.l_whence = SEEK_SET;
104 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
105 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
107 res = fcntl (fd, F_SETLKW, &lock_info);
108 if (res == -1) return -1;
109 #elif defined( _WIN32 )
110 res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
111 if (res == -1) return -1;
112 res = _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
113 if (res == -1) return -1;
114 #else
115 # error alock needs lockf, fcntl, or _locking
116 #endif
118 return 0;
121 static int
122 alock_test_lock ( int fd, int slot )
124 int res;
126 #if defined( HAVE_LOCKF )
127 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
128 if (res == -1) return -1;
130 res = lockf (fd, F_TEST, (off_t) ALOCK_SLOT_SIZE);
131 if (res == -1) {
132 if (errno == EACCES || errno == EAGAIN) {
133 return ALOCK_LOCKED;
134 } else {
135 return -1;
138 #elif defined( HAVE_FCNTL )
139 struct flock lock_info;
140 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
142 lock_info.l_type = F_WRLCK;
143 lock_info.l_whence = SEEK_SET;
144 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
145 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
147 res = fcntl (fd, F_GETLK, &lock_info);
148 if (res == -1) return -1;
150 if (lock_info.l_type != F_UNLCK) return ALOCK_LOCKED;
151 #elif defined( _WIN32 )
152 res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
153 if (res == -1) return -1;
154 res = _locking( fd, _LK_NBLCK, ALOCK_SLOT_SIZE );
155 _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
156 if (res == -1) {
157 if( errno == EACCES ) {
158 return ALOCK_LOCKED;
159 } else {
160 return -1;
163 #else
164 # error alock needs lockf, fcntl, or _locking
165 #endif
167 return 0;
170 /* Read a 64bit LE value */
171 static unsigned long int
172 alock_read_iattr ( unsigned char * bufptr )
174 unsigned long int val = 0;
175 int count;
177 assert (bufptr != NULL);
179 bufptr += sizeof (unsigned long int);
180 for (count=0; count <= sizeof (unsigned long int); ++count) {
181 val <<= 8;
182 val += (unsigned long int) *bufptr--;
185 return val;
188 /* Write a 64bit LE value */
189 static void
190 alock_write_iattr ( unsigned char * bufptr,
191 unsigned long int val )
193 int count;
195 assert (bufptr != NULL);
197 for (count=0; count < 8; ++count) {
198 *bufptr++ = (unsigned char) (val & 0xff);
199 val >>= 8;
203 static int
204 alock_read_slot ( alock_info_t * info,
205 alock_slot_t * slot_data )
207 unsigned char slotbuf [ALOCK_SLOT_SIZE];
208 int res, size, size_total, err;
210 assert (info != NULL);
211 assert (slot_data != NULL);
212 assert (info->al_slot > 0);
214 res = lseek (info->al_fd,
215 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
216 SEEK_SET);
217 if (res == -1) return -1;
219 size_total = 0;
220 while (size_total < ALOCK_SLOT_SIZE) {
221 size = read (info->al_fd,
222 slotbuf + size_total,
223 ALOCK_SLOT_SIZE - size_total);
224 if (size == 0) return -1;
225 if (size < 0) {
226 err = errno;
227 if (err != EINTR && err != EAGAIN) return -1;
228 } else {
229 size_total += size;
233 if (alock_read_iattr (slotbuf) != ALOCK_MAGIC) {
234 return -1;
236 slot_data->al_lock = alock_read_iattr (slotbuf+8);
237 slot_data->al_stamp = alock_read_iattr (slotbuf+16);
238 slot_data->al_pid = alock_read_iattr (slotbuf+24);
240 if (slot_data->al_appname) free (slot_data->al_appname);
241 slot_data->al_appname = calloc (1, ALOCK_MAX_APPNAME);
242 strncpy (slot_data->al_appname, (char *)slotbuf+32, ALOCK_MAX_APPNAME-1);
243 (slot_data->al_appname) [ALOCK_MAX_APPNAME-1] = '\0';
245 return 0;
248 static int
249 alock_write_slot ( alock_info_t * info,
250 alock_slot_t * slot_data )
252 unsigned char slotbuf [ALOCK_SLOT_SIZE];
253 int res, size, size_total, err;
255 assert (info != NULL);
256 assert (slot_data != NULL);
257 assert (info->al_slot > 0);
259 (void) memset ((void *) slotbuf, 0, ALOCK_SLOT_SIZE);
261 alock_write_iattr (slotbuf, ALOCK_MAGIC);
262 assert (alock_read_iattr (slotbuf) == ALOCK_MAGIC);
263 alock_write_iattr (slotbuf+8, slot_data->al_lock);
264 alock_write_iattr (slotbuf+16, slot_data->al_stamp);
265 alock_write_iattr (slotbuf+24, slot_data->al_pid);
267 if (slot_data->al_appname)
268 strncpy ((char *)slotbuf+32, slot_data->al_appname, ALOCK_MAX_APPNAME-1);
269 slotbuf[ALOCK_SLOT_SIZE-1] = '\0';
271 res = lseek (info->al_fd,
272 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
273 SEEK_SET);
274 if (res == -1) return -1;
276 size_total = 0;
277 while (size_total < ALOCK_SLOT_SIZE) {
278 size = write (info->al_fd,
279 slotbuf + size_total,
280 ALOCK_SLOT_SIZE - size_total);
281 if (size == 0) return -1;
282 if (size < 0) {
283 err = errno;
284 if (err != EINTR && err != EAGAIN) return -1;
285 } else {
286 size_total += size;
290 return 0;
293 static int
294 alock_query_slot ( alock_info_t * info )
296 int res, nosave;
297 alock_slot_t slot_data;
299 assert (info != NULL);
300 assert (info->al_slot > 0);
302 (void) memset ((void *) &slot_data, 0, sizeof (alock_slot_t));
303 alock_read_slot (info, &slot_data);
305 if (slot_data.al_appname != NULL) free (slot_data.al_appname);
306 slot_data.al_appname = NULL;
308 nosave = slot_data.al_lock & ALOCK_NOSAVE;
310 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNLOCKED)
311 return slot_data.al_lock;
313 res = alock_test_lock (info->al_fd, info->al_slot);
314 if (res < 0) return -1;
315 if (res > 0) {
316 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNIQUE) {
317 return slot_data.al_lock;
318 } else {
319 return ALOCK_LOCKED | nosave;
323 return ALOCK_DIRTY | nosave;
326 int
327 alock_open ( alock_info_t * info,
328 const char * appname,
329 const char * envdir,
330 int locktype )
332 struct stat statbuf;
333 alock_info_t scan_info;
334 alock_slot_t slot_data;
335 char * filename;
336 int res, max_slot;
337 int dirty_count, live_count, nosave;
339 assert (info != NULL);
340 assert (appname != NULL);
341 assert (envdir != NULL);
342 assert ((locktype & ALOCK_SMASK) >= 1 && (locktype & ALOCK_SMASK) <= 2);
344 slot_data.al_lock = locktype;
345 slot_data.al_stamp = time(NULL);
346 slot_data.al_pid = getpid();
347 slot_data.al_appname = calloc (1, ALOCK_MAX_APPNAME);
348 strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
349 slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
351 filename = calloc (1, strlen (envdir) + strlen ("/alock") + 1);
352 strcpy (filename, envdir);
353 strcat (filename, "/alock");
354 info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
355 free (filename);
356 if (info->al_fd < 0) {
357 free (slot_data.al_appname);
358 return ALOCK_UNSTABLE;
360 info->al_slot = 0;
362 res = alock_grab_lock (info->al_fd, 0);
363 if (res == -1) {
364 close (info->al_fd);
365 free (slot_data.al_appname);
366 return ALOCK_UNSTABLE;
369 res = fstat (info->al_fd, &statbuf);
370 if (res == -1) {
371 close (info->al_fd);
372 free (slot_data.al_appname);
373 return ALOCK_UNSTABLE;
376 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
377 dirty_count = 0;
378 live_count = 0;
379 nosave = 0;
380 scan_info.al_fd = info->al_fd;
381 for (scan_info.al_slot = 1;
382 scan_info.al_slot < max_slot;
383 ++ scan_info.al_slot) {
384 if (scan_info.al_slot != info->al_slot) {
385 res = alock_query_slot (&scan_info);
387 if (res & ALOCK_NOSAVE) {
388 nosave = ALOCK_NOSAVE;
389 res ^= ALOCK_NOSAVE;
391 if (res == ALOCK_UNLOCKED
392 && info->al_slot == 0) {
393 info->al_slot = scan_info.al_slot;
395 } else if (res == ALOCK_LOCKED) {
396 ++live_count;
398 } else if (res == ALOCK_UNIQUE
399 && locktype == ALOCK_UNIQUE) {
400 close (info->al_fd);
401 free (slot_data.al_appname);
402 return ALOCK_BUSY;
404 } else if (res == ALOCK_DIRTY) {
405 ++dirty_count;
407 } else if (res == -1) {
408 close (info->al_fd);
409 free (slot_data.al_appname);
410 return ALOCK_UNSTABLE;
416 if (dirty_count && live_count) {
417 close (info->al_fd);
418 free (slot_data.al_appname);
419 return ALOCK_UNSTABLE;
422 if (info->al_slot == 0) info->al_slot = max_slot + 1;
423 res = alock_grab_lock (info->al_fd,
424 info->al_slot);
425 if (res == -1) {
426 close (info->al_fd);
427 free (slot_data.al_appname);
428 return ALOCK_UNSTABLE;
430 res = alock_write_slot (info, &slot_data);
431 free (slot_data.al_appname);
432 if (res == -1) {
433 close (info->al_fd);
434 return ALOCK_UNSTABLE;
437 res = alock_release_lock (info->al_fd, 0);
438 if (res == -1) {
439 close (info->al_fd);
440 return ALOCK_UNSTABLE;
443 if (dirty_count) return ALOCK_RECOVER | nosave;
444 return ALOCK_CLEAN | nosave;
447 int
448 alock_scan ( alock_info_t * info )
450 struct stat statbuf;
451 alock_info_t scan_info;
452 int res, max_slot;
453 int dirty_count, live_count, nosave;
455 assert (info != NULL);
457 scan_info.al_fd = info->al_fd;
459 res = alock_grab_lock (info->al_fd, 0);
460 if (res == -1) {
461 close (info->al_fd);
462 return ALOCK_UNSTABLE;
465 res = fstat (info->al_fd, &statbuf);
466 if (res == -1) {
467 close (info->al_fd);
468 return ALOCK_UNSTABLE;
471 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
472 dirty_count = 0;
473 live_count = 0;
474 nosave = 0;
475 for (scan_info.al_slot = 1;
476 scan_info.al_slot < max_slot;
477 ++ scan_info.al_slot) {
478 if (scan_info.al_slot != info->al_slot) {
479 res = alock_query_slot (&scan_info);
481 if (res & ALOCK_NOSAVE) {
482 nosave = ALOCK_NOSAVE;
483 res ^= ALOCK_NOSAVE;
486 if (res == ALOCK_LOCKED) {
487 ++live_count;
489 } else if (res == ALOCK_DIRTY) {
490 ++dirty_count;
492 } else if (res == -1) {
493 close (info->al_fd);
494 return ALOCK_UNSTABLE;
500 res = alock_release_lock (info->al_fd, 0);
501 if (res == -1) {
502 close (info->al_fd);
503 return ALOCK_UNSTABLE;
506 if (dirty_count) {
507 if (live_count) {
508 close (info->al_fd);
509 return ALOCK_UNSTABLE;
510 } else {
511 return ALOCK_RECOVER | nosave;
515 return ALOCK_CLEAN | nosave;
519 alock_close ( alock_info_t * info, int nosave )
521 alock_slot_t slot_data;
522 int res;
524 if ( !info->al_slot )
525 return ALOCK_CLEAN;
527 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
529 res = alock_grab_lock (info->al_fd, 0);
530 if (res == -1) {
531 close (info->al_fd);
532 return ALOCK_UNSTABLE;
535 /* mark our slot as clean */
536 res = alock_read_slot (info, &slot_data);
537 if (res == -1) {
538 close (info->al_fd);
539 if (slot_data.al_appname != NULL)
540 free (slot_data.al_appname);
541 return ALOCK_UNSTABLE;
543 slot_data.al_lock = ALOCK_UNLOCKED;
544 if ( nosave )
545 slot_data.al_lock |= ALOCK_NOSAVE;
546 res = alock_write_slot (info, &slot_data);
547 if (res == -1) {
548 close (info->al_fd);
549 if (slot_data.al_appname != NULL)
550 free (slot_data.al_appname);
551 return ALOCK_UNSTABLE;
553 if (slot_data.al_appname != NULL) {
554 free (slot_data.al_appname);
555 slot_data.al_appname = NULL;
558 res = alock_release_lock (info->al_fd, info->al_slot);
559 if (res == -1) {
560 close (info->al_fd);
561 return ALOCK_UNSTABLE;
563 res = alock_release_lock (info->al_fd, 0);
564 if (res == -1) {
565 close (info->al_fd);
566 return ALOCK_UNSTABLE;
569 res = close (info->al_fd);
570 if (res == -1) return ALOCK_UNSTABLE;
572 return ALOCK_CLEAN;
575 int
576 alock_recover ( alock_info_t * info )
578 struct stat statbuf;
579 alock_slot_t slot_data;
580 alock_info_t scan_info;
581 int res, max_slot;
583 assert (info != NULL);
585 scan_info.al_fd = info->al_fd;
587 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
589 res = alock_grab_lock (info->al_fd, 0);
590 if (res == -1) {
591 close (info->al_fd);
592 return ALOCK_UNSTABLE;
595 res = fstat (info->al_fd, &statbuf);
596 if (res == -1) {
597 close (info->al_fd);
598 return ALOCK_UNSTABLE;
601 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
602 for (scan_info.al_slot = 1;
603 scan_info.al_slot < max_slot;
604 ++ scan_info.al_slot) {
605 if (scan_info.al_slot != info->al_slot) {
606 res = alock_query_slot (&scan_info) & ~ALOCK_NOSAVE;
608 if (res == ALOCK_LOCKED
609 || res == ALOCK_UNIQUE) {
610 /* recovery attempt on an active db? */
611 close (info->al_fd);
612 return ALOCK_UNSTABLE;
614 } else if (res == ALOCK_DIRTY) {
615 /* mark it clean */
616 res = alock_read_slot (&scan_info, &slot_data);
617 if (res == -1) {
618 close (info->al_fd);
619 return ALOCK_UNSTABLE;
621 slot_data.al_lock = ALOCK_UNLOCKED;
622 res = alock_write_slot (&scan_info, &slot_data);
623 if (res == -1) {
624 close (info->al_fd);
625 if (slot_data.al_appname != NULL)
626 free (slot_data.al_appname);
627 return ALOCK_UNSTABLE;
629 if (slot_data.al_appname != NULL) {
630 free (slot_data.al_appname);
631 slot_data.al_appname = NULL;
634 } else if (res == -1) {
635 close (info->al_fd);
636 return ALOCK_UNSTABLE;
642 res = alock_release_lock (info->al_fd, 0);
643 if (res == -1) {
644 close (info->al_fd);
645 return ALOCK_UNSTABLE;
648 return ALOCK_CLEAN;
651 #endif /* SLAPD_BDB || SLAPD_HDB */