1 /* alock.c - access lock library */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2005 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.
25 #include <ac/stdlib.h>
26 #include <ac/string.h>
27 #include <ac/unistd.h>
29 #include <ac/assert.h>
30 #include <sys/types.h>
38 #include <sys/locking.h>
43 alock_grab_lock ( int fd, int slot )
47 #if defined( HAVE_LOCKF )
48 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
49 if (res == -1) return -1;
50 res = lockf (fd, F_LOCK, (off_t) ALOCK_SLOT_SIZE);
51 #elif defined( HAVE_FCNTL )
52 struct flock lock_info;
53 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
55 lock_info.l_type = F_WRLCK;
56 lock_info.l_whence = SEEK_SET;
57 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
58 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
60 res = fcntl (fd, F_SETLKW, &lock_info);
61 #elif defined( _WIN32 )
62 if( _lseek( fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET ) < 0 )
65 * _lock will try for the lock once per second, returning EDEADLOCK
66 * after ten tries. We just loop until we either get the lock
67 * or some other error is returned.
69 while((res = _locking( fd, _LK_LOCK, ALOCK_SLOT_SIZE )) < 0 ) {
70 if( errno != EDEADLOCK )
74 # error alock needs lockf, fcntl, or _locking
77 assert (errno != EDEADLK);
84 alock_release_lock ( int fd, int slot )
88 #if defined( HAVE_LOCKF )
89 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
90 if (res == -1) return -1;
91 res = lockf (fd, F_ULOCK, (off_t) ALOCK_SLOT_SIZE);
92 if (res == -1) return -1;
93 #elif defined ( HAVE_FCNTL )
94 struct flock lock_info;
95 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
97 lock_info.l_type = F_UNLCK;
98 lock_info.l_whence = SEEK_SET;
99 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
100 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
102 res = fcntl (fd, F_SETLKW, &lock_info);
103 if (res == -1) return -1;
104 #elif defined( _WIN32 )
105 res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
106 if (res == -1) return -1;
107 res = _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
108 if (res == -1) return -1;
110 # error alock needs lockf, fcntl, or _locking
117 alock_test_lock ( int fd, int slot )
121 #if defined( HAVE_LOCKF )
122 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
123 if (res == -1) return -1;
125 res = lockf (fd, F_TEST, (off_t) ALOCK_SLOT_SIZE);
127 if (errno == EACCES) {
133 #elif defined( HAVE_FCNTL )
134 struct flock lock_info;
135 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
137 lock_info.l_type = F_WRLCK;
138 lock_info.l_whence = SEEK_SET;
139 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
140 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
142 res = fcntl (fd, F_GETLK, &lock_info);
143 if (res == -1) return -1;
145 if (lock_info.l_type != F_UNLCK) return ALOCK_LOCKED;
146 #elif defined( _WIN32 )
147 res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
148 if (res == -1) return -1;
149 res = _locking( fd, _LK_NBLCK, ALOCK_SLOT_SIZE );
150 _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
152 if( errno == EACCES ) {
159 # error alock needs lockf, fcntl, or _locking
165 /* Read a 64bit LE value */
166 static unsigned long int
167 alock_read_iattr ( unsigned char * bufptr )
169 unsigned long int val = 0;
172 assert (bufptr != NULL);
174 bufptr += sizeof (unsigned long int);
175 for (count=0; count <= sizeof (unsigned long int); ++count) {
177 val += (unsigned long int) *bufptr--;
183 /* Write a 64bit LE value */
185 alock_write_iattr ( unsigned char * bufptr,
186 unsigned long int val )
190 assert (bufptr != NULL);
192 for (count=0; count < 8; ++count) {
193 *bufptr++ = (unsigned char) (val & 0xff);
199 alock_read_slot ( alock_info_t * info,
200 alock_slot_t * slot_data )
202 unsigned char slotbuf [ALOCK_SLOT_SIZE];
203 int res, size, size_total, err;
205 assert (info != NULL);
206 assert (slot_data != NULL);
207 assert (info->al_slot > 0);
209 res = lseek (info->al_fd,
210 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
212 if (res == -1) return -1;
215 while (size_total < ALOCK_SLOT_SIZE) {
216 size = read (info->al_fd,
217 slotbuf + size_total,
218 ALOCK_SLOT_SIZE - size_total);
219 if (size == 0) return -1;
222 if (err != EINTR && err != EAGAIN) return -1;
228 if (alock_read_iattr (slotbuf) != ALOCK_MAGIC) {
231 slot_data->al_lock = alock_read_iattr (slotbuf+8);
232 slot_data->al_stamp = alock_read_iattr (slotbuf+16);
233 slot_data->al_pid = alock_read_iattr (slotbuf+24);
235 if (slot_data->al_appname) free (slot_data->al_appname);
236 slot_data->al_appname = calloc (1, ALOCK_MAX_APPNAME);
237 strncpy (slot_data->al_appname, slotbuf+32, ALOCK_MAX_APPNAME-1);
238 (slot_data->al_appname) [ALOCK_MAX_APPNAME-1] = '\0';
244 alock_write_slot ( alock_info_t * info,
245 alock_slot_t * slot_data )
247 unsigned char slotbuf [ALOCK_SLOT_SIZE];
248 int res, size, size_total, err;
250 assert (info != NULL);
251 assert (slot_data != NULL);
252 assert (info->al_slot > 0);
254 (void) memset ((void *) slotbuf, 0, ALOCK_SLOT_SIZE);
256 alock_write_iattr (slotbuf, ALOCK_MAGIC);
257 assert (alock_read_iattr (slotbuf) == ALOCK_MAGIC);
258 alock_write_iattr (slotbuf+8, slot_data->al_lock);
259 alock_write_iattr (slotbuf+16, slot_data->al_stamp);
260 alock_write_iattr (slotbuf+24, slot_data->al_pid);
262 strncpy (slotbuf+32, slot_data->al_appname, ALOCK_MAX_APPNAME-1);
263 slotbuf[ALOCK_SLOT_SIZE-1] = '\0';
265 res = lseek (info->al_fd,
266 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
268 if (res == -1) return -1;
271 while (size_total < ALOCK_SLOT_SIZE) {
272 size = write (info->al_fd,
273 slotbuf + size_total,
274 ALOCK_SLOT_SIZE - size_total);
275 if (size == 0) return -1;
278 if (err != EINTR && err != EAGAIN) return -1;
288 alock_query_slot ( alock_info_t * info )
291 alock_slot_t slot_data;
293 assert (info != NULL);
294 assert (info->al_slot > 0);
296 (void) memset ((void *) &slot_data, 0, sizeof (alock_slot_t));
297 alock_read_slot (info, &slot_data);
298 if (slot_data.al_lock == ALOCK_UNLOCKED) return ALOCK_UNLOCKED;
300 if (slot_data.al_appname != NULL) free (slot_data.al_appname);
301 slot_data.al_appname = NULL;
303 res = alock_test_lock (info->al_fd, info->al_slot);
304 if (res < 0) return -1;
306 if (slot_data.al_lock == ALOCK_UNIQUE) {
317 alock_open ( alock_info_t * info,
318 const char * appname,
323 alock_info_t scan_info;
324 alock_slot_t slot_data;
327 int dirty_count, live_count;
329 assert (info != NULL);
330 assert (appname != NULL);
331 assert (envdir != NULL);
332 assert (locktype >= 1 && locktype <= 2);
334 slot_data.al_lock = locktype;
335 slot_data.al_stamp = time(NULL);
336 slot_data.al_pid = getpid();
337 slot_data.al_appname = calloc (1, ALOCK_MAX_APPNAME);
338 strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
339 slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
341 filename = calloc (1, strlen (envdir) + strlen ("/alock") + 1);
342 strcpy (filename, envdir);
343 strcat (filename, "/alock");
344 info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
346 if (info->al_fd < 0) {
347 free (slot_data.al_appname);
348 return ALOCK_UNSTABLE;
352 res = alock_grab_lock (info->al_fd, 0);
355 free (slot_data.al_appname);
356 return ALOCK_UNSTABLE;
359 res = fstat (info->al_fd, &statbuf);
362 free (slot_data.al_appname);
363 return ALOCK_UNSTABLE;
366 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
369 scan_info.al_fd = info->al_fd;
370 for (scan_info.al_slot = 1;
371 scan_info.al_slot < max_slot;
372 ++ scan_info.al_slot) {
373 if (scan_info.al_slot != info->al_slot) {
374 res = alock_query_slot (&scan_info);
376 if (res == ALOCK_UNLOCKED
377 && info->al_slot == 0) {
378 info->al_slot = scan_info.al_slot;
380 } else if (res == ALOCK_LOCKED) {
383 } else if (res == ALOCK_UNIQUE
384 && locktype == ALOCK_UNIQUE) {
386 free (slot_data.al_appname);
389 } else if (res == ALOCK_DIRTY) {
392 } else if (res == -1) {
394 free (slot_data.al_appname);
395 return ALOCK_UNSTABLE;
401 if (dirty_count && live_count) {
403 free (slot_data.al_appname);
404 return ALOCK_UNSTABLE;
407 if (info->al_slot == 0) info->al_slot = max_slot + 1;
408 res = alock_grab_lock (info->al_fd,
412 free (slot_data.al_appname);
413 return ALOCK_UNSTABLE;
415 res = alock_write_slot (info, &slot_data);
416 free (slot_data.al_appname);
419 return ALOCK_UNSTABLE;
422 res = alock_release_lock (info->al_fd, 0);
425 return ALOCK_UNSTABLE;
428 if (dirty_count) return ALOCK_RECOVER;
433 alock_scan ( alock_info_t * info )
436 alock_info_t scan_info;
438 int dirty_count, live_count;
440 assert (info != NULL);
442 scan_info.al_fd = info->al_fd;
444 res = alock_grab_lock (info->al_fd, 0);
447 return ALOCK_UNSTABLE;
450 res = fstat (info->al_fd, &statbuf);
453 return ALOCK_UNSTABLE;
456 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
459 for (scan_info.al_slot = 1;
460 scan_info.al_slot < max_slot;
461 ++ scan_info.al_slot) {
462 if (scan_info.al_slot != info->al_slot) {
463 res = alock_query_slot (&scan_info);
465 if (res == ALOCK_LOCKED) {
468 } else if (res == ALOCK_DIRTY) {
471 } else if (res == -1) {
473 return ALOCK_UNSTABLE;
479 res = alock_release_lock (info->al_fd, 0);
482 return ALOCK_UNSTABLE;
488 return ALOCK_UNSTABLE;
490 return ALOCK_RECOVER;
498 alock_close ( alock_info_t * info )
500 alock_slot_t slot_data;
503 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
505 res = alock_grab_lock (info->al_fd, 0);
508 return ALOCK_UNSTABLE;
511 /* mark our slot as clean */
512 res = alock_read_slot (info, &slot_data);
515 if (slot_data.al_appname != NULL)
516 free (slot_data.al_appname);
517 return ALOCK_UNSTABLE;
519 slot_data.al_lock = ALOCK_UNLOCKED;
520 res = alock_write_slot (info, &slot_data);
523 if (slot_data.al_appname != NULL)
524 free (slot_data.al_appname);
525 return ALOCK_UNSTABLE;
527 if (slot_data.al_appname != NULL) {
528 free (slot_data.al_appname);
529 slot_data.al_appname = NULL;
532 res = alock_release_lock (info->al_fd, info->al_slot);
535 return ALOCK_UNSTABLE;
537 res = alock_release_lock (info->al_fd, 0);
540 return ALOCK_UNSTABLE;
543 res = close (info->al_fd);
544 if (res == -1) return ALOCK_UNSTABLE;
550 alock_recover ( alock_info_t * info )
553 alock_slot_t slot_data;
554 alock_info_t scan_info;
557 assert (info != NULL);
559 scan_info.al_fd = info->al_fd;
561 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
563 res = alock_grab_lock (info->al_fd, 0);
566 return ALOCK_UNSTABLE;
569 res = fstat (info->al_fd, &statbuf);
572 return ALOCK_UNSTABLE;
575 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
576 for (scan_info.al_slot = 1;
577 scan_info.al_slot < max_slot;
578 ++ scan_info.al_slot) {
579 if (scan_info.al_slot != info->al_slot) {
580 res = alock_query_slot (&scan_info);
582 if (res == ALOCK_LOCKED
583 || res == ALOCK_UNIQUE) {
584 /* recovery attempt on an active db? */
586 return ALOCK_UNSTABLE;
588 } else if (res == ALOCK_DIRTY) {
590 res = alock_read_slot (&scan_info, &slot_data);
593 return ALOCK_UNSTABLE;
595 slot_data.al_lock = ALOCK_UNLOCKED;
596 res = alock_write_slot (&scan_info, &slot_data);
599 if (slot_data.al_appname != NULL)
600 free (slot_data.al_appname);
601 return ALOCK_UNSTABLE;
603 if (slot_data.al_appname != NULL) {
604 free (slot_data.al_appname);
605 slot_data.al_appname = NULL;
608 } else if (res == -1) {
610 return ALOCK_UNSTABLE;
616 res = alock_release_lock (info->al_fd, 0);
619 return ALOCK_UNSTABLE;