2 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
7 #if !defined(lint) && !defined(__CODECENTER__)
8 static char *rcsid = "$Header$";
17 #define MAX_LOCK_TRIES 5
20 osa_adb_lock_ent lockinfo;
21 struct _locklist *next;
24 osa_adb_ret_t osa_adb_create_db(char *filename, char *lockfilename,
31 lf = fopen(lockfilename, "w+");
36 memset(&info, 0, sizeof(info));
42 db = dbopen(filename, O_RDWR | O_CREAT | O_EXCL, 0600, DB_HASH, &info);
45 if (db->close(db) < 0)
50 osa_adb_ret_t osa_adb_destroy_db(char *filename, char *lockfilename,
53 /* the admin databases do not contain security-critical data */
54 if (unlink(filename) < 0 ||
55 unlink(lockfilename) < 0)
60 osa_adb_ret_t osa_adb_init_db(osa_adb_db_t *dbp, char *filename,
61 char *lockfilename, int magic)
64 static struct _locklist *locklist = NULL;
65 struct _locklist *lockp;
68 if (dbp == NULL || filename == NULL)
71 db = (osa_adb_princ_t) malloc(sizeof(osa_adb_db_ent));
75 memset(db, 0, sizeof(*db));
79 db->info.nelem = 25000;
83 * A process is allowed to open the same database multiple times
84 * and access it via different handles. If the handles use
85 * distinct lockinfo structures, things get confused: lock(A),
86 * lock(B), release(B) will result in the kernel unlocking the
87 * lock file but handle A will still think the file is locked.
88 * Therefore, all handles using the same lock file must share a
89 * single lockinfo structure.
91 * It is not sufficient to have a single lockinfo structure,
92 * however, because a single process may also wish to open
93 * multiple different databases simultaneously, with different
94 * lock files. This code used to use a single static lockinfo
95 * structure, which means that the second database opened used
96 * the first database's lock file. This was Bad.
98 * We now maintain a linked list of lockinfo structures, keyed by
99 * lockfilename. An entry is added when this function is called
100 * with a new lockfilename, and all subsequent calls with that
101 * lockfilename use the existing entry, updating the refcnt.
102 * When the database is closed with fini_db(), the refcnt is
103 * decremented, and when it is zero the lockinfo structure is
104 * freed and reset. The entry in the linked list, however, is
105 * never removed; it will just be reinitialized the next time
106 * init_db is called with the right lockfilename.
109 /* find or create the lockinfo structure for lockfilename */
112 if (strcmp(lockp->lockinfo.filename, lockfilename) == 0)
118 /* doesn't exist, create it, add to list */
119 lockp = (struct _locklist *) malloc(sizeof(*lockp));
124 memset(lockp, 0, sizeof(*lockp));
125 lockp->next = locklist;
129 /* now initialize lockp->lockinfo if necessary */
130 if (lockp->lockinfo.lockfile == NULL) {
131 if (code = krb5_init_context(&lockp->lockinfo.context)) {
133 return((osa_adb_ret_t) code);
137 * needs be open read/write so that write locking can work with
140 lockp->lockinfo.filename = strdup(lockfilename);
141 if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r+")) == NULL) {
143 * maybe someone took away write permission so we could only
146 if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r"))
149 return OSA_ADB_NOLOCKFILE;
152 lockp->lockinfo.lockmode = lockp->lockinfo.lockcnt = 0;
155 /* lockp is set, lockinfo is initialized, update the reference count */
156 db->lock = &lockp->lockinfo;
159 db->filename = strdup(filename);
167 osa_adb_ret_t osa_adb_fini_db(osa_adb_db_t db, int magic)
169 if (db->magic != magic)
171 if (db->lock->refcnt == 0) {
172 /* barry says this can't happen */
173 return OSA_ADB_FAILURE;
178 if (db->lock->refcnt == 0) {
180 * Don't free db->lock->filename, it is used as a key to
181 * find the lockinfo entry in the linked list. If the
182 * lockfile doesn't exist, we must be closing the database
183 * after trashing it. This has to be allowed, so don't
186 (void) fclose(db->lock->lockfile);
187 db->lock->lockfile = NULL;
188 krb5_free_context(db->lock->context);
197 osa_adb_ret_t osa_adb_get_lock(osa_adb_db_t db, int mode)
199 int tries, gotlock, perm, krb5_mode, ret;
201 if (db->lock->lockmode >= mode) {
202 /* No need to upgrade lock, just incr refcnt and return */
209 case OSA_ADB_PERMANENT:
211 case OSA_ADB_EXCLUSIVE:
212 krb5_mode = KRB5_LOCKMODE_EXCLUSIVE;
215 krb5_mode = KRB5_LOCKMODE_SHARED;
221 for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) {
222 if ((ret = krb5_lock_file(db->lock->context,
223 fileno(db->lock->lockfile),
224 krb5_mode|KRB5_LOCKMODE_DONTBLOCK)) == 0) {
227 } else if (ret == EBADF && mode == OSA_ADB_EXCLUSIVE)
228 /* tried to exclusive-lock something we don't have */
229 /* write access to */
230 return OSA_ADB_NOEXCL_PERM;
235 /* test for all the likely "can't get lock" error codes */
236 if (ret == EACCES || ret == EAGAIN || ret == EWOULDBLOCK)
237 return OSA_ADB_CANTLOCK_DB;
242 * If the file no longer exists, someone acquired a permanent
243 * lock. If that process terminates its exclusive lock is lost,
244 * but if we already had the file open we can (probably) lock it
245 * even though it has been unlinked. So we need to insist that
248 if (access(db->lock->filename, F_OK) < 0) {
249 (void) krb5_lock_file(db->lock->context,
250 fileno(db->lock->lockfile),
251 KRB5_LOCKMODE_UNLOCK);
252 return OSA_ADB_NOLOCKFILE;
255 /* we have the shared/exclusive lock */
258 if (unlink(db->lock->filename) < 0) {
261 /* somehow we can't delete the file, but we already */
262 /* have the lock, so release it and return */
265 (void) krb5_lock_file(db->lock->context,
266 fileno(db->lock->lockfile),
267 KRB5_LOCKMODE_UNLOCK);
269 /* maybe we should return CANTLOCK_DB.. but that would */
270 /* look just like the db was already locked */
274 /* this releases our exclusive lock.. which is okay because */
275 /* now no one else can get one either */
276 (void) fclose(db->lock->lockfile);
279 db->lock->lockmode = mode;
284 osa_adb_ret_t osa_adb_release_lock(osa_adb_db_t db)
288 if (!db->lock->lockcnt) /* lock already unlocked */
289 return OSA_ADB_NOTLOCKED;
291 if (--db->lock->lockcnt == 0) {
292 if (db->lock->lockmode == OSA_ADB_PERMANENT) {
293 /* now we need to create the file since it does not exist */
294 if ((db->lock->lockfile = fopen(db->lock->filename,
296 return OSA_ADB_NOLOCKFILE;
297 } else if (ret = krb5_lock_file(db->lock->context,
298 fileno(db->lock->lockfile),
299 KRB5_LOCKMODE_UNLOCK))
302 db->lock->lockmode = 0;
307 osa_adb_ret_t osa_adb_open_and_lock(osa_adb_princ_t db, int locktype)
311 ret = osa_adb_get_lock(db, locktype);
312 if (ret != OSA_ADB_OK)
315 db->db = dbopen(db->filename, O_RDWR, 0600, DB_HASH, &db->info);
316 if (db->db == NULL) {
317 (void) osa_adb_release_lock(db);
319 return OSA_ADB_BAD_DB;
325 osa_adb_ret_t osa_adb_close_and_unlock(osa_adb_princ_t db)
329 if(db->db->close(db->db) == -1) {
330 (void) osa_adb_release_lock(db);
331 return OSA_ADB_FAILURE;
336 return(osa_adb_release_lock(db));