From acd207af1bf340df75a64e8bc4d1e93caa13bbb2 Mon Sep 17 00:00:00 2001 From: Amos Jeffries Date: Tue, 17 Jul 2018 23:36:31 +0000 Subject: [PATCH] TrivialDB support (#223) Allow use of Samba TrivialDB instead of outdated BerkleyDB in the session helper. Require TrivialDB support for use of the time_quota helper. libdb v1.85 is no longer supported by distributors and upgrading to v5 only to deprecate use does not seem to be worthwhile. --- acinclude/tdb.m4 | 49 ++++++++ configure.ac | 1 + src/acl/external/session/Makefile.am | 3 +- src/acl/external/session/ext_session_acl.cc | 143 ++++++++++++++++++---- src/acl/external/session/required.m4 | 26 ++-- src/acl/external/time_quota/Makefile.am | 2 +- src/acl/external/time_quota/ext_time_quota_acl.cc | 93 +++++++------- src/acl/external/time_quota/required.m4 | 13 +- test-suite/buildtests/layer-01-minimal.opts | 1 + test-suite/buildtests/layer-02-maximus.opts | 1 + 11 files changed, 253 insertions(+), 86 deletions(-) create mode 100644 acinclude/tdb.m4 diff --git a/acinclude/tdb.m4 b/acinclude/tdb.m4 new file mode 100644 index 0000000..20b2b2a --- /dev/null +++ b/acinclude/tdb.m4 @@ -0,0 +1,49 @@ +## Copyright (C) 1996-2018 The Squid Software Foundation and contributors +## +## Squid software is distributed under GPLv2+ license and includes +## contributions from numerous individuals and organizations. +## Please see the COPYING and CONTRIBUTORS files for details. +## + +dnl check for --with-tdb option +AC_DEFUN([SQUID_CHECK_LIBTDB],[ +AC_ARG_WITH(tdb, + AS_HELP_STRING([--without-tdb], + [Do not use Samba TrivialDB. Default: auto-detect]), [ +case "$with_tdb" in + yes|no|auto) + : # Nothing special to do here + ;; + *) + AS_IF([test ! -d "$withval"], + AC_MSG_ERROR([--with-tdb path ($with_tdb) does not point to a directory]) + ) + LIBTDB_PATH="-L$withval/lib" + CPPFLAGS="-I$withval/include $CPPFLAGS" + ;; +esac +]) +AH_TEMPLATE(USE_TRIVIALDB,[Samba TrivialDB support is available]) +AS_IF([test "x$with_tdb" != "xno"],[ + SQUID_STATE_SAVE(squid_libtdb_state) + LIBS="$LIBS $LIBTDB_PATH" + PKG_CHECK_MODULES([LIBTDB],[tdb],[CPPFLAGS="$CPPFLAGS $LIBTDB_CFLAGS"],[:]) + AC_CHECK_HEADERS([sys/stat.h tdb.h],,,[ +#if HAVE_SYS_STAT_H +#include +#endif + ]) + SQUID_STATE_ROLLBACK(squid_libtdb_state) #de-pollute LIBS + + AS_IF([test "x$with_tdb" = "xyes" -a "x$LIBTDB_LIBS" = "x"], + AC_MSG_ERROR([Required TrivialDB library not found]) + ) + AS_IF([test "x$LIBTDB_LIBS" != "x"],[ + CXXFLAGS="$LIBTDB_CFLAGS $CXXFLAGS" + LIBTDB_LIBS="$LIBTDB_PATH $LIBTDB_LIBS" + AC_DEFINE_UNQUOTED(USE_TRIVIALDB, HAVE_TDB_H, [Samba TrivialDB support is available]) + ],[with_tdb=no]) +]) +AC_MSG_NOTICE([Samba TrivialDB library support: ${with_tdb:=auto} ${LIBTDB_PATH} ${LIBTDB_LIBS}]) +AC_SUBST(LIBTDB_LIBS) +]) diff --git a/configure.ac b/configure.ac index 1ec245a..c8cd996 100644 --- a/configure.ac +++ b/configure.ac @@ -23,6 +23,7 @@ m4_include([acinclude/os-deps.m4]) m4_include([acinclude/krb5.m4]) m4_include([acinclude/pam.m4]) m4_include([acinclude/pkg.m4]) +m4_include([acinclude/tdb.m4]) m4_include([acinclude/lib-checks.m4]) m4_include([acinclude/ax_cxx_compile_stdcxx_11.m4]) m4_include([acinclude/ax_cxx_0x_types.m4]) diff --git a/src/acl/external/session/Makefile.am b/src/acl/external/session/Makefile.am index e54f22b..7509848 100644 --- a/src/acl/external/session/Makefile.am +++ b/src/acl/external/session/Makefile.am @@ -14,6 +14,7 @@ ext_session_acl_SOURCES= \ ext_session_acl.cc ext_session_acl_LDADD = \ $(COMPAT_LIB) \ - -ldb + $(LIBBDB_LIBS) \ + $(LIBTDB_LIBS) EXTRA_DIST= ext_session_acl.8 required.m4 diff --git a/src/acl/external/session/ext_session_acl.cc b/src/acl/external/session/ext_session_acl.cc index 09a35ca..64ffb60 100644 --- a/src/acl/external/session/ext_session_acl.cc +++ b/src/acl/external/session/ext_session_acl.cc @@ -43,6 +43,9 @@ #endif #include #include +#if HAVE_TDB_H +#include +#endif #if HAVE_UNISTD_H #include #endif @@ -60,8 +63,36 @@ static int fixed_timeout = 0; char *db_path = NULL; const char *program_name; +#if USE_BERKLEYDB DB *db = NULL; DB_ENV *db_env = NULL; +typedef DBT DB_ENTRY; + +#elif USE_TRIVIALDB +TDB_CONTEXT *db = nullptr; +typedef TDB_DATA DB_ENTRY; + +#endif + +static void +shutdown_db() +{ + if (db) { +#if USE_BERKLEYDB + db->close(db, 0); + } + if (db_env) { + db_env->close(db_env, 0); + +#elif USE_TRIVIALDB + if (tdb_close(db) != 0) { + fprintf(stderr, "%s| WARNING: error closing session db '%s'\n", program_name, db_path); + exit(1); + } +#endif + } + xfree(db_path); +} static void init_db(void) { @@ -70,6 +101,7 @@ static void init_db(void) if (db_path) { if (!stat(db_path, &st_buf)) { if (S_ISDIR (st_buf.st_mode)) { +#if USE_BERKLEYDB /* If directory then open database environment. This prevents sync problems between different processes. Otherwise fallback to single file */ db_env_create(&db_env, 0); @@ -79,10 +111,16 @@ static void init_db(void) exit(1); } db_create(&db, db_env, 0); +#elif USE_TRIVIALDB + std::string newPath(db_path); + newPath.append("session", 7); + db_path = xstrdup(newPath.c_str()); +#endif } } } +#if USE_BERKLEYDB if (db_env) { if (db->open(db, NULL, "session", NULL, DB_BTREE, DB_CREATE, 0666)) { fprintf(stderr, "FATAL: %s: Failed to open db file '%s' in dir '%s'\n", @@ -93,60 +131,121 @@ static void init_db(void) } else { db_create(&db, NULL, 0); if (db->open(db, NULL, db_path, NULL, DB_BTREE, DB_CREATE, 0666)) { - fprintf(stderr, "FATAL: %s: Failed to open session db '%s'\n", program_name, db_path); - exit(1); + db = nullptr; } } +#elif USE_TRIVIALDB + db = tdb_open(db_path, 0, TDB_CLEAR_IF_FIRST, O_CREAT|O_DSYNC, 0666); +#endif + if (!db) { + fprintf(stderr, "FATAL: %s: Failed to open session db '%s'\n", program_name, db_path); + shutdown_db(); + exit(1); + } } -static void shutdown_db(void) +int session_is_active = 0; + +static size_t +dataSize(DB_ENTRY *data) { - db->close(db, 0); - if (db_env) { - db_env->close(db_env, 0); - } +#if USE_BERKLEYDB + return data->size; +#elif USE_TRIVIALDB + return data->dsize; +#endif } -int session_is_active = 0; +static bool +fetchKey(/*const*/ DB_ENTRY &key, DB_ENTRY *data) +{ +#if USE_BERKLEYDB + return (db->get(db, nullptr, &key, data, 0) == 0); +#elif USE_TRIVIALDB + // NP: API says returns NULL on errors, but return is a struct type WTF?? + *data = tdb_fetch(db, key); + return (data->dptr != nullptr); +#endif +} + +static void +deleteEntry(/*const*/ DB_ENTRY &key) +{ +#if USE_BERKLEYDB + db->del(db, nullptr, &key, 0); +#elif USE_TRIVIALDB + tdb_delete(db, key); +#endif +} + +static void +copyValue(void *dst, const DB_ENTRY *src, size_t sz) +{ +#if USE_BERKLEYDB + memcpy(dst, src->data, sz); +#elif USE_TRIVIALDB + memcpy(dst, src->dptr, sz); +#endif +} static int session_active(const char *details, size_t len) { +#if USE_BERKLEYDB DBT key = {0}; DBT data = {0}; key.data = (void *)details; key.size = len; - if (db->get(db, NULL, &key, &data, 0) == 0) { +#elif USE_TRIVIALDB + TDB_DATA key; + TDB_DATA data; +#endif + if (fetchKey(key, &data)) { time_t timestamp; - if (data.size != sizeof(timestamp)) { + if (dataSize(&data) != sizeof(timestamp)) { fprintf(stderr, "ERROR: %s: CORRUPTED DATABASE (%s)\n", program_name, details); - db->del(db, NULL, &key, 0); + deleteEntry(key); return 0; } - memcpy(×tamp, data.data, sizeof(timestamp)); + copyValue(×tamp, &data, sizeof(timestamp)); if (timestamp + session_ttl >= time(NULL)) return 1; } return 0; } -static void session_login(const char *details, size_t len) +static void +session_login(/*const*/ char *details, size_t len) { - DBT key = {0}; - DBT data = {0}; - key.data = (void *)details; + DB_ENTRY key = {0}; + DB_ENTRY data = {0}; + time_t now = time(0); +#if USE_BERKLEYDB + key.data = static_cast(details); key.size = len; - time_t now = time(NULL); data.data = &now; data.size = sizeof(now); db->put(db, NULL, &key, &data, 0); +#elif USE_TRIVIALDB + key.dptr = reinterpret_cast(details); + key.dsize = len; + data.dptr = reinterpret_cast(&now); + data.dsize = sizeof(now); + tdb_store(db, key, data, 0); +#endif } -static void session_logout(const char *details, size_t len) +static void +session_logout(/*const*/ char *details, size_t len) { - DBT key = {0}; - key.data = (void *)details; + DB_ENTRY key = {0}; +#if USE_BERKLEYDB + key.data = static_cast(details); key.size = len; - db->del(db, NULL, &key, 0); +#elif USE_TRIVIALDB + key.dptr = reinterpret_cast(details); + key.dsize = len; +#endif + deleteEntry(key); } static void usage(void) @@ -173,7 +272,7 @@ int main(int argc, char **argv) session_ttl = strtol(optarg, NULL, 0); break; case 'b': - db_path = optarg; + db_path = xstrdup(optarg); break; case 'a': default_action = 0; diff --git a/src/acl/external/session/required.m4 b/src/acl/external/session/required.m4 index 229774b..1fe8a0e 100755 --- a/src/acl/external/session/required.m4 +++ b/src/acl/external/session/required.m4 @@ -5,11 +5,23 @@ ## Please see the COPYING and CONTRIBUTORS files for details. ## -AC_CHECK_HEADERS(db.h,[ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ - DB_ENV *db_env = nullptr; - db_env_create(&db_env, 0); - ]])],[ +SQUID_CHECK_LIBTDB +if test "$with_tdb" != "no"; then BUILD_HELPER="session" - ],[]) -]) +fi + +LIBBDB_LIBS= +AH_TEMPLATE(USE_BERKLEYDB,[BerkleyDB support is available]) +if test "x$with_tdb" = "xno"; then + AC_CHECK_HEADERS(db.h,[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ + DB_ENV *db_env = nullptr; + db_env_create(&db_env, 0); + ]])],[ + AC_DEFINE_UNQUOTED(USE_BERKLEYDB, HAVE_DB_H, [BerkleyDB support is available]) + BUILD_HELPER="session" + LIBBDB_LIBS="-ldb" + ],[]) + ]) +fi +AC_SUBST(LIBBDB_LIBS) diff --git a/src/acl/external/time_quota/Makefile.am b/src/acl/external/time_quota/Makefile.am index 4684637..20eba22 100644 --- a/src/acl/external/time_quota/Makefile.am +++ b/src/acl/external/time_quota/Makefile.am @@ -16,6 +16,6 @@ ext_time_quota_acl_SOURCES= \ ext_time_quota_acl.cc ext_time_quota_acl_LDADD = \ $(COMPAT_LIB) \ - -ldb + $(LIBTDB_LIBS) EXTRA_DIST= ext_time_quota_acl.8 required.m4 diff --git a/src/acl/external/time_quota/ext_time_quota_acl.cc b/src/acl/external/time_quota/ext_time_quota_acl.cc index 764fa86..8f1bbef 100644 --- a/src/acl/external/time_quota/ext_time_quota_acl.cc +++ b/src/acl/external/time_quota/ext_time_quota_acl.cc @@ -41,19 +41,8 @@ #if HAVE_GETOPT_H #include #endif - -/* At this point all Bit Types are already defined, so we must - protect from multiple type definition on platform where - __BIT_TYPES_DEFINED__ is not defined. - */ -#ifndef __BIT_TYPES_DEFINED__ -#define __BIT_TYPES_DEFINED__ -#endif - -#if HAVE_DB_185_H -#include -#elif HAVE_DB_H -#include +#if HAVE_TDB_H +#include #endif #ifndef DEFAULT_QUOTA_DB @@ -63,7 +52,7 @@ const char *db_path = DEFAULT_QUOTA_DB; const char *program_name; -DB *db = NULL; +TDB_CONTEXT *db = nullptr; #define KEY_LAST_ACTIVITY "last-activity" #define KEY_PERIOD_START "period-start" @@ -147,7 +136,7 @@ static void log_fatal(const char *format, ...) static void init_db(void) { log_info("opening time quota database \"%s\".\n", db_path); - db = dbopen(db_path, O_CREAT | O_RDWR, 0666, DB_BTREE, NULL); + db = tdb_open(db_path, 0, TDB_CLEAR_IF_FIRST, O_CREAT | O_RDWR, 0666); if (!db) { log_fatal("Failed to open time_quota db '%s'\n", db_path); exit(1); @@ -156,52 +145,68 @@ static void init_db(void) static void shutdown_db(void) { - db->close(db); + tdb_close(db); } -static void writeTime(const char *user_key, const char *sub_key, time_t t) +static char *KeyString(int &len, const char *user_key, const char *sub_key) { - char keybuffer[TQ_BUFFERSIZE]; - DBT key, data; + static char keybuffer[TQ_BUFFERSIZE]; + *keybuffer = 0; + + len = snprintf(keybuffer, sizeof(keybuffer), "%s-%s", user_key, sub_key); + if (len < 0) { + log_error("Cannot add entry: %s-%s", user_key, sub_key); + len = 0; - if ( strlen(user_key) + strlen(sub_key) + 1 + 1 > sizeof(keybuffer) ) { + } else if (static_cast(len) >= sizeof(keybuffer)) { log_error("key too long (%s,%s)\n", user_key, sub_key); - } else { - snprintf(keybuffer, sizeof(keybuffer), "%s-%s", user_key, sub_key); + len = 0; + } + + return keybuffer; +} + +static void writeTime(const char *user_key, const char *sub_key, time_t t) +{ + int len = 0; + if (/* const */ char *keybuffer = KeyString(len, user_key, sub_key)) { + + TDB_DATA key, data; + + key.dptr = reinterpret_cast(keybuffer); + key.dsize = len; - key.data = (void *)keybuffer; - key.size = strlen(keybuffer); - data.data = &t; - data.size = sizeof(t); - db->put(db, &key, &data, 0); + data.dptr = reinterpret_cast(&t); + data.dsize = sizeof(t); + + tdb_store(db, key, data, TDB_REPLACE); log_debug("writeTime(\"%s\", %d)\n", keybuffer, t); } } static time_t readTime(const char *user_key, const char *sub_key) { - char keybuffer[TQ_BUFFERSIZE]; - DBT key, data; - time_t t = 0; + int len = 0; + if (/* const */ char *keybuffer = KeyString(len, user_key, sub_key)) { - if ( strlen(user_key) + 1 + strlen(sub_key) + 1 > sizeof(keybuffer) ) { - log_error("key too long (%s,%s)\n", user_key, sub_key); - } else { - snprintf(keybuffer, sizeof(keybuffer), "%s-%s", user_key, sub_key); + TDB_DATA key; + key.dptr = reinterpret_cast(keybuffer); + key.dsize = len; - key.data = (void *)keybuffer; - key.size = strlen(keybuffer); - if (db->get(db, &key, &data, 0) == 0) { - if (data.size != sizeof(t)) { - log_error("CORRUPTED DATABASE (%s)\n", keybuffer); - } else { - memcpy(&t, data.data, sizeof(t)); - } + auto data = tdb_fetch(db, key); + + time_t t = 0; + if (data.dsize != sizeof(t)) { + log_error("CORRUPTED DATABASE (%s)\n", keybuffer); + } else { + memcpy(&t, data.dptr, sizeof(t)); } + log_debug("readTime(\"%s\")=%d\n", keybuffer, t); + return t; } - return t; + return 0; } static void parseTime(const char *s, time_t *secs, time_t *start) @@ -388,8 +393,6 @@ static void processActivity(const char *user_key) log_debug("ERR %s\n", message); SEND_ERR("Time budget exceeded."); } - - db->sync(db, 0); } static void usage(void) diff --git a/src/acl/external/time_quota/required.m4 b/src/acl/external/time_quota/required.m4 index a54daae..c9e52bf 100644 --- a/src/acl/external/time_quota/required.m4 +++ b/src/acl/external/time_quota/required.m4 @@ -5,12 +5,7 @@ ## Please see the COPYING and CONTRIBUTORS files for details. ## -AC_CHECK_HEADERS(db_185.h,[BUILD_HELPER="time_quota"],[ - AC_CHECK_HEADERS(db.h,[ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ - DB *db = dbopen("/tmp", O_CREAT | O_RDWR, 0666, DB_BTREE, NULL); - ]])],[ - BUILD_HELPER="time_quota" - ],[]) - ]) -]) +SQUID_CHECK_LIBTDB +if test "$with_tdb" != "no"; then + BUILD_HELPER="time_quota" +fi -- 1.8.3.1