1 /* alock.c - access lock library */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2005-2006 The OpenLDAP Foundation.
6 * Portions Copyright 2004-2005 Symas Corporation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
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>.
18 * This work was initially developed by Matthew Backes at Symas
19 * Corporation for inclusion in OpenLDAP Software.
24 #if SLAPD_BDB || SLAPD_HDB
28 #include <ac/stdlib.h>
29 #include <ac/string.h>
30 #include <ac/unistd.h>
32 #include <ac/assert.h>
33 #include <sys/types.h>
41 #include <sys/locking.h>
46 alock_grab_lock ( int fd, int slot )
50 #if defined( HAVE_LOCKF )
51 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
52 if (res == -1) return -1;
53 res = lockf (fd, F_LOCK, (off_t) ALOCK_SLOT_SIZE);
54 #elif defined( HAVE_FCNTL )
55 struct flock lock_info;
56 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
58 lock_info.l_type = F_WRLCK;
59 lock_info.l_whence = SEEK_SET;
60 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
61 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
63 res = fcntl (fd, F_SETLKW, &lock_info);
64 #elif defined( _WIN32 )
65 if( _lseek( fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET ) < 0 )
68 * _lock will try for the lock once per second, returning EDEADLOCK
69 * after ten tries. We just loop until we either get the lock
70 * or some other error is returned.
72 while((res = _locking( fd, _LK_LOCK, ALOCK_SLOT_SIZE )) < 0 ) {
73 if( errno != EDEADLOCK )
77 # error alock needs lockf, fcntl, or _locking
80 assert (errno != EDEADLK);
87 alock_release_lock ( int fd, int slot )
91 #if defined( HAVE_LOCKF )
92 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
93 if (res == -1) return -1;
94 res = lockf (fd, F_ULOCK, (off_t) ALOCK_SLOT_SIZE);
95 if (res == -1) return -1;
96 #elif defined ( HAVE_FCNTL )
97 struct flock lock_info;
98 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
100 lock_info.l_type = F_UNLCK;
101 lock_info.l_whence = SEEK_SET;
102 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
103 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
105 res = fcntl (fd, F_SETLKW, &lock_info);
106 if (res == -1) return -1;
107 #elif defined( _WIN32 )
108 res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
109 if (res == -1) return -1;
110 res = _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
111 if (res == -1) return -1;
113 # error alock needs lockf, fcntl, or _locking
120 alock_test_lock ( int fd, int slot )
124 #if defined( HAVE_LOCKF )
125 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
126 if (res == -1) return -1;
128 res = lockf (fd, F_TEST, (off_t) ALOCK_SLOT_SIZE);
130 if (errno == EACCES || errno == EAGAIN) {
136 #elif defined( HAVE_FCNTL )
137 struct flock lock_info;
138 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
140 lock_info.l_type = F_WRLCK;
141 lock_info.l_whence = SEEK_SET;
142 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
143 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
145 res = fcntl (fd, F_GETLK, &lock_info);
146 if (res == -1) return -1;
148 if (lock_info.l_type != F_UNLCK) return ALOCK_LOCKED;
149 #elif defined( _WIN32 )
150 res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
151 if (res == -1) return -1;
152 res = _locking( fd, _LK_NBLCK, ALOCK_SLOT_SIZE );
153 _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
155 if( errno == EACCES ) {
162 # error alock needs lockf, fcntl, or _locking
168 /* Read a 64bit LE value */
169 static unsigned long int
170 alock_read_iattr ( unsigned char * bufptr )
172 unsigned long int val = 0;
175 assert (bufptr != NULL);
177 bufptr += sizeof (unsigned long int);
178 for (count=0; count <= sizeof (unsigned long int); ++count) {
180 val += (unsigned long int) *bufptr--;
186 /* Write a 64bit LE value */
188 alock_write_iattr ( unsigned char * bufptr,
189 unsigned long int val )
193 assert (bufptr != NULL);
195 for (count=0; count < 8; ++count) {
196 *bufptr++ = (unsigned char) (val & 0xff);
202 alock_read_slot ( alock_info_t * info,
203 alock_slot_t * slot_data )
205 unsigned char slotbuf [ALOCK_SLOT_SIZE];
206 int res, size, size_total, err;
208 assert (info != NULL);
209 assert (slot_data != NULL);
210 assert (info->al_slot > 0);
212 res = lseek (info->al_fd,
213 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
215 if (res == -1) return -1;
218 while (size_total < ALOCK_SLOT_SIZE) {
219 size = read (info->al_fd,
220 slotbuf + size_total,
221 ALOCK_SLOT_SIZE - size_total);
222 if (size == 0) return -1;
225 if (err != EINTR && err != EAGAIN) return -1;
231 if (alock_read_iattr (slotbuf) != ALOCK_MAGIC) {
234 slot_data->al_lock = alock_read_iattr (slotbuf+8);
235 slot_data->al_stamp = alock_read_iattr (slotbuf+16);
236 slot_data->al_pid = alock_read_iattr (slotbuf+24);
238 if (slot_data->al_appname) free (slot_data->al_appname);
239 slot_data->al_appname = calloc (1, ALOCK_MAX_APPNAME);
240 strncpy (slot_data->al_appname, (char *)slotbuf+32, ALOCK_MAX_APPNAME-1);
241 (slot_data->al_appname) [ALOCK_MAX_APPNAME-1] = '\0';
247 alock_write_slot ( alock_info_t * info,
248 alock_slot_t * slot_data )
250 unsigned char slotbuf [ALOCK_SLOT_SIZE];
251 int res, size, size_total, err;
253 assert (info != NULL);
254 assert (slot_data != NULL);
255 assert (info->al_slot > 0);
257 (void) memset ((void *) slotbuf, 0, ALOCK_SLOT_SIZE);
259 alock_write_iattr (slotbuf, ALOCK_MAGIC);
260 assert (alock_read_iattr (slotbuf) == ALOCK_MAGIC);
261 alock_write_iattr (slotbuf+8, slot_data->al_lock);
262 alock_write_iattr (slotbuf+16, slot_data->al_stamp);
263 alock_write_iattr (slotbuf+24, slot_data->al_pid);
265 if (slot_data->al_appname)
266 strncpy ((char *)slotbuf+32, slot_data->al_appname, ALOCK_MAX_APPNAME-1);
267 slotbuf[ALOCK_SLOT_SIZE-1] = '\0';
269 res = lseek (info->al_fd,
270 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
272 if (res == -1) return -1;
275 while (size_total < ALOCK_SLOT_SIZE) {
276 size = write (info->al_fd,
277 slotbuf + size_total,
278 ALOCK_SLOT_SIZE - size_total);
279 if (size == 0) return -1;
282 if (err != EINTR && err != EAGAIN) return -1;
292 alock_query_slot ( alock_info_t * info )
295 alock_slot_t slot_data;
297 assert (info != NULL);
298 assert (info->al_slot > 0);
300 (void) memset ((void *) &slot_data, 0, sizeof (alock_slot_t));
301 alock_read_slot (info, &slot_data);
303 if (slot_data.al_appname != NULL) free (slot_data.al_appname);
304 slot_data.al_appname = NULL;
306 nosave = slot_data.al_lock & ALOCK_NOSAVE;
308 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNLOCKED)
309 return slot_data.al_lock;
311 res = alock_test_lock (info->al_fd, info->al_slot);
312 if (res < 0) return -1;
314 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNIQUE) {
315 return slot_data.al_lock;
317 return ALOCK_LOCKED | nosave;
321 return ALOCK_DIRTY | nosave;
325 alock_open ( alock_info_t * info,
326 const char * appname,
331 alock_info_t scan_info;
332 alock_slot_t slot_data;
335 int dirty_count, live_count, nosave;
337 assert (info != NULL);
338 assert (appname != NULL);
339 assert (envdir != NULL);
340 assert ((locktype & ALOCK_SMASK) >= 1 && (locktype & ALOCK_SMASK) <= 2);
342 slot_data.al_lock = locktype;
343 slot_data.al_stamp = time(NULL);
344 slot_data.al_pid = getpid();
345 slot_data.al_appname = calloc (1, ALOCK_MAX_APPNAME);
346 strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
347 slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
349 filename = calloc (1, strlen (envdir) + strlen ("/alock") + 1);
350 strcpy (filename, envdir);
351 strcat (filename, "/alock");
352 info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
354 if (info->al_fd < 0) {
355 free (slot_data.al_appname);
356 return ALOCK_UNSTABLE;
360 res = alock_grab_lock (info->al_fd, 0);
363 free (slot_data.al_appname);
364 return ALOCK_UNSTABLE;
367 res = fstat (info->al_fd, &statbuf);
370 free (slot_data.al_appname);
371 return ALOCK_UNSTABLE;
374 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
378 scan_info.al_fd = info->al_fd;
379 for (scan_info.al_slot = 1;
380 scan_info.al_slot < max_slot;
381 ++ scan_info.al_slot) {
382 if (scan_info.al_slot != info->al_slot) {
383 res = alock_query_slot (&scan_info);
385 if (res & ALOCK_NOSAVE) {
386 nosave = ALOCK_NOSAVE;
389 if (res == ALOCK_UNLOCKED
390 && info->al_slot == 0) {
391 info->al_slot = scan_info.al_slot;
393 } else if (res == ALOCK_LOCKED) {
396 } else if (res == ALOCK_UNIQUE
397 && locktype == ALOCK_UNIQUE) {
399 free (slot_data.al_appname);
402 } else if (res == ALOCK_DIRTY) {
405 } else if (res == -1) {
407 free (slot_data.al_appname);
408 return ALOCK_UNSTABLE;
414 if (dirty_count && live_count) {
416 free (slot_data.al_appname);
417 return ALOCK_UNSTABLE;
420 if (info->al_slot == 0) info->al_slot = max_slot + 1;
421 res = alock_grab_lock (info->al_fd,
425 free (slot_data.al_appname);
426 return ALOCK_UNSTABLE;
428 res = alock_write_slot (info, &slot_data);
429 free (slot_data.al_appname);
432 return ALOCK_UNSTABLE;
435 res = alock_release_lock (info->al_fd, 0);
438 return ALOCK_UNSTABLE;
441 if (dirty_count) return ALOCK_RECOVER | nosave;
442 return ALOCK_CLEAN | nosave;
446 alock_scan ( alock_info_t * info )
449 alock_info_t scan_info;
451 int dirty_count, live_count, nosave;
453 assert (info != NULL);
455 scan_info.al_fd = info->al_fd;
457 res = alock_grab_lock (info->al_fd, 0);
460 return ALOCK_UNSTABLE;
463 res = fstat (info->al_fd, &statbuf);
466 return ALOCK_UNSTABLE;
469 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
473 for (scan_info.al_slot = 1;
474 scan_info.al_slot < max_slot;
475 ++ scan_info.al_slot) {
476 if (scan_info.al_slot != info->al_slot) {
477 res = alock_query_slot (&scan_info);
479 if (res & ALOCK_NOSAVE) {
480 nosave = ALOCK_NOSAVE;
484 if (res == ALOCK_LOCKED) {
487 } else if (res == ALOCK_DIRTY) {
490 } else if (res == -1) {
492 return ALOCK_UNSTABLE;
498 res = alock_release_lock (info->al_fd, 0);
501 return ALOCK_UNSTABLE;
507 return ALOCK_UNSTABLE;
509 return ALOCK_RECOVER | nosave;
513 return ALOCK_CLEAN | nosave;
517 alock_close ( alock_info_t * info )
519 alock_slot_t slot_data;
522 if ( !info->al_slot )
525 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
527 res = alock_grab_lock (info->al_fd, 0);
530 return ALOCK_UNSTABLE;
533 /* mark our slot as clean */
534 res = alock_read_slot (info, &slot_data);
537 if (slot_data.al_appname != NULL)
538 free (slot_data.al_appname);
539 return ALOCK_UNSTABLE;
541 slot_data.al_lock = ALOCK_UNLOCKED | (slot_data.al_lock & ALOCK_NOSAVE);
542 res = alock_write_slot (info, &slot_data);
545 if (slot_data.al_appname != NULL)
546 free (slot_data.al_appname);
547 return ALOCK_UNSTABLE;
549 if (slot_data.al_appname != NULL) {
550 free (slot_data.al_appname);
551 slot_data.al_appname = NULL;
554 res = alock_release_lock (info->al_fd, info->al_slot);
557 return ALOCK_UNSTABLE;
559 res = alock_release_lock (info->al_fd, 0);
562 return ALOCK_UNSTABLE;
565 res = close (info->al_fd);
566 if (res == -1) return ALOCK_UNSTABLE;
572 alock_recover ( alock_info_t * info )
575 alock_slot_t slot_data;
576 alock_info_t scan_info;
579 assert (info != NULL);
581 scan_info.al_fd = info->al_fd;
583 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
585 res = alock_grab_lock (info->al_fd, 0);
588 return ALOCK_UNSTABLE;
591 res = fstat (info->al_fd, &statbuf);
594 return ALOCK_UNSTABLE;
597 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
598 for (scan_info.al_slot = 1;
599 scan_info.al_slot < max_slot;
600 ++ scan_info.al_slot) {
601 if (scan_info.al_slot != info->al_slot) {
602 res = alock_query_slot (&scan_info) & ~ALOCK_NOSAVE;
604 if (res == ALOCK_LOCKED
605 || res == ALOCK_UNIQUE) {
606 /* recovery attempt on an active db? */
608 return ALOCK_UNSTABLE;
610 } else if (res == ALOCK_DIRTY) {
612 res = alock_read_slot (&scan_info, &slot_data);
615 return ALOCK_UNSTABLE;
617 slot_data.al_lock = ALOCK_UNLOCKED;
618 res = alock_write_slot (&scan_info, &slot_data);
621 if (slot_data.al_appname != NULL)
622 free (slot_data.al_appname);
623 return ALOCK_UNSTABLE;
625 if (slot_data.al_appname != NULL) {
626 free (slot_data.al_appname);
627 slot_data.al_appname = NULL;
630 } else if (res == -1) {
632 return ALOCK_UNSTABLE;
638 res = alock_release_lock (info->al_fd, 0);
641 return ALOCK_UNSTABLE;
647 #endif /* SLAPD_BDB || SLAPD_HDB */