From 1409096860f185f51adc9632011ecd9112907b70 Mon Sep 17 00:00:00 2001 From: Jon Meredith Date: Fri, 29 May 2009 12:13:19 -0600 Subject: [PATCH] Renamed get_lg_dir() to get_lg_dir_info and added get_data_dirs_info(). Both new calls return the filesystem id and the number of mbytes available on that filesystem. --- c_src/bdberl_drv.c | 170 ++++++++++++++++++++++++++++++++++++++++++ c_src/bdberl_drv.h | 3 + c_src/bin_helper.c | 4 + include/bdberl.hrl | 3 + src/bdberl.erl | 76 ++++++++++++++++++- test/bdberl_SUITE.erl | 12 ++- 6 files changed, 266 insertions(+), 2 deletions(-) diff --git a/c_src/bdberl_drv.c b/c_src/bdberl_drv.c index 8b53950..398fa55 100644 --- a/c_src/bdberl_drv.c +++ b/c_src/bdberl_drv.c @@ -9,12 +9,15 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include #include "hive_hash.h" #include "bdberl_drv.h" @@ -40,7 +43,9 @@ static void do_async_log_stat(void* arg); static void do_async_memp_stat(void* arg); static void do_async_mutex_stat(void* arg); static void do_async_txn_stat(void* arg); +static void do_sync_data_dirs_info(PortData *p); +static int send_dir_info(ErlDrvPort port, ErlDrvTermData pid, const char *path); static void send_rc(ErlDrvPort port, ErlDrvTermData pid, int rc); static int add_dbref(PortData* data, int dbref); @@ -1039,6 +1044,44 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd, int rc = G_DB_ENV->txn_stat_print(G_DB_ENV, flags); RETURN_INT(rc, outbuf); } + case CMD_DATA_DIRS_INFO: + { + FAIL_IF_ASYNC_PENDING(d, outbuf); + + do_sync_data_dirs_info(d); + + // Let caller know that the operation is in progress + // Outbuf is: <<0:32>> + RETURN_INT(0, outbuf); + } + + case CMD_LOG_DIR_INFO: + { + FAIL_IF_ASYNC_PENDING(d, outbuf); + + // Find the log dir or use DB_HOME - error if not present + const char *lg_dir = NULL; + int rc = G_DB_ENV->get_lg_dir(G_DB_ENV, &lg_dir); + if (0 == rc && NULL == lg_dir) + { + rc = G_DB_ENV->get_home(G_DB_ENV, &lg_dir); + } + // Send info if we can get a dir, otherwise return the error + if (0 == rc) + { + // send a dirinfo message - will send an error message on a NULL lg_dir + send_dir_info(d->port, d->port_owner, lg_dir); + } + else + { + send_rc(d->port, d->port_owner, rc); + } + + // Let caller know that the operation is in progress + // Outbuf is: <<0:32>> + RETURN_INT(0, outbuf); + } + } *outbuf = 0; @@ -1271,6 +1314,21 @@ static void get_info(int target, void* values, BinHelper* bh) } break; } + case SYSP_LOG_DIR_GET: + { + const char* dir = 0; + // Get the log dir - according to BDB docs, if not set + // the DB_HOME is used. + int rc = G_DB_ENV->get_lg_dir(G_DB_ENV, &dir); + if (NULL == dir) + { + dir = getenv("DB_HOME"); + } + bin_helper_init(bh); + bin_helper_push_int32(bh, rc); + bin_helper_push_string(bh, dir); + break; + } } } @@ -1328,6 +1386,53 @@ static char *rc_to_atom_str(int rc) } +// Send a {dirinfo, Path, FsId, MbyteAvail} message to pid given. +// Send an {errno, Reason} on failure +// returns 0 on success, errno on failure +static int send_dir_info(ErlDrvPort port, ErlDrvTermData pid, const char *path) +{ + struct statvfs svfs; + int rc; + + if (NULL == path) + { + rc = EINVAL; + } + else if (0 != statvfs(path, &svfs)) + { + rc = errno; + } + else + { + rc = 0; + } + + if (0 != rc) + { + send_rc(port, pid, rc); + } + else + { + fsblkcnt_t blocks_per_mbyte = 1024 * 1024 / svfs.f_frsize; + assert(blocks_per_mbyte > 0); + unsigned int mbyte_avail = (unsigned int) (svfs.f_bavail / blocks_per_mbyte); + int path_len = strlen(path); + + ErlDrvTermData response[] = { ERL_DRV_ATOM, driver_mk_atom("dirinfo"), + ERL_DRV_STRING, (ErlDrvTermData) path, path_len, + // send fsid as a binary as will only be used + // to compare which physical filesystem is on + // and the definintion varies between platforms. + ERL_DRV_BUF2BINARY, (ErlDrvTermData) &svfs.f_fsid, + sizeof(svfs.f_fsid), + ERL_DRV_UINT, mbyte_avail, + ERL_DRV_TUPLE, 4}; + driver_send_term(port, pid, response, sizeof(response) / sizeof(response[0])); + } + return rc; +} + + static void send_rc(ErlDrvPort port, ErlDrvTermData pid, int rc) { // TODO: May need to tag the messages a bit more explicitly so that if another async @@ -2334,6 +2439,71 @@ static void do_async_txn_stat(void* arg) } } + +static void do_sync_data_dirs_info(PortData *d) +{ + // Get DB_HOME and find the real path + const char *db_home = NULL; + const char *data_dir = NULL; + const char **data_dirs = NULL; + char db_home_realpath[PATH_MAX+1]; + char data_dir_realpath[PATH_MAX+1]; + int got_db_home = 0; + + // Lookup the environment and add it if not explicitly included in the data_dirs + int rc = G_DB_ENV->get_home(G_DB_ENV, &db_home); + if (rc != 0 || NULL == db_home) + { + // If no db_home we'll have to rely on whatever the global environment is configured with + got_db_home = 1; + } + else + { + if (NULL == realpath(db_home, db_home_realpath)) + rc = errno; + } + + // Get the data first + rc = G_DB_ENV->get_data_dirs(G_DB_ENV, &data_dirs); + int i; + for (i = 0; 0 == rc && NULL != data_dirs && NULL != data_dirs[i]; i++) + { + data_dir = data_dirs[i]; + + if (!got_db_home) + { + // Get the real path of the data dir + if (NULL == realpath(data_dir, data_dir_realpath)) + { + rc = errno; + } + else + { + // Set got_db_home if it matches + if (0 == strcmp(data_dir_realpath, db_home_realpath)) + { + got_db_home = 1; + } + } + } + + if (0 == rc) + { + rc = send_dir_info(d->port, d->port_owner, data_dir); + } + } + + // BDB always searches the environment home too so add it to the list + if (!got_db_home && rc == 0) + { + rc = send_dir_info(d->port, d->port_owner, db_home); + } + + // Send the return code - will termiante the receive loop in bdberl.erl + send_rc(d->port, d->port_owner, rc); +} + + static void* zalloc(unsigned int size) { void* res = driver_alloc(size); diff --git a/c_src/bdberl_drv.h b/c_src/bdberl_drv.h index e22d60e..e282b0d 100644 --- a/c_src/bdberl_drv.h +++ b/c_src/bdberl_drv.h @@ -59,6 +59,8 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd, #define CMD_MUTEX_STAT_PRINT 28 #define CMD_TXN_STAT 29 #define CMD_TXN_STAT_PRINT 30 +#define CMD_DATA_DIRS_INFO 31 +#define CMD_LOG_DIR_INFO 32 /** * Command status values @@ -96,6 +98,7 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd, #define SYSP_CACHESIZE_GET 1 #define SYSP_TXN_TIMEOUT_GET 2 #define SYSP_DATA_DIR_GET 3 +#define SYSP_LOG_DIR_GET 4 /** * Driver Entry diff --git a/c_src/bin_helper.c b/c_src/bin_helper.c index beedafb..bc1c0d2 100644 --- a/c_src/bin_helper.c +++ b/c_src/bin_helper.c @@ -44,6 +44,10 @@ void bin_helper_push_int32(BinHelper* bh, int value) void bin_helper_push_string(BinHelper* bh, const char* string) { + if (NULL == string) + { + string = ""; + } int sz = strlen(string); bin_helper_check_size(bh, sz+1); strncpy(bh->bin->orig_bytes+(bh->offset), string, sz+1); diff --git a/include/bdberl.hrl b/include/bdberl.hrl index 4836c56..939c741 100644 --- a/include/bdberl.hrl +++ b/include/bdberl.hrl @@ -36,6 +36,8 @@ -define(CMD_MUTEX_STAT_PRINT,28). -define(CMD_TXN_STAT, 29). -define(CMD_TXN_STAT_PRINT, 30). +-define(CMD_DATA_DIRS_INFO, 31). +-define(CMD_LOG_DIR_INFO, 32). -define(DB_TYPE_BTREE, 1). -define(DB_TYPE_HASH, 2). @@ -44,6 +46,7 @@ -define(SYSP_CACHESIZE_GET, 1). -define(SYSP_TXN_TIMEOUT_GET, 2). -define(SYSP_DATA_DIR_GET, 3). +-define(SYSP_LOG_DIR_GET, 4). -define(STATUS_OK, 0). -define(STATUS_ERROR, 1). diff --git a/src/bdberl.erl b/src/bdberl.erl index 0950019..35bd6ae 100644 --- a/src/bdberl.erl +++ b/src/bdberl.erl @@ -13,6 +13,8 @@ txn_commit/0, txn_commit/1, txn_abort/0, get_cache_size/0, get_data_dirs/0, + get_data_dirs_info/0, + get_lg_dir_info/0, get_txn_timeout/0, stat/1, stat/2, stat_print/1, stat_print/2, @@ -48,10 +50,11 @@ -type db_name() :: [byte(),...]. -type db_type() :: btree | hash. -type db_flags() :: [atom()]. +-type db_fsid() :: binary(). -type db_key() :: term(). +-type db_mbytes() :: non_neg_integer(). -type db_value() :: term(). -type db_ret_value() :: not_found | db_value(). - -type db_error_reason() :: atom() | {unknown, integer()}. -type db_error() :: {error, db_error_reason()}. @@ -1267,6 +1270,63 @@ get_data_dirs() -> end. +%%-------------------------------------------------------------------- +%% @doc +%% Returns the list of directories that bdberl searches to find databases +%% with the number of megabytes available for each dir +%% +%% @spec get_data_dirs_info() -> {ok, [DirName, Fsid, MbytesAvail]} | {error, Error} +%% where +%% DirName = string() +%% Fsid = binary() +%% MbytesAvail = integer() +%% +%% @end +%%-------------------------------------------------------------------- +-spec get_data_dirs_info() -> {ok, [{string(), db_fsid(), db_mbytes()}]} | db_error(). + +get_data_dirs_info() -> + % Call into the BDB library and get a list of configured data directories + Cmd = <<>>, + <> = erlang:port_control(get_port(),?CMD_DATA_DIRS_INFO, Cmd), + case decode_rc(Result) of + ok -> + recv_dirs_info([]); + Reason -> + {error, Reason} + end. + + +%%-------------------------------------------------------------------- +%% @doc +%% Returns the log directory info (name and megabytes available) +%% +%% @spec get_lg_dir_info() -> {ok, DirName, Fsid, MbytesAvail} | {error, Error} +%% where +%% DirName = string() +%% Fsid = binary() +%% MbytesAvail = integer() +%% +%% @end +%%-------------------------------------------------------------------- +-spec get_lg_dir_info() -> {ok, string(), db_fsid(), db_mbytes()} | db_error(). +get_lg_dir_info() -> + % Call into the BDB library and get the log dir and filesystem info + Cmd = <<>>, + <> = erlang:port_control(get_port(), ?CMD_LOG_DIR_INFO, Cmd), + case decode_rc(Result) of + ok -> + receive + {dirinfo, Path, FsId, MbytesAvail} -> + {ok, Path, FsId, MbytesAvail}; + {error, Reason} -> + {error, Reason} + end; + Reason -> + {error, Reason} + end. + + %%-------------------------------------------------------------------- %% @doc %% Returns the size of the in-memory cache. @@ -2196,3 +2256,17 @@ recv_txn_stat(Tstats) -> {ok, Stats} -> {ok, Stats, Tstats} end. + +%% +%% Receive directory info messages until ok +%% +recv_dirs_info(DirInfos) -> + receive + {dirinfo, Path, FsId, MbytesAvail} -> + recv_dirs_info([{Path, FsId, MbytesAvail} | DirInfos]); + {error, Reason} -> + {error, Reason}; + ok -> + {ok, DirInfos} + end. + diff --git a/test/bdberl_SUITE.erl b/test/bdberl_SUITE.erl index 57caf4f..82b0830 100644 --- a/test/bdberl_SUITE.erl +++ b/test/bdberl_SUITE.erl @@ -39,7 +39,10 @@ all() -> log_stat_should_report_on_success, memp_stat_should_report_on_success, mutex_stat_should_report_on_success, - txn_stat_should_report_on_success]. + txn_stat_should_report_on_success, + data_dirs_info_should_report_on_success, + lg_dir_info_should_report_on_success]. + dbconfig(Config) -> @@ -312,3 +315,10 @@ txn_stat_should_report_on_success(_Config) -> bdberl:txn_abort(), {ok, _GStat3, []} = bdberl:txn_stat([]), done. + +data_dirs_info_should_report_on_success(_Config) -> + {ok, _DataDirs} = bdberl:get_data_dirs_info(). + +lg_dir_info_should_report_on_success(_Config) -> + {ok, _LgDir, _Fsid, _MBytesAvail} = bdberl:get_lg_dir_info(). +