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 || errno == EAGAIN) {
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, (char *)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 ((char *)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);
299 if (slot_data.al_appname != NULL) free (slot_data.al_appname);
300 slot_data.al_appname = NULL;
302 if (slot_data.al_lock == ALOCK_UNLOCKED) return ALOCK_UNLOCKED;
304 res = alock_test_lock (info->al_fd, info->al_slot);
305 if (res < 0) return -1;
307 if (slot_data.al_lock == ALOCK_UNIQUE) {
318 alock_open ( alock_info_t * info,
319 const char * appname,
324 alock_info_t scan_info;
325 alock_slot_t slot_data;
328 int dirty_count, live_count;
330 assert (info != NULL);
331 assert (appname != NULL);
332 assert (envdir != NULL);
333 assert (locktype >= 1 && locktype <= 2);
335 slot_data.al_lock = locktype;
336 slot_data.al_stamp = time(NULL);
337 slot_data.al_pid = getpid();
338 slot_data.al_appname = calloc (1, ALOCK_MAX_APPNAME);
339 strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
340 slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
342 filename = calloc (1, strlen (envdir) + strlen ("/alock") + 1);
343 strcpy (filename, envdir);
344 strcat (filename, "/alock");
345 info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
347 if (info->al_fd < 0) {
348 free (slot_data.al_appname);
349 return ALOCK_UNSTABLE;
353 res = alock_grab_lock (info->al_fd, 0);
356 free (slot_data.al_appname);
357 return ALOCK_UNSTABLE;
360 res = fstat (info->al_fd, &statbuf);
363 free (slot_data.al_appname);
364 return ALOCK_UNSTABLE;
367 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
370 scan_info.al_fd = info->al_fd;
371 for (scan_info.al_slot = 1;
372 scan_info.al_slot < max_slot;
373 ++ scan_info.al_slot) {
374 if (scan_info.al_slot != info->al_slot) {
375 res = alock_query_slot (&scan_info);
377 if (res == ALOCK_UNLOCKED
378 && info->al_slot == 0) {
379 info->al_slot = scan_info.al_slot;
381 } else if (res == ALOCK_LOCKED) {
384 } else if (res == ALOCK_UNIQUE
385 && locktype == ALOCK_UNIQUE) {
387 free (slot_data.al_appname);
390 } else if (res == ALOCK_DIRTY) {
393 } else if (res == -1) {
395 free (slot_data.al_appname);
396 return ALOCK_UNSTABLE;
402 if (dirty_count && live_count) {
404 free (slot_data.al_appname);
405 return ALOCK_UNSTABLE;
408 if (info->al_slot == 0) info->al_slot = max_slot + 1;
409 res = alock_grab_lock (info->al_fd,
413 free (slot_data.al_appname);
414 return ALOCK_UNSTABLE;
416 res = alock_write_slot (info, &slot_data);
417 free (slot_data.al_appname);
420 return ALOCK_UNSTABLE;
423 res = alock_release_lock (info->al_fd, 0);
426 return ALOCK_UNSTABLE;
429 if (dirty_count) return ALOCK_RECOVER;
434 alock_scan ( alock_info_t * info )
437 alock_info_t scan_info;
439 int dirty_count, live_count;
441 assert (info != NULL);
443 scan_info.al_fd = info->al_fd;
445 res = alock_grab_lock (info->al_fd, 0);
448 return ALOCK_UNSTABLE;
451 res = fstat (info->al_fd, &statbuf);
454 return ALOCK_UNSTABLE;
457 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
460 for (scan_info.al_slot = 1;
461 scan_info.al_slot < max_slot;
462 ++ scan_info.al_slot) {
463 if (scan_info.al_slot != info->al_slot) {
464 res = alock_query_slot (&scan_info);
466 if (res == ALOCK_LOCKED) {
469 } else if (res == ALOCK_DIRTY) {
472 } else if (res == -1) {
474 return ALOCK_UNSTABLE;
480 res = alock_release_lock (info->al_fd, 0);
483 return ALOCK_UNSTABLE;
489 return ALOCK_UNSTABLE;
491 return ALOCK_RECOVER;
499 alock_close ( alock_info_t * info )
501 alock_slot_t slot_data;
504 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
506 res = alock_grab_lock (info->al_fd, 0);
509 return ALOCK_UNSTABLE;
512 /* mark our slot as clean */
513 res = alock_read_slot (info, &slot_data);
516 if (slot_data.al_appname != NULL)
517 free (slot_data.al_appname);
518 return ALOCK_UNSTABLE;
520 slot_data.al_lock = ALOCK_UNLOCKED;
521 res = alock_write_slot (info, &slot_data);
524 if (slot_data.al_appname != NULL)
525 free (slot_data.al_appname);
526 return ALOCK_UNSTABLE;
528 if (slot_data.al_appname != NULL) {
529 free (slot_data.al_appname);
530 slot_data.al_appname = NULL;
533 res = alock_release_lock (info->al_fd, info->al_slot);
536 return ALOCK_UNSTABLE;
538 res = alock_release_lock (info->al_fd, 0);
541 return ALOCK_UNSTABLE;
544 res = close (info->al_fd);
545 if (res == -1) return ALOCK_UNSTABLE;
551 alock_recover ( alock_info_t * info )
554 alock_slot_t slot_data;
555 alock_info_t scan_info;
558 assert (info != NULL);
560 scan_info.al_fd = info->al_fd;
562 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
564 res = alock_grab_lock (info->al_fd, 0);
567 return ALOCK_UNSTABLE;
570 res = fstat (info->al_fd, &statbuf);
573 return ALOCK_UNSTABLE;
576 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
577 for (scan_info.al_slot = 1;
578 scan_info.al_slot < max_slot;
579 ++ scan_info.al_slot) {
580 if (scan_info.al_slot != info->al_slot) {
581 res = alock_query_slot (&scan_info);
583 if (res == ALOCK_LOCKED
584 || res == ALOCK_UNIQUE) {
585 /* recovery attempt on an active db? */
587 return ALOCK_UNSTABLE;
589 } else if (res == ALOCK_DIRTY) {
591 res = alock_read_slot (&scan_info, &slot_data);
594 return ALOCK_UNSTABLE;
596 slot_data.al_lock = ALOCK_UNLOCKED;
597 res = alock_write_slot (&scan_info, &slot_data);
600 if (slot_data.al_appname != NULL)
601 free (slot_data.al_appname);
602 return ALOCK_UNSTABLE;
604 if (slot_data.al_appname != NULL) {
605 free (slot_data.al_appname);
606 slot_data.al_appname = NULL;
609 } else if (res == -1) {
611 return ALOCK_UNSTABLE;
617 res = alock_release_lock (info->al_fd, 0);
620 return ALOCK_UNSTABLE;