/* $Id: dbexp.c,v 1.5 2005/09/20 19:13:03 dm Exp $ */ /* * * Copyright (C) 2004 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "avutil.h" #include "dbexp.h" #define RECOVLK "recov.lock" static int _dbenv_init_lockfd (dbenv *dbe, const char *path) { struct stat sb; char *pb; mode_t m; if (!stat (path, &sb)) { if (!S_ISDIR (sb.st_mode)) { fprintf (stderr, "%s: not a directory\n", path); return ENOTDIR; } } else if (errno != ENOENT) { perror (path); return errno; } else if (mkdir (path, 0700)) { perror (path); return errno; } pb = xmalloc (strlen (path) + sizeof (RECOVLK) + 1); sprintf (pb, "%s/%s", path, RECOVLK); m = umask (0); umask (m); if (m & 02) m |= 04; if (m & 020) m |= 040; dbe->lockfd = open (pb, O_CREAT|O_RDWR, 0666 & ~m); if (dbe->lockfd < 0) { perror (pb); free (pb); return errno; } free (pb); return 0; } dbenv * dbenv_alloc (const char *path) { int err; int recover = DB_RECOVER; dbenv *dbe = xmalloc (sizeof (*dbe)); bzero (dbe, sizeof (*dbe)); err = db_env_create (&dbe->e, 0); if (err) { fprintf (stderr, "db_env_create: %s\n", db_strerror (err)); return NULL; } if (!path) { err = dbe->e->open (dbe->e, NULL, DB_PRIVATE|DB_CREATE|DB_INIT_MPOOL, 0666); if (!err) return dbe; fprintf (stderr, "dbenv_open: %s\n", db_strerror (err)); dbe->e->close (dbe->e, 0); free (dbe); return NULL; } if (_dbenv_init_lockfd (dbe, path)) { dbe->e->close (dbe->e, 0); free (dbe); return NULL; } if (flock (dbe->lockfd, LOCK_EX|LOCK_NB)) { recover = 0; if (flock (dbe->lockfd, LOCK_SH)) { fprintf (stderr, "%s/%s: %s\n", path, RECOVLK, strerror (errno)); close (dbe->lockfd); dbe->e->close (dbe->e, 0); free (dbe); return NULL; } } #if 0 /* For debugging: */ err = dbe->e->set_lg_bsize (dbe->e, 25 * 1024); if (err) fprintf (stderr, "set_lg_bsize: %s\n", db_strerror (err)); dbe->e->set_lg_max (dbe->e, 100 * 1024); if (err) fprintf (stderr, "set_lg_size: %s\n", db_strerror (err)); #endif err = dbe->e->open (dbe->e, path, (DB_INIT_MPOOL|DB_INIT_LOG|DB_INIT_LOCK|DB_INIT_TXN |recover|DB_CREATE), 0666); if (err) { fprintf (stderr, "%s: %s\n", path, db_strerror (err)); close (dbe->lockfd); dbe->e->close (dbe->e, 0); free (dbe); return NULL; } if (recover) flock (dbe->lockfd, LOCK_SH); dbe->txnflag = DB_AUTO_COMMIT; dbe->home = xmalloc (strlen (path) + 1); strcpy (dbe->home, path); return dbe; } void dbenv_free (dbenv *dbe) { dbe->e->close (dbe->e, 0); close (dbe->lockfd); free (dbe); } int dbenv_txn (DB_TXN **tid, dbenv *dbe) { if (dbe->txnflag) return dbe->e->txn_begin (dbe->e, NULL, tid, 0); *tid = NULL; return 0; } int dbenv_commit (DB_TXN *tid) { if (tid) return tid->commit (tid, 0); return 0; } int dbexp_clean (dbexp *dbx) { u_int32_t now = time (NULL); DB_TXN *tid; int i; int err = 0; DBC *c; DBT k, v; while (!err) { if ((err = dbenv_txn (&tid, dbx->dbe))) { fprintf (stderr, "%s: %s\n", dbx->path, db_strerror (err)); return err; } if ((err = dbx->exp->cursor (dbx->exp, tid, &c, 0))) { fprintf (stderr, "%s: %s\n", dbx->path, db_strerror (err)); dbenv_commit (tid); return err; } bzero (&k, sizeof (k)); bzero (&v, sizeof (v)); err = c->c_get (c, &k, &v, DB_FIRST); for (i = 0; !err && i < 20; i++) { if (k.size >= 4) { if (getint (k.data) > now) err = DB_NOTFOUND; else err = c->c_del (c, 0); } if (!err) err = c->c_get (c, &k, &v, DB_NEXT); } if (err && err != DB_NOTFOUND) fprintf (stderr, "%s: %s\n", dbx->path, db_strerror (err)); c->c_close (c); if (!err) err = dbenv_commit (tid); else dbenv_commit (tid); } if (dbx->dbe->home) { #ifdef DB_ARCH_REMOVE dbx->dbe->e->log_archive (dbx->dbe->e, NULL, DB_ARCH_REMOVE); #else /* !DB_ARCH_REMOVE */ int i; char **paths = NULL; int err = dbx->dbe->e->log_archive (dbx->dbe->e, &paths, DB_ARCH_ABS); if (err) fprintf (stderr, "%s: log_archive: %s\n", dbx->path, db_strerror (err)); else if (paths) { for (i = 0; paths[i]; i++) unlink (paths[i]); free (paths); } #endif /* !DB_ARCH_REMOVE */ } if (err == DB_NOTFOUND) return 0; return err; } int dbexp_free (dbexp *dbx, int sync) { int ret = 0; int err; if (dbx->exp) { err = dbx->exp->close (dbx->exp, 0); if (err) { fprintf (stderr, "%s: %s\n", dbx->path, db_strerror (err)); ret = err; } } if (dbx->db) { if (sync && !ret && !dbx->dbe->txnflag) { err = dbx->db->sync (dbx->db, 0); if (!ret && err) { fprintf (stderr, "%s; %s\n", dbx->path, db_strerror (err)); ret = err; } } err = dbx->db->close (dbx->db, 0); if (!ret && err) { fprintf (stderr, "%s: %s\n", dbx->path, db_strerror (err)); ret = err; } } free (dbx->path); free (dbx); return ret; } static int _dbexp_getexp (DB *exp, const DBT *pkey, const DBT *pdata, DBT *skey) { if (pdata->size < 4) return EINVAL; skey->data = pdata->data; skey->size = 4; return 0; } dbexp * dbexp_alloc (dbenv *dbe, const char *path, int rdonly) { int err; int fd; dbexp *dbx = malloc (sizeof (*dbx)); bzero (dbx, sizeof (*dbx)); dbx->dbe = dbe; dbx->path = xmalloc (strlen (path) + 1); strcpy (dbx->path, path); err = db_create (&dbx->db, dbx->dbe->e, 0); if (!err) err = dbx->db->set_flags(dbx->db, DB_CHKSUM); if (!rdonly) { if (!err) err = db_create (&dbx->exp, dbx->dbe->e, 0); if (!err) err = dbx->exp->set_flags(dbx->exp, DB_DUPSORT); } if (err) { fprintf (stderr, "db_create: %s\n", db_strerror (err)); dbexp_free (dbx, 0); return NULL; } err = dbx->db->open (dbx->db, NULL, dbx->path, "db", DB_BTREE, rdonly ? DB_RDONLY : dbx->dbe->txnflag|DB_CREATE, 0666); if (!err) err = dbx->db->fd (dbx->db, &fd); if (err) { fprintf (stderr, "%s: %s\n", dbx->path, db_strerror (err)); dbexp_free (dbx, 0); return NULL; } if (flock (fd, rdonly ? LOCK_SH : LOCK_EX) < 0) { fprintf (stderr, "flock (%s): %s\n", dbx->path, strerror (errno)); dbexp_free (dbx, 0); return NULL; } if (!rdonly) { err = dbx->exp->open (dbx->exp, NULL, dbx->path, "exp", DB_BTREE, dbx->dbe->txnflag|DB_CREATE, 0666); if (!err) err = dbx->db->associate (dbx->db, NULL, dbx->exp, _dbexp_getexp, dbx->dbe->txnflag); if (err) { fprintf (stderr, "%s: %s\n", dbx->path, db_strerror (err)); dbexp_free (dbx, 0); return NULL; } } return dbx; }