/* Copyright 2000, 2001, 2002, 2004 Laurent Wacrenier This file is part of libhome libhome is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. libhome 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with libhome; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" static char const rcsid[] UNUSED = "$Id: cache3.c,v 1.18 2005/04/11 11:18:34 lwa Exp $"; #include #include #include #include #include #include #include #include #include #include #include "hparam.h" #define BUFCACHE 1024 extern struct param home_param; static DB_ENV *dbenv=NULL; static DB *dbp=NULL; /* DBD API change again :-( */ #if DB_VERSION_MAJOR == 3 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR < 3) static void err_callback (const char *errpfx, char *msg) { home_error("%s: %s", errpfx, msg); } #else static void err_callback (const DB_ENV *dbenv, const char *errpfx, const char *msg) { home_error("%s: %s", errpfx, msg); } #endif static DB *opencache(void) { extern int hparam_done; char cachedir[PATH_MAX]; char *cachefile; int ret; if (dbp) return dbp; if (!hparam_done) { home_init(NULL); } if (!home_param.cachefile || *(home_param.cachefile) != '/' ) { return NULL; } cachefile = strrchr(home_param.cachefile, '/'); if (cachefile > home_param.cachefile && cachefile[-1] == '/') { snprintf(cachedir, PATH_MAX, "%.*s/%d.%d.%d", (int)(cachefile-home_param.cachefile), home_param.cachefile, DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH); } else { snprintf(cachedir, PATH_MAX, "%.*s", (int)(cachefile-home_param.cachefile), home_param.cachefile); } cachefile++; if (!dbenv) { if ((ret = db_env_create(&dbenv, 0)) != 0) { home_error("env create: %s", db_strerror(ret)); return NULL; } dbenv->set_errpfx(dbenv, "cache"); dbenv->set_errcall(dbenv, err_callback); if (home_param.cachesize > 0 && (ret = dbenv->set_cachesize(dbenv, 0, home_param.cachesize * 1024, 0)) != 0) { home_error("set cachesize: %s", db_strerror(ret)); dbenv->close(dbenv, 0); dbenv=NULL; return NULL; } if (home_param.cachelockers > 0) { #if DB_VERSION_MAJOR == 3 dbenv->set_lk_max(dbenv, home_param.cachelockers); #else dbenv->set_lk_max_lockers(dbenv, home_param.cachelockers); dbenv->set_lk_max_locks(dbenv, home_param.cachelockers); dbenv->set_lk_max_objects(dbenv, home_param.cachelockers); #endif } mkdir(cachedir, 0700); if ((ret = dbenv->open(dbenv, cachedir, DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL, 0))!=0) { home_error("env open '%s': %s", cachedir, db_strerror(ret)); dbenv->close(dbenv, 0); dbenv=NULL; return NULL; } } if ((ret = db_create(&dbp, dbenv, 0)) != 0) { home_error("db create %s", db_strerror(ret)); return NULL; } #if DB_VERSION_MAJOR == 3 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 0) if ((ret = dbp->open(dbp, cachefile, NULL, DB_HASH, DB_CREATE, 0600))!= 0) #else if ((ret = dbp->open(dbp, NULL, cachefile, NULL, DB_HASH, DB_CREATE, 0600)) != 0) #endif { dbp->close(dbp, 0); dbp=NULL; home_error("db open %s/%s: %s", cachedir, cachefile, db_strerror(ret)); return NULL; } return dbp; } void cache_clean(void) { if (dbp) { dbp->close(dbp, 0); dbp=NULL; } if (dbenv) { dbenv->close(dbenv, 0); dbenv=NULL; } } #define STORESTRING(e) if (e) { char *t = e; while ((*p++ = *t++)); } else { *p++=0; } #define STOREINT(e) memmove(p, &(e), sizeof(e)); p+=sizeof(e); void storecache(char *name, struct passwd *pwd) { char buf[BUFCACHE]; time_t stamp; char *p=buf; DBT key={}, value={}; #ifdef DBLOCK DB_LOCK lock={}; #endif if (opencache()==NULL) { return; } time(&stamp); STOREINT(stamp); STORESTRING(pwd->pw_name); STORESTRING(pwd->pw_passwd); STOREINT(pwd->pw_uid); STOREINT(pwd->pw_gid); #ifdef HAS_PW_QUOTA STOREINT(pwd->pw_quota); #endif #ifdef HAS_PW_CLASS STORESTRING(pwd->pw_class); #endif STORESTRING(pwd->pw_gecos); STORESTRING(pwd->pw_dir); STORESTRING(pwd->pw_shell); #ifdef HAS_PW_EXPIRE STOREINT(pwd->pw_expire); #endif key.data=name; key.size=strlen(key.data); value.data=buf; value.size=p-buf; dbp->put(dbp, NULL, &key, &value, 0); #if 0 dbp->sync(dbp, 0); #endif } #define EXPAND(e) { size_t l = strlen(p)+1; if ((e=malloc(l))==NULL) return NULL ; memmove(e, p, l); p+=l ; } /* #define EXPAND(e) e = p; while ( *p++ ); */ #define SCALAR(v) memmove(&(v), p, sizeof v); p += sizeof v static struct passwd *decodedata(char *p) { struct passwd *pwd=home_getpwd(); /* time_t stamp; SCALAR(stamp); if (timeout>0 && stamppw_name); EXPAND(pwd->pw_passwd); SCALAR(pwd->pw_uid); SCALAR(pwd->pw_gid); #ifdef HAS_PW_QUOTA SCALAR(pwd->pw_quota); #endif #ifdef HAS_PW_CLASS EXPAND(pwd->pw_class); #endif EXPAND(pwd->pw_gecos); EXPAND(pwd->pw_dir); EXPAND(pwd->pw_shell); #ifdef HAS_PW_EXPIRE SCALAR(pwd->pw_expire); #endif return pwd; } struct passwd *retrfromcache(char *name, int timeout) { DBT key={}, value={}; struct passwd *pwd; int ret; if (opencache()==NULL) { return NULL; } key.data=name; key.size=strlen(key.data); ret=dbp->get(dbp, NULL, &key, &value, 0); if (ret==0) { char *p = value.data; time_t stamp; memmove(&stamp, p, sizeof stamp); p += sizeof stamp; if (timeout > 0 && stamp < time(NULL) - timeout) { pwd = NULL; } else { pwd=decodedata(p); } if (pwd == NULL && home_param.cacherevivettl > 0 && (home_param.cacherevivettl <= timeout || /* plain timeout */ stamp < time(NULL) - home_param.cacherevivettl)) { dbp->del(dbp, NULL, &key, 0); } return pwd; } return NULL; } void expire_cache(char *tag) { int ret; DBC *dbcp; DBT key = {}; DBT value = {}; if (opencache()==NULL) { return; } if ( 0 != (ret= #if DB_VERSION_MAJOR == 3 lock_detect(dbenv, 0, DB_LOCK_DEFAULT, NULL) #else dbenv->lock_detect(dbenv, 0, DB_LOCK_DEFAULT, NULL) #endif )) { fprintf(stderr, "lock_detect: %s\n", db_strerror(ret)); return; } home_blocsignal(1); if ((ret = dbp->cursor(dbp, NULL, &dbcp, DB_WRITECURSOR)) != 0) { dbp->err(dbp, ret, "DB->cursor"); return; } if ((ret = dbcp->c_get(dbcp, &key, &value, DB_FIRST))==0) { do { if (decodedata(value.data)==NULL) { printf("expire(%s): %.*s\n", tag, (int)key.size, (char *)key.data); dbcp->c_del(dbcp, 0); } } while (dbcp->c_get(dbcp, &key, &value, DB_NEXT) == 0); } else { if (ret != DB_NOTFOUND) dbp->err(dbp, ret, "DBC->get FIRST"); } dbcp->c_close(dbcp); home_blocsignal(0); return; }