/* * A component of ZMailer * * Copyright 1996-2002 Matti Aarnio */ /* LINTLIBRARY */ #include "mailer.h" #include "sleepycatdb.h" #ifdef HAVE_DB #ifdef HAVE_FCNTL_H # include #endif #include #include "search.h" #include "io.h" #include "libz.h" #include "libc.h" #include "libsh.h" #include extern int errno; extern int deferit; /* * Flush buffered information from this database, close any file descriptors. */ void close_bhash(sip,comment) search_info *sip; const char *comment; { ZSleepyPrivate *prv; if (*(sip->dbprivate) == NULL ) return; prv = *(sip->dbprivate); SLEEPYCATDBCLOSE(prv->db); zsleepyprivatefree(prv); *(sip->dbprivate) = NULL; } static ZSleepyPrivate * open_bhash __((search_info *, int, const char *)); static ZSleepyPrivate * open_bhash(sip, roflag, comment) search_info *sip; int roflag; const char *comment; { int i; ZSleepyPrivate **prvp = (ZSleepyPrivate **)sip->dbprivate; DB *db = NULL; if (sip->cfgfile) { /* read the related configuration file, e.g. information about environment, etc.. */ } if (sip->file == NULL) return NULL; if (*prvp && roflag != (*prvp)->roflag) close_bhash(sip,"open_bhash"); if (*prvp) db = (*prvp)->db; if (db == NULL) { char *phase = ""; /* Three attempts to open it.. */ for (i = 0; i < 3; ++i) { int err; *prvp = zsleepyprivateinit(sip->file, sip->cfgfile, DB_HASH); if (!*prvp) break; /* URGH!! Out of memory! */ err = zsleepyprivateopen(*prvp, roflag, 0644, &phase); db = (*prvp)->db; if (db) break; if (*prvp) zsleepyprivatefree(*prvp); *prvp = NULL; sleep(1); /* Open failed, retry after a moment */ } /* Still failed ?? */ if (db == NULL) { ++deferit; v_set(DEFER, DEFER_IO_ERROR); fprintf(stderr, "%s: cannot open%s %s!\n", comment, phase, sip->file); if (*prvp) zsleepyprivatefree(*prvp); return NULL; } } /* Got it open ? */ if (db != NULL) { /* Prepare for modp_bhash() tests. */ struct stat stbuf; int fd = -1, err = 0; #if defined(HAVE_DB2) || defined(HAVE_DB3) || defined(HAVE_DB4) err = (db->fd)(db, &fd); if (fstat(fd, &stbuf) < 0) { fprintf(stderr, "open_bhash: cannot fstat(\"%s\"(%d))! err=%d/%s (%s/%s)\n", sip->file, fd, err, errno, db_strerror(err), strerror(errno)); SLEEPYCATDBCLOSE(db); if (*prvp) zsleepyprivatefree(*prvp); return NULL; } #else fd = (db->fd)(db); if (fstat(fd, &stbuf) < 0) { fprintf(stderr, "open_bhash: cannot fstat(\"%s\"/%d))! err=%d (%s)\n", sip->file, fd, errno, strerror(errno)); SLEEPYCATDBCLOSE(db); if (*prvp) zsleepyprivatefree(*prvp); return NULL; } #endif (*prvp)->mtime = stbuf.st_mtime; } return *prvp; } /* * Search a B-TREE format database for a key pair. */ conscell * search_bhash(sip) search_info *sip; { ZSleepyPrivate *prv; DB *db; DBT val, key; int retry, rc; retry = 0; #if 0 reopen: #endif prv = open_bhash(sip, O_RDONLY, "search_bhash"); if (prv == NULL) return NULL; /* Huh! */ db = prv->db; memset(&key, 0, sizeof(key)); memset(&val, 0, sizeof(val)); key.data = (void*)sip->key; key.size = strlen(sip->key) + 1; #ifdef DB_INIT_TXN rc = (db->get)(db, NULL, &key, &val, 0); #else rc = (db->get)(db, &key, &val, 0); #endif if (rc != 0) { #if 0 /* SleepyCat's DB 2.x leaks memory mappings when opening... at least the version at glibc 2.1.1 */ if (!retry && rc < 0) { close_bhash(sip,"search_bhash"); ++retry; goto reopen; } #endif return NULL; } return newstring(dupnstr(val.data, val.size), val.size); } /* * Add the indicated key/value pair to the database. */ int add_bhash(sip, value) search_info *sip; const char *value; { DB *db; DBT val, key; int rc; ZSleepyPrivate *prv; prv = open_bhash(sip, O_RDWR, "add_bhash"); if (prv == NULL) return EOF; db = prv->db; memset(&key, 0, sizeof(key)); memset(&val, 0, sizeof(val)); key.data = (void*)sip->key; key.size = strlen(sip->key) + 1; val.data = (void*)value; val.size = strlen(value)+1; #ifdef DB_INIT_TXN rc = (db->put)(db, NULL, &key, &val, 0); /* Emulate BSD DB 1.85 behaviour */ if (rc != 0) rc = -1; #else rc = (db->put)(db, &key, &val, 0); #endif if (rc < 0) { ++deferit; v_set(DEFER, DEFER_IO_ERROR); fprintf(stderr, "add_bhash: cannot store (\"%s\",\"%s\")\n", sip->key, value); return EOF; } return 0; } /* * Remove the indicated key from the database. */ int remove_bhash(sip) search_info *sip; { DB *db; DBT key; int rc; ZSleepyPrivate *prv; prv = open_bhash(sip, O_RDWR, "remove_bhash"); if (prv == NULL) return EOF; db = prv->db; memset(&key, 0, sizeof(key)); key.data = (void*)sip->key; key.size = strlen(sip->key) + 1; #ifdef DB_INIT_TXN rc = (db->del)(db, NULL, &key, 0); #else rc = (db->del)(db, &key, 0); #endif if (rc < 0) { ++deferit; v_set(DEFER, DEFER_IO_ERROR); fprintf(stderr, "remove_bhash: cannot remove \"%s\"\n", sip->key); return EOF; } return 0; } /* * Print the database. */ void print_bhash(sip, outfp) search_info *sip; FILE *outfp; { DB *db; DBT key, val; int rc; ZSleepyPrivate *prv; #if defined(HAVE_DB2) || defined(HAVE_DB3) || defined(HAVE_DB4) DBC *curs = NULL; prv = open_bhash(sip, O_RDONLY, "print_bhash"); if (prv == NULL) return; db = prv->db; #ifdef HAVE_DB_CURSOR4 rc = (db->cursor)(db, NULL, &curs, 0); #else rc = (db->cursor)(db, NULL, &curs); #endif prv->cursor = curs; memset(&val, 0, sizeof(val)); memset(&key, 0, sizeof(key)); if (rc == 0 && curs) rc = (curs->c_get)(curs, &key, &val, DB_FIRST); for ( ; rc == 0 ; ) { if (val.data == NULL) continue; if (*(char*)val.data == '\0') fprintf(outfp, "%s\n", key.data); else fprintf(outfp, "%s\t%s\n", key.data, val.data); memset(&val, 0, sizeof(val)); memset(&key, 0, sizeof(key)); rc = (curs->c_get)(curs, &key, &val, DB_NEXT); } (curs->c_close)(curs); prv->cursor = NULL; #else prv = open_bhash(sip, O_RDONLY, "print_bhash"); if (prv == NULL) return; db = prv->db; memset(&val, 0, sizeof(val)); memset(&key, 0, sizeof(key)); rc = (db->seq)(db, &key, &val, R_FIRST); for ( ; rc == 0 ; ) { if (val.data == NULL) continue; if (*(char*)val.data == '\0') fprintf(outfp, "%s\n", key.data); else fprintf(outfp, "%s\t%s\n", key.data, val.data); memset(&val, 0, sizeof(val)); memset(&key, 0, sizeof(key)); rc = (db->seq)(db, &key, &val, R_NEXT); } #endif fflush(outfp); } /* * Count the database. */ void count_bhash(sip, outfp) search_info *sip; FILE *outfp; { DB *db; DBT key, val; int cnt = 0; int rc; ZSleepyPrivate * prv; #if defined(HAVE_DB2) || defined(HAVE_DB3) || defined(HAVE_DB4) DBC *curs; prv = open_bhash(sip, O_RDONLY, "count_bhash"); if (prv && prv->db) { db = prv->db; curs = NULL; #ifdef HAVE_DB_CURSOR4 rc = (db->cursor)(db, NULL, &curs, 0); #else rc = (db->cursor)(db, NULL, &curs); #endif prv->cursor = curs; memset(&val, 0, sizeof(val)); memset(&key, 0, sizeof(key)); if (rc == 0 && curs) rc = (curs->c_get)(curs, &key, &val, DB_FIRST); while (rc == 0) { if (val.data == NULL) /* ???? When this would happen ? */ continue; ++cnt; memset(&val, 0, sizeof(val)); memset(&key, 0, sizeof(key)); rc = (curs->c_get)(curs, &key, &val, DB_NEXT); } } (curs->c_close)(curs); prv->cursor = NULL; #else prv = open_bhash(sip, O_RDONLY, "count_bhash"); if (prv != NULL) { db = prv->db; memset(&val, 0, sizeof(val)); memset(&key, 0, sizeof(key)); rc = (db->seq)(db, &key, &val, R_FIRST); while (rc == 0) { if (val.data == NULL) /* ???? When this would happen ? */ continue; ++cnt; rc = (db->seq)(db, &key, &val, R_NEXT); } } #endif fprintf(outfp,"%d\n",cnt); fflush(outfp); } /* * Print the uid of the owner of the database. Note that for db-style * databases there are several files involved so picking one of them for * security purposes is very dubious. */ void owner_bhash(sip, outfp) search_info *sip; FILE *outfp; { DB *db; struct stat stbuf; int fd = -1; ZSleepyPrivate *prv = open_bhash(sip, O_RDONLY, "owner_bhash"); if (!prv || !prv->db) return; db = prv->db; /* There are timing hazards, when the internal fd is not available for probing.. */ #if defined(HAVE_DB2) || defined(HAVE_DB3) || defined(HAVE_DB4) (db->fd)(db, &fd); #else fd = (db->fd)(db); #endif if (fd < 0 || fstat(fd, &stbuf) < 0) { fprintf(stderr, "owner_bhash: cannot fstat(\"%s\")!\n", sip->file); return; } fprintf(outfp, "%d\n", stbuf.st_uid); fflush(outfp); } int modp_bhash(sip) search_info *sip; { DB *db; struct stat stbuf; int rval, fd = -1, err = 0; int roflag = O_RDONLY; ZSleepyPrivate *prv; prv = open_bhash(sip, roflag, "owner_bhash"); /* if it isn't open.. */ if (!prv || !prv->db) return 0; roflag = prv->roflag; db = prv->db; #if defined(HAVE_DB2) || defined(HAVE_DB3) || defined(HAVE_DB4) err = (db->fd)(db, &fd); #else fd = (db->fd)(db); #endif if (fstat(fd, &stbuf) < 0) { fprintf(stderr, "modp_bhash: cannot fstat(\"%s\"(%d))! err=%d\n", sip->file, fd, err); return 0; } if (stbuf.st_nlink == 0) return 1; /* Unlinked underneath of us! */ if (roflag != O_RDONLY) return 0; /* We are a WRITER ?? Of course it changes.. */ rval = (stbuf.st_mtime != prv->mtime || stbuf.st_nlink != 1); prv->mtime = stbuf.st_mtime; return rval; } #endif /* HAVE_DB */