]> git.sur5r.net Git - openldap/blob - servers/slapd/alock.c
Happy New Year
[openldap] / servers / slapd / alock.c
1 /* alock.c - access lock library */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2005-2018 The OpenLDAP Foundation.
6  * Portions Copyright 2004-2005 Symas Corporation.
7  * All rights reserved.
8  *
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.
12  *
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>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Emily Backes at Symas
19  * Corporation for inclusion in OpenLDAP Software.
20  */
21
22 #include "portable.h"
23
24 #if SLAPD_BDB || SLAPD_HDB
25
26 #include <lber.h>
27 #include "alock.h"
28 #include "lutil.h"
29
30 #include <ac/stdlib.h>
31 #include <ac/string.h>
32 #include <ac/unistd.h>
33 #include <ac/errno.h>
34 #include <ac/assert.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #ifdef HAVE_SYS_FILE_H
38 #include <sys/file.h>
39 #endif
40 #include <fcntl.h>
41
42 #ifdef _WIN32
43 #include <windows.h>
44 #include <stdio.h>
45 #include <io.h>
46 #endif
47
48
49 static int
50 alock_grab_lock ( int fd, int slot )
51 {
52         int res;
53         
54 #if defined( HAVE_LOCKF )
55         res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
56         if (res == -1) return -1;
57         res = lockf (fd, F_LOCK, (off_t) ALOCK_SLOT_SIZE);
58 #elif defined( HAVE_FCNTL )
59         struct flock lock_info;
60         (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
61
62         lock_info.l_type = F_WRLCK;
63         lock_info.l_whence = SEEK_SET;
64         lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
65         lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
66
67         res = fcntl (fd, F_SETLKW, &lock_info);
68 #elif defined( _WIN32 )
69         OVERLAPPED ov;
70         HANDLE hh = _get_osfhandle ( fd );
71         ov.hEvent = 0;
72         ov.Offset = ALOCK_SLOT_SIZE*slot;
73         ov.OffsetHigh = 0;
74         res = LockFileEx( hh, LOCKFILE_EXCLUSIVE_LOCK, 0,
75                 ALOCK_SLOT_SIZE, 0, &ov ) ? 0 : -1;
76 #else
77 #   error alock needs lockf, fcntl, or LockFile[Ex]
78 #endif
79         if (res == -1) {
80                 assert (errno != EDEADLK);
81                 return -1;
82         }
83         return 0;
84 }
85
86 static int
87 alock_release_lock ( int fd, int slot )
88 {
89         int res;
90         
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));
99
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;
104
105         res = fcntl (fd, F_SETLKW, &lock_info);
106         if (res == -1) return -1;
107 #elif defined( _WIN32 )
108         HANDLE hh = _get_osfhandle ( fd );
109         if ( !UnlockFile ( hh, ALOCK_SLOT_SIZE*slot, 0,
110                 ALOCK_SLOT_SIZE, 0 ))
111                 return -1;
112 #else
113 #   error alock needs lockf, fcntl, or LockFile[Ex]
114 #endif
115
116         return 0;
117 }
118
119 static int
120 alock_share_lock ( int fd, int slot )
121 {
122         int res;
123
124 #if defined( HAVE_LOCKF )
125         res = 0;        /* lockf has no shared locks */
126 #elif defined ( HAVE_FCNTL )
127         struct flock lock_info;
128         (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
129
130         /* The shared lock replaces the existing lock */
131         lock_info.l_type = F_RDLCK;
132         lock_info.l_whence = SEEK_SET;
133         lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
134         lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
135
136         res = fcntl (fd, F_SETLK, &lock_info);
137         if (res == -1) return -1;
138 #elif defined( _WIN32 )
139         OVERLAPPED ov;
140         HANDLE hh = _get_osfhandle ( fd );
141
142         /* Windows locks are mandatory, not advisory.
143          * We must downgrade the lock to allow future
144          * callers to read the slot data.
145          *
146          * First acquire a shared lock. Unlock will
147          * release the existing exclusive lock.
148          */
149         ov.hEvent = 0;
150         ov.Offset = ALOCK_SLOT_SIZE*slot;
151         ov.OffsetHigh = 0;
152         LockFileEx (hh, 0, 0, ALOCK_SLOT_SIZE, 0, &ov);
153         UnlockFile (hh, ALOCK_SLOT_SIZE*slot, 0, ALOCK_SLOT_SIZE, 0);
154 #else
155 #   error alock needs lockf, fcntl, or LockFile[Ex]
156 #endif
157
158         return 0;
159 }
160
161 static int
162 alock_test_lock ( int fd, int slot )
163 {
164         int res;
165
166 #if defined( HAVE_LOCKF )
167         res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
168         if (res == -1) return -1;
169
170         res = lockf (fd, F_TEST, (off_t) ALOCK_SLOT_SIZE);
171         if (res == -1) {
172                 if (errno == EACCES || errno == EAGAIN) { 
173                         return ALOCK_LOCKED;
174                 } else {
175                         return -1;
176                 }
177         }
178 #elif defined( HAVE_FCNTL )
179         struct flock lock_info;
180         (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
181
182         lock_info.l_type = F_WRLCK;
183         lock_info.l_whence = SEEK_SET;
184         lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
185         lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
186
187         res = fcntl (fd, F_GETLK, &lock_info);
188         if (res == -1) return -1;
189
190         if (lock_info.l_type != F_UNLCK) return ALOCK_LOCKED;
191 #elif defined( _WIN32 )
192         OVERLAPPED ov;
193         HANDLE hh = _get_osfhandle ( fd );
194         ov.hEvent = 0;
195         ov.Offset = ALOCK_SLOT_SIZE*slot;
196         ov.OffsetHigh = 0;
197         if( !LockFileEx( hh,
198                 LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY, 0,
199                 ALOCK_SLOT_SIZE, 0, &ov )) {
200                 int err = GetLastError();
201                 if ( err == ERROR_LOCK_VIOLATION )
202                         return ALOCK_LOCKED;
203                 else
204                         return -1;
205         }
206 #else
207 #   error alock needs lockf, fcntl, or LockFile
208 #endif
209         
210         return 0;
211 }
212
213 /* Read a 64bit LE value */
214 static unsigned long int
215 alock_read_iattr ( unsigned char * bufptr )
216 {
217         unsigned long int val = 0;
218         int count;
219
220         assert (bufptr != NULL);
221
222         bufptr += sizeof (unsigned long int);
223         for (count=0; count <= (int) sizeof (unsigned long int); ++count) {
224                 val <<= 8;
225                 val += (unsigned long int) *bufptr--;
226         }
227
228         return val;
229 }
230
231 /* Write a 64bit LE value */
232 static void
233 alock_write_iattr ( unsigned char * bufptr,
234                     unsigned long int val )
235 {
236         int count;
237
238         assert (bufptr != NULL);
239
240         for (count=0; count < 8; ++count) {
241                 *bufptr++ = (unsigned char) (val & 0xff);
242                 val >>= 8;
243         }
244 }
245
246 static int
247 alock_read_slot ( alock_info_t * info,
248                   alock_slot_t * slot_data )
249 {
250         unsigned char slotbuf [ALOCK_SLOT_SIZE];
251         int res, size, size_total, err;
252
253         assert (info != NULL);
254         assert (slot_data != NULL);
255         assert (info->al_slot > 0);
256
257         res = lseek (info->al_fd, 
258                      (off_t) (ALOCK_SLOT_SIZE * info->al_slot), 
259                      SEEK_SET);
260         if (res == -1) return -1;
261
262         size_total = 0;
263         while (size_total < ALOCK_SLOT_SIZE) {
264                 size = read (info->al_fd, 
265                              slotbuf + size_total, 
266                              ALOCK_SLOT_SIZE - size_total);
267                 if (size == 0) return -1;
268                 if (size < 0) {
269                         err = errno;
270                         if (err != EINTR && err != EAGAIN) return -1;
271                 } else {
272                         size_total += size;
273                 }
274         }
275         
276         if (alock_read_iattr (slotbuf) != ALOCK_MAGIC) {
277                 return -1;
278         }
279         slot_data->al_lock  = alock_read_iattr (slotbuf+8);
280         slot_data->al_stamp = alock_read_iattr (slotbuf+16);
281         slot_data->al_pid   = alock_read_iattr (slotbuf+24);
282
283         if (slot_data->al_appname) ber_memfree (slot_data->al_appname);
284         slot_data->al_appname = ber_memcalloc (1, ALOCK_MAX_APPNAME);
285         if (slot_data->al_appname == NULL) {
286                 return -1;
287         }
288         strncpy (slot_data->al_appname, (char *)slotbuf+32, ALOCK_MAX_APPNAME-1);
289         (slot_data->al_appname) [ALOCK_MAX_APPNAME-1] = '\0';
290
291         return 0;
292 }
293
294 static int
295 alock_write_slot ( alock_info_t * info,
296                    alock_slot_t * slot_data )
297 {
298         unsigned char slotbuf [ALOCK_SLOT_SIZE];
299         int res, size, size_total, err;
300
301         assert (info != NULL);
302         assert (slot_data != NULL);
303         assert (info->al_slot > 0);
304
305         (void) memset ((void *) slotbuf, 0, ALOCK_SLOT_SIZE);
306         
307         alock_write_iattr (slotbuf,    ALOCK_MAGIC);
308         assert (alock_read_iattr (slotbuf) == ALOCK_MAGIC);
309         alock_write_iattr (slotbuf+8,  slot_data->al_lock);
310         alock_write_iattr (slotbuf+16, slot_data->al_stamp);
311         alock_write_iattr (slotbuf+24, slot_data->al_pid);
312
313         if (slot_data->al_appname)
314                 strncpy ((char *)slotbuf+32, slot_data->al_appname, ALOCK_MAX_APPNAME-1);
315         slotbuf[ALOCK_SLOT_SIZE-1] = '\0';
316
317         res = lseek (info->al_fd, 
318                      (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
319                      SEEK_SET);
320         if (res == -1) return -1;
321
322         size_total = 0;
323         while (size_total < ALOCK_SLOT_SIZE) {
324                 size = write (info->al_fd, 
325                               slotbuf + size_total, 
326                               ALOCK_SLOT_SIZE - size_total);
327                 if (size == 0) return -1;
328                 if (size < 0) {
329                         err = errno;
330                         if (err != EINTR && err != EAGAIN) return -1;
331                 } else {
332                         size_total += size;
333                 }
334         }
335         
336         return 0;
337 }
338
339 static int
340 alock_query_slot ( alock_info_t * info )
341 {
342         int res, nosave;
343         alock_slot_t slot_data;
344
345         assert (info != NULL);
346         assert (info->al_slot > 0);
347         
348         (void) memset ((void *) &slot_data, 0, sizeof (alock_slot_t));
349         alock_read_slot (info, &slot_data);
350
351         if (slot_data.al_appname != NULL) ber_memfree (slot_data.al_appname);
352         slot_data.al_appname = NULL;
353
354         nosave = slot_data.al_lock & ALOCK_NOSAVE;
355
356         if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNLOCKED)
357                 return slot_data.al_lock;
358
359         res = alock_test_lock (info->al_fd, info->al_slot);
360         if (res < 0) return -1;
361         if (res > 0) {
362                 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNIQUE) {
363                         return slot_data.al_lock;
364                 } else {
365                         return ALOCK_LOCKED | nosave;
366                 }
367         }
368         
369         return ALOCK_DIRTY | nosave;
370 }
371
372 int 
373 alock_open ( alock_info_t * info,
374              const char * appname,
375              const char * envdir,
376              int locktype )
377 {
378         struct stat statbuf;
379         alock_info_t scan_info;
380         alock_slot_t slot_data;
381         char * filename;
382         int res, max_slot;
383         int dirty_count, live_count, nosave;
384         char *ptr;
385
386         assert (info != NULL);
387         assert (appname != NULL);
388         assert (envdir != NULL);
389         assert ((locktype & ALOCK_SMASK) >= 1 && (locktype & ALOCK_SMASK) <= 2);
390
391         slot_data.al_lock = locktype;
392         slot_data.al_stamp = time(NULL);
393         slot_data.al_pid = getpid();
394         slot_data.al_appname = ber_memcalloc (1, ALOCK_MAX_APPNAME);
395         if (slot_data.al_appname == NULL) {
396                 return ALOCK_UNSTABLE;
397         }
398         strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
399         slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
400
401         filename = ber_memcalloc (1, strlen (envdir) + strlen ("/alock") + 1);
402         if (filename == NULL ) {
403                 ber_memfree (slot_data.al_appname);
404                 return ALOCK_UNSTABLE;
405         }
406         ptr = lutil_strcopy(filename, envdir);
407         lutil_strcopy(ptr, "/alock");
408 #ifdef _WIN32
409         { HANDLE handle = CreateFile (filename, GENERIC_READ|GENERIC_WRITE,
410                 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
411                 FILE_ATTRIBUTE_NORMAL, NULL);
412                 info->al_fd = _open_osfhandle (handle, 0);
413         }
414 #else
415         info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
416 #endif
417         ber_memfree (filename);
418         if (info->al_fd < 0) {
419                 ber_memfree (slot_data.al_appname);
420                 return ALOCK_UNSTABLE;
421         }
422         info->al_slot = 0;
423
424         res = alock_grab_lock (info->al_fd, 0);
425         if (res == -1) { 
426                 close (info->al_fd);
427                 ber_memfree (slot_data.al_appname);
428                 return ALOCK_UNSTABLE;
429         }
430
431         res = fstat (info->al_fd, &statbuf);
432         if (res == -1) { 
433                 close (info->al_fd);
434                 ber_memfree (slot_data.al_appname);
435                 return ALOCK_UNSTABLE;
436         }
437
438         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
439         dirty_count = 0;
440         live_count = 0;
441         nosave = 0;
442         scan_info.al_fd = info->al_fd;
443         for (scan_info.al_slot = 1; 
444              scan_info.al_slot < max_slot;
445              ++ scan_info.al_slot) {
446                 if (scan_info.al_slot != info->al_slot) {
447                         res = alock_query_slot (&scan_info);
448
449                         if (res & ALOCK_NOSAVE) {
450                                 nosave = ALOCK_NOSAVE;
451                                 res ^= ALOCK_NOSAVE;
452                         }
453                         if (res == ALOCK_UNLOCKED
454                             && info->al_slot == 0) {
455                                 info->al_slot = scan_info.al_slot;
456
457                         } else if (res == ALOCK_LOCKED) {
458                                 ++live_count;
459
460                         } else if (res == ALOCK_UNIQUE
461                                 && (( locktype & ALOCK_SMASK ) == ALOCK_UNIQUE
462                                 || nosave )) {
463                                 close (info->al_fd);
464                                 ber_memfree (slot_data.al_appname);
465                                 return ALOCK_BUSY;
466
467                         } else if (res == ALOCK_DIRTY) {
468                                 ++dirty_count;
469
470                         } else if (res == -1) {
471                                 close (info->al_fd);
472                                 ber_memfree (slot_data.al_appname);
473                                 return ALOCK_UNSTABLE;
474
475                         }
476                 }
477         }
478
479         if (dirty_count && live_count) {
480                 close (info->al_fd);
481                 ber_memfree (slot_data.al_appname);
482                 return ALOCK_UNSTABLE;
483         }
484         
485         if (info->al_slot == 0) info->al_slot = max_slot + 1;
486         res = alock_grab_lock (info->al_fd,
487                                info->al_slot);
488         if (res == -1) { 
489                 close (info->al_fd);
490                 ber_memfree (slot_data.al_appname);
491                 return ALOCK_UNSTABLE;
492         }
493         res = alock_write_slot (info, &slot_data);
494         ber_memfree (slot_data.al_appname);
495         if (res == -1) { 
496                 close (info->al_fd);
497                 return ALOCK_UNSTABLE;
498         }
499         alock_share_lock (info->al_fd, info->al_slot);
500
501         res = alock_release_lock (info->al_fd, 0);
502         if (res == -1) { 
503                 close (info->al_fd);
504                 return ALOCK_UNSTABLE;
505         }
506         
507         if (dirty_count) return ALOCK_RECOVER | nosave;
508         return ALOCK_CLEAN | nosave;
509 }
510
511 int 
512 alock_scan ( alock_info_t * info )
513 {
514         struct stat statbuf;
515         alock_info_t scan_info;
516         int res, max_slot;
517         int dirty_count, live_count, nosave;
518
519         assert (info != NULL);
520
521         scan_info.al_fd = info->al_fd;
522
523         res = alock_grab_lock (info->al_fd, 0);
524         if (res == -1) {
525                 close (info->al_fd);
526                 return ALOCK_UNSTABLE;
527         }
528
529         res = fstat (info->al_fd, &statbuf);
530         if (res == -1) {
531                 close (info->al_fd);
532                 return ALOCK_UNSTABLE;
533         }
534
535         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
536         dirty_count = 0;
537         live_count = 0;
538         nosave = 0;
539         for (scan_info.al_slot = 1; 
540              scan_info.al_slot < max_slot;
541              ++ scan_info.al_slot) {
542                 if (scan_info.al_slot != info->al_slot) {
543                         res = alock_query_slot (&scan_info);
544
545                         if (res & ALOCK_NOSAVE) {
546                                 nosave = ALOCK_NOSAVE;
547                                 res ^= ALOCK_NOSAVE;
548                         }
549
550                         if (res == ALOCK_LOCKED) {
551                                 ++live_count;
552                                 
553                         } else if (res == ALOCK_DIRTY) {
554                                 ++dirty_count;
555
556                         } else if (res == -1) {
557                                 close (info->al_fd);
558                                 return ALOCK_UNSTABLE;
559
560                         }
561                 }
562         }
563
564         res = alock_release_lock (info->al_fd, 0);
565         if (res == -1) {
566                 close (info->al_fd);
567                 return ALOCK_UNSTABLE;
568         }
569
570         if (dirty_count) {
571                 if (live_count) {
572                         close (info->al_fd);
573                         return ALOCK_UNSTABLE;
574                 } else {
575                         return ALOCK_RECOVER | nosave;
576                 }
577         }
578         
579         return ALOCK_CLEAN | nosave;
580 }
581
582 int
583 alock_close ( alock_info_t * info, int nosave )
584 {
585         alock_slot_t slot_data;
586         int res;
587
588         if ( !info->al_slot )
589                 return ALOCK_CLEAN;
590
591         (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
592
593         res = alock_grab_lock (info->al_fd, 0);
594         if (res == -1) {
595 fail:
596                 /* Windows doesn't clean up locks immediately when a process exits.
597                  * Make sure we release our locks, to prevent stale locks from
598                  * hanging around.
599                  */
600                 alock_release_lock (info->al_fd, 0);
601                 close (info->al_fd);
602                 return ALOCK_UNSTABLE;
603         }
604
605         /* mark our slot as clean */
606         res = alock_read_slot (info, &slot_data);
607         if (res == -1) {
608                 if (slot_data.al_appname != NULL) 
609                         ber_memfree (slot_data.al_appname);
610                 goto fail;
611         }
612         slot_data.al_lock = ALOCK_UNLOCKED;
613         if ( nosave )
614                 slot_data.al_lock |= ALOCK_NOSAVE;
615         /* since we have slot 0 locked, we don't need our slot lock */
616         res = alock_release_lock (info->al_fd, info->al_slot);
617         if (res == -1) {
618                 goto fail;
619         }
620         res = alock_write_slot (info, &slot_data);
621         if (res == -1) {
622                 if (slot_data.al_appname != NULL) 
623                         ber_memfree (slot_data.al_appname);
624                 goto fail;
625         }
626         if (slot_data.al_appname != NULL) {
627                 ber_memfree (slot_data.al_appname);
628                 slot_data.al_appname = NULL;
629         }
630
631         res = alock_release_lock (info->al_fd, 0);
632         if (res == -1) {
633                 close (info->al_fd);
634                 return ALOCK_UNSTABLE;
635         }
636
637         res = close (info->al_fd);
638         if (res == -1) return ALOCK_UNSTABLE;
639         
640         return ALOCK_CLEAN;
641 }
642
643 int 
644 alock_recover ( alock_info_t * info )
645 {
646         struct stat statbuf;
647         alock_slot_t slot_data;
648         alock_info_t scan_info;
649         int res, max_slot;
650
651         assert (info != NULL);
652
653         scan_info.al_fd = info->al_fd;
654
655         (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
656
657         res = alock_grab_lock (info->al_fd, 0);
658         if (res == -1) {
659                 goto fail;
660         }
661
662         res = fstat (info->al_fd, &statbuf);
663         if (res == -1) {
664                 goto fail;
665         }
666
667         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
668         for (scan_info.al_slot = 1; 
669              scan_info.al_slot < max_slot;
670              ++ scan_info.al_slot) {
671                 if (scan_info.al_slot != info->al_slot) {
672                         res = alock_query_slot (&scan_info) & ~ALOCK_NOSAVE;
673
674                         if (res == ALOCK_LOCKED
675                             || res == ALOCK_UNIQUE) {
676                                 /* recovery attempt on an active db? */
677                                 goto fail;
678                                 
679                         } else if (res == ALOCK_DIRTY) {
680                                 /* mark it clean */
681                                 res = alock_read_slot (&scan_info, &slot_data);
682                                 if (res == -1) {
683                                         goto fail;
684                                 }
685                                 slot_data.al_lock = ALOCK_UNLOCKED;
686                                 res = alock_write_slot (&scan_info, &slot_data);
687                                 if (res == -1) {
688                                         if (slot_data.al_appname != NULL) 
689                                                 ber_memfree (slot_data.al_appname);
690                                         goto fail;
691                                 }
692                                 if (slot_data.al_appname != NULL) {
693                                         ber_memfree (slot_data.al_appname);
694                                         slot_data.al_appname = NULL;
695                                 }
696                                 
697                         } else if (res == -1) {
698                                 goto fail;
699
700                         }
701                 }
702         }
703
704         res = alock_release_lock (info->al_fd, 0);
705         if (res == -1) {
706                 close (info->al_fd);
707                 return ALOCK_UNSTABLE;
708         }
709
710         return ALOCK_CLEAN;
711
712 fail:
713         alock_release_lock (info->al_fd, 0);
714         close (info->al_fd);
715         return ALOCK_UNSTABLE;
716 }
717
718 #endif /* SLAPD_BDB || SLAPD_HDB */