| /*  | 
 |  * This file is part of the UCB release of Plan 9. It is subject to the license | 
 |  * terms in the LICENSE file found in the top-level directory of this | 
 |  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
 |  * part of the UCB release of Plan 9, including this file, may be copied, | 
 |  * modified, propagated, or distributed except according to the terms contained | 
 |  * in the LICENSE file. | 
 |  */ | 
 | #include <stdlib.h> | 
 |  | 
 | #include <stdio.h> | 
 | #include <parlib/parlib.h> | 
 | #include <sys/types.h> | 
 | #include <sys/stat.h> | 
 | #include <unistd.h> | 
 | #include <signal.h> | 
 | #include <fcntl.h> | 
 | #include <iplib/iplib.h> | 
 | #include <ndblib/ndb.h> | 
 | #include <ndblib/ndbhf.h> | 
 |  | 
 | static struct ndb*	doopen(char*); | 
 | static void	hffree(struct ndb*); | 
 |  | 
 | static char *deffile = "/lib/ndb/local"; | 
 |  | 
 | /* | 
 |  *  the database entry in 'file' indicates the list of files | 
 |  *  that makeup the database.  Open each one and search in | 
 |  *  the same order. | 
 |  */ | 
 | struct ndb* | 
 | ndbopen(char *file) | 
 | { | 
 |  | 
 | 	struct ndb *db, *first, *last; | 
 | 	struct ndbs s; | 
 | 	struct ndbtuple *t, *nt; | 
 |  | 
 | 	if(file == 0) | 
 | 		file = deffile; | 
 | 	db = doopen(file); | 
 | 	if(db == 0) { | 
 | 		return 0; | 
 | 	} | 
 | 	first = last = db; | 
 | 	t = ndbsearch(db, &s, "database", ""); | 
 | 	fseek(db->b, 0, 0); | 
 | 	if(t == 0) { | 
 | 		return db; | 
 | 	} | 
 | 	for(nt = t; nt; nt = nt->entry){ | 
 | 		if(strcmp(nt->attr, "file") != 0) | 
 | 			continue; | 
 | 		if(strcmp(nt->val, file) == 0){ | 
 | 			/* default file can be reordered in the list */ | 
 | 			if(first->next == 0) | 
 | 				continue; | 
 | 			if(strcmp(first->file, file) == 0){ | 
 | 				db = first; | 
 | 				first = first->next; | 
 | 				last->next = db; | 
 | 				db->next = 0; | 
 | 				last = db; | 
 | 			} | 
 | 			continue; | 
 | 		} | 
 | 		db = doopen(nt->val); | 
 | 		if(db == 0) | 
 | 			continue; | 
 | 		last->next = db; | 
 | 		last = db; | 
 | 	} | 
 | 	ndbfree(t); | 
 | 	return first; | 
 | } | 
 |  | 
 | /* | 
 |  *  open a single file | 
 |  */ | 
 | static struct ndb* | 
 | doopen(char *file) | 
 | { | 
 |  | 
 | 	struct ndb *db; | 
 |  | 
 | 	db = (struct ndb*)calloc(sizeof(struct ndb), 1); | 
 | 	if(db == 0) { | 
 | 		return 0; | 
 | 	} | 
 | 	memset(db, 0, sizeof(struct ndb)); | 
 | 	strncpy(db->file, file, sizeof(db->file)-1); | 
 |  | 
 | 	if(ndbreopen(db) < 0){ | 
 | 		free(db); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	return db; | 
 | } | 
 |  | 
 | /* | 
 |  *  dump any cached information, forget the hash tables, and reopen a single file | 
 |  */ | 
 | int | 
 | ndbreopen(struct ndb *db) | 
 | { | 
 |  | 
 | 	int fd; | 
 | 	struct dir *d; | 
 |  | 
 | 	/* forget what we know about the open files */ | 
 | 	if(db->isopen){ | 
 | 		_ndbcacheflush(db); | 
 | 		hffree(db); | 
 | 		fclose(db->b); | 
 | 		db->mtime = 0; | 
 | 		db->isopen = 0; | 
 | 	} | 
 |  | 
 | 	/* try the open again */ | 
 | 	db->b = fopen(db->file, "r"); | 
 | 	if(! db->b) { | 
 | 		return -1; | 
 | 	} | 
 | #if 0 | 
 | 	d = dirfstat(fd); | 
 | 	if(d == NULL){ | 
 | 		close(fd); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	db->qid.path = d->qid.path; | 
 | 	db->mtime = d->mtime; | 
 | 	db->length = d->length; | 
 | 	free(d); | 
 | #else | 
 | 	struct stat s; | 
 | 	/* we opened it, this WILL work */ | 
 | 	stat(db->file, &s); | 
 | 	db->qid.path = s.st_ino; | 
 | 	db->mtime = s.st_mtime + 1; | 
 | 	db->length = s.st_size; | 
 | #endif | 
 | 	db->isopen = 1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  close the database files | 
 |  */ | 
 | void | 
 | ndbclose(struct ndb *db) | 
 | { | 
 |  | 
 | 	struct ndb *nextdb; | 
 |  | 
 | 	for(; db; db = nextdb){ | 
 | 		nextdb = db->next; | 
 | 		_ndbcacheflush(db); | 
 | 		hffree(db); | 
 | 		fclose(db->b); | 
 | 		free(db); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  *  free the hash files belonging to a db | 
 |  */ | 
 | static void | 
 | hffree(struct ndb *db) | 
 | { | 
 |  | 
 | 	struct ndbhf *hf, *next; | 
 |  | 
 | 	for(hf = db->hf; hf; hf = next){ | 
 | 		next = hf->next; | 
 | 		close(hf->fd); | 
 | 		free(hf); | 
 | 	} | 
 | 	db->hf = 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  return true if any part of the database has changed | 
 |  */ | 
 | int | 
 | ndbchanged(struct ndb *db) | 
 | { | 
 | 	/* TODO: implement me (no one calls this yet) */ | 
 | 	assert(0); | 
 | 	return 0; | 
 | #if 0 | 
 | 	struct ndb *ndb; | 
 | 	struct dir *d; | 
 |  | 
 | /* FIX ME */ | 
 | 	for(ndb = db; ndb != NULL; ndb = ndb->next){ | 
 | 		d = dirfstat(Bfildes(&ndb->b)); | 
 | 		if(d == NULL) | 
 | 			continue; | 
 | 		if(ndb->qid.path != d->qid.path | 
 | 		|| ndb->qid.vers != d->qid.vers){ | 
 | 			free(d); | 
 | 			return 1; | 
 | 		} | 
 | 		free(d); | 
 | 	} | 
 | 	return 0; | 
 | #endif | 
 | } |