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.
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>
35
#ifdef HAVE_SYS_FILE_H
43
#include <sys/locking.h>
48
alock_grab_lock ( int fd, int slot )
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 )
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 )
79
# error alock needs lockf, fcntl, or _locking
82
assert (errno != EDEADLK);
89
alock_release_lock ( int fd, int slot )
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;
115
# error alock needs lockf, fcntl, or _locking
122
alock_test_lock ( int fd, int slot )
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);
132
if (errno == EACCES || errno == EAGAIN) {
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 );
157
if( errno == EACCES ) {
164
# error alock needs lockf, fcntl, or _locking
170
/* Read a 64bit LE value */
171
static unsigned long int
172
alock_read_iattr ( unsigned char * bufptr )
174
unsigned long int val = 0;
177
assert (bufptr != NULL);
179
bufptr += sizeof (unsigned long int);
180
for (count=0; count <= sizeof (unsigned long int); ++count) {
182
val += (unsigned long int) *bufptr--;
188
/* Write a 64bit LE value */
190
alock_write_iattr ( unsigned char * bufptr,
191
unsigned long int val )
195
assert (bufptr != NULL);
197
for (count=0; count < 8; ++count) {
198
*bufptr++ = (unsigned char) (val & 0xff);
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),
217
if (res == -1) return -1;
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;
227
if (err != EINTR && err != EAGAIN) return -1;
233
if (alock_read_iattr (slotbuf) != ALOCK_MAGIC) {
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';
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),
274
if (res == -1) return -1;
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;
284
if (err != EINTR && err != EAGAIN) return -1;
294
alock_query_slot ( alock_info_t * info )
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;
316
if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNIQUE) {
317
return slot_data.al_lock;
319
return ALOCK_LOCKED | nosave;
323
return ALOCK_DIRTY | nosave;
327
alock_open ( alock_info_t * info,
328
const char * appname,
333
alock_info_t scan_info;
334
alock_slot_t slot_data;
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);
356
if (info->al_fd < 0) {
357
free (slot_data.al_appname);
358
return ALOCK_UNSTABLE;
362
res = alock_grab_lock (info->al_fd, 0);
365
free (slot_data.al_appname);
366
return ALOCK_UNSTABLE;
369
res = fstat (info->al_fd, &statbuf);
372
free (slot_data.al_appname);
373
return ALOCK_UNSTABLE;
376
max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
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;
391
if (res == ALOCK_UNLOCKED
392
&& info->al_slot == 0) {
393
info->al_slot = scan_info.al_slot;
395
} else if (res == ALOCK_LOCKED) {
398
} else if (res == ALOCK_UNIQUE
399
&& locktype == ALOCK_UNIQUE) {
401
free (slot_data.al_appname);
404
} else if (res == ALOCK_DIRTY) {
407
} else if (res == -1) {
409
free (slot_data.al_appname);
410
return ALOCK_UNSTABLE;
416
if (dirty_count && live_count) {
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,
427
free (slot_data.al_appname);
428
return ALOCK_UNSTABLE;
430
res = alock_write_slot (info, &slot_data);
431
free (slot_data.al_appname);
434
return ALOCK_UNSTABLE;
437
res = alock_release_lock (info->al_fd, 0);
440
return ALOCK_UNSTABLE;
443
if (dirty_count) return ALOCK_RECOVER | nosave;
444
return ALOCK_CLEAN | nosave;
448
alock_scan ( alock_info_t * info )
451
alock_info_t scan_info;
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);
462
return ALOCK_UNSTABLE;
465
res = fstat (info->al_fd, &statbuf);
468
return ALOCK_UNSTABLE;
471
max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
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;
486
if (res == ALOCK_LOCKED) {
489
} else if (res == ALOCK_DIRTY) {
492
} else if (res == -1) {
494
return ALOCK_UNSTABLE;
500
res = alock_release_lock (info->al_fd, 0);
503
return ALOCK_UNSTABLE;
509
return ALOCK_UNSTABLE;
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;
524
if ( !info->al_slot )
527
(void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
529
res = alock_grab_lock (info->al_fd, 0);
532
return ALOCK_UNSTABLE;
535
/* mark our slot as clean */
536
res = alock_read_slot (info, &slot_data);
539
if (slot_data.al_appname != NULL)
540
free (slot_data.al_appname);
541
return ALOCK_UNSTABLE;
543
slot_data.al_lock = ALOCK_UNLOCKED;
545
slot_data.al_lock |= ALOCK_NOSAVE;
546
res = alock_write_slot (info, &slot_data);
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);
561
return ALOCK_UNSTABLE;
563
res = alock_release_lock (info->al_fd, 0);
566
return ALOCK_UNSTABLE;
569
res = close (info->al_fd);
570
if (res == -1) return ALOCK_UNSTABLE;
576
alock_recover ( alock_info_t * info )
579
alock_slot_t slot_data;
580
alock_info_t scan_info;
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);
592
return ALOCK_UNSTABLE;
595
res = fstat (info->al_fd, &statbuf);
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? */
612
return ALOCK_UNSTABLE;
614
} else if (res == ALOCK_DIRTY) {
616
res = alock_read_slot (&scan_info, &slot_data);
619
return ALOCK_UNSTABLE;
621
slot_data.al_lock = ALOCK_UNLOCKED;
622
res = alock_write_slot (&scan_info, &slot_data);
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) {
636
return ALOCK_UNSTABLE;
642
res = alock_release_lock (info->al_fd, 0);
645
return ALOCK_UNSTABLE;
651
#endif /* SLAPD_BDB || SLAPD_HDB */