[SCM] Samba Shared Repository - branch master updated
Volker Lendecke
vlendec at samba.org
Fri Apr 20 09:06:03 MDT 2012
The branch, master has been updated
via 3f00295 s3-dbwrap: dbwrap_watch_record_stored => NT_STATUS_NOT_FOUND is ok...
via 1be6d84 s3-g_lock: Use dbwrap_record_watch_send/recv
via 1b5b38a s3-dbwrap: Add "listwatchers" to dbwrap_tool
via 99fa29a s3-dbwrap: Add dbwrap_record_watch_send/recv
via 61c9750 s3-dbwrap: Add dbwrap_set_stored_callback
via 8e5b11b s3-dbwrap: Add "db_context" to "db_record"
via 0f20ffb s3-dbwrap: Add dbwrap_db_id
via e3de5e4 s3: Fix msg_channel in the cluster case
from 5ca8422 s3: Remove an unused variable
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit 3f00295f5eba1ff9a08cb35656222c78479fae11
Author: Stefan Metzmacher <metze at samba.org>
Date: Thu Apr 19 09:06:40 2012 +0200
s3-dbwrap: dbwrap_watch_record_stored => NT_STATUS_NOT_FOUND is ok...
Autobuild-User: Volker Lendecke <vl at samba.org>
Autobuild-Date: Fri Apr 20 17:05:52 CEST 2012 on sn-devel-104
commit 1be6d849ab9d2c992dfa94419260fc28cf573d87
Author: Volker Lendecke <vl at samba.org>
Date: Wed Feb 15 16:38:43 2012 +0100
s3-g_lock: Use dbwrap_record_watch_send/recv
This simplifies the g_lock implementation. The new implementation tries to
acquire a lock. If that fails due to a lock conflict, wait for the g_lock
record to change. Upon change, just try again. The old logic had to cope with
pending records and an ugly hack into ctdb itself. As a bonus, we now get a
really clean async g_lock_lock_send/recv that can asynchronously wait for a
global lock. This would have been almost impossible to do without the
dbwrap_record_watch infrastructure.
commit 1b5b38a61547a48fa7a51bdd634c880264553d36
Author: Volker Lendecke <vl at samba.org>
Date: Wed Feb 15 16:33:21 2012 +0100
s3-dbwrap: Add "listwatchers" to dbwrap_tool
commit 99fa29ae09da5bd2e860bca914a7314586a27994
Author: Volker Lendecke <vl at samba.org>
Date: Wed Feb 15 15:17:33 2012 +0100
s3-dbwrap: Add dbwrap_record_watch_send/recv
With this API you can asynchronously wait for a record to be modified
commit 61c97506e8ccb878d06edae72f4216ad58b96a9b
Author: Volker Lendecke <vl at samba.org>
Date: Wed Feb 15 15:08:29 2012 +0100
s3-dbwrap: Add dbwrap_set_stored_callback
This is a per-db function that is called whenever some record is modified
commit 8e5b11bc143e8532aeed504e47b881ce53411ce3
Author: Volker Lendecke <vl at samba.org>
Date: Mon Jan 2 13:30:51 2012 +0100
s3-dbwrap: Add "db_context" to "db_record"
commit 0f20ffbcbd41895dd073051539411f7194ae8224
Author: Volker Lendecke <vl at samba.org>
Date: Wed Feb 15 14:57:01 2012 +0100
s3-dbwrap: Add dbwrap_db_id
This returns a blob uniquely identifying the database
commit e3de5e4fb6703976d7592b4dd8a52495a7deb951
Author: Volker Lendecke <vl at samba.org>
Date: Fri Apr 20 11:52:02 2012 +0200
s3: Fix msg_channel in the cluster case
-----------------------------------------------------------------------
Summary of changes:
source3/Makefile.in | 2 +
source3/include/g_lock.h | 5 -
source3/lib/dbwrap/dbwrap.c | 49 +++-
source3/lib/dbwrap/dbwrap.h | 7 +
source3/lib/dbwrap/dbwrap_ctdb.c | 12 +
source3/lib/dbwrap/dbwrap_private.h | 5 +
source3/lib/dbwrap/dbwrap_rbt.c | 8 +
source3/lib/dbwrap/dbwrap_tdb.c | 28 ++
source3/lib/dbwrap/dbwrap_watch.c | 486 +++++++++++++++++++++++++++
source3/lib/dbwrap/dbwrap_watch.h | 46 +++
source3/lib/g_lock.c | 616 ++++++++++-------------------------
source3/lib/msg_channel.c | 9 +-
source3/librpc/idl/messaging.idl | 3 +-
source3/torture/proto.h | 1 +
source3/torture/test_dbwrap_watch.c | 96 ++++++
source3/torture/torture.c | 1 +
source3/utils/dbwrap_tool.c | 71 ++++-
source3/utils/net_g_lock.c | 5 +-
source3/wscript_build | 2 +
19 files changed, 983 insertions(+), 469 deletions(-)
create mode 100644 source3/lib/dbwrap/dbwrap_watch.c
create mode 100644 source3/lib/dbwrap/dbwrap_watch.h
create mode 100644 source3/torture/test_dbwrap_watch.c
Changeset truncated at 500 lines:
diff --git a/source3/Makefile.in b/source3/Makefile.in
index 21b41df..d48d298 100644
--- a/source3/Makefile.in
+++ b/source3/Makefile.in
@@ -263,6 +263,7 @@ TDB_LIB_OBJ = lib/util_tdb.o ../lib/util/util_tdb.o \
lib/dbwrap/dbwrap_ctdb.o \
lib/g_lock.o \
lib/dbwrap/dbwrap_cache.o \
+ lib/dbwrap/dbwrap_watch.o \
lib/dbwrap/dbwrap_rbt.o
TDB_VALIDATE_OBJ = lib/tdb_validate.o
@@ -1280,6 +1281,7 @@ SMBTORTURE_OBJ1 = torture/torture.o torture/nbio.o torture/scanner.o torture/uta
torture/test_ctdbconn.o \
torture/test_msg.o \
torture/test_notify.o \
+ torture/test_dbwrap_watch.o \
torture/t_strappend.o
SMBTORTURE_OBJ = $(SMBTORTURE_OBJ1) $(PARAM_OBJ) $(TLDAP_OBJ) \
diff --git a/source3/include/g_lock.h b/source3/include/g_lock.h
index f4b7809..1ac8418 100644
--- a/source3/include/g_lock.h
+++ b/source3/include/g_lock.h
@@ -29,11 +29,6 @@ enum g_lock_type {
G_LOCK_WRITE = 1,
};
-/*
- * Or'ed with g_lock_type
- */
-#define G_LOCK_PENDING (2)
-
struct g_lock_ctx *g_lock_ctx_init(TALLOC_CTX *mem_ctx,
struct messaging_context *msg);
diff --git a/source3/lib/dbwrap/dbwrap.c b/source3/lib/dbwrap/dbwrap.c
index c78ee74..14562bb 100644
--- a/source3/lib/dbwrap/dbwrap.c
+++ b/source3/lib/dbwrap/dbwrap.c
@@ -67,12 +67,46 @@ TDB_DATA dbwrap_record_get_value(const struct db_record *rec)
NTSTATUS dbwrap_record_store(struct db_record *rec, TDB_DATA data, int flags)
{
- return rec->store(rec, data, flags);
+ NTSTATUS status;
+ struct db_context *db;
+
+ status = rec->store(rec, data, flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ db = rec->db;
+ if (db->stored_callback != NULL) {
+ db->stored_callback(db, rec,
+ db->stored_callback_private_data);
+ }
+ return NT_STATUS_OK;
+}
+
+void dbwrap_set_stored_callback(
+ struct db_context *db,
+ void (*cb)(struct db_context *db, struct db_record *rec,
+ void *private_data),
+ void *private_data)
+{
+ db->stored_callback = cb;
+ db->stored_callback_private_data = private_data;
}
NTSTATUS dbwrap_record_delete(struct db_record *rec)
{
- return rec->delete_rec(rec);
+ NTSTATUS status;
+ struct db_context *db;
+
+ status = rec->delete_rec(rec);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ db = rec->db;
+ if (db->stored_callback != NULL) {
+ db->stored_callback(db, rec,
+ db->stored_callback_private_data);
+ }
+ return NT_STATUS_OK;
}
struct dbwrap_lock_order_state {
@@ -161,6 +195,7 @@ static struct db_record *dbwrap_fetch_locked_internal(
return NULL;
}
(void)talloc_steal(rec, lock_order);
+ rec->db = db;
TALLOC_FREE(frame);
return rec;
}
@@ -183,6 +218,11 @@ struct db_record *dbwrap_try_fetch_locked(struct db_context *db,
? db->try_fetch_locked : db->fetch_locked);
}
+struct db_context *dbwrap_record_get_db(struct db_record *rec)
+{
+ return rec->db;
+}
+
struct dbwrap_fetch_state {
TALLOC_CTX *mem_ctx;
TDB_DATA data;
@@ -351,3 +391,8 @@ int dbwrap_transaction_cancel(struct db_context *db)
{
return db->transaction_cancel(db);
}
+
+void dbwrap_db_id(struct db_context *db, const uint8_t **id, size_t *idlen)
+{
+ db->id(db, id, idlen);
+}
diff --git a/source3/lib/dbwrap/dbwrap.h b/source3/lib/dbwrap/dbwrap.h
index 9981b6d..3304bcf 100644
--- a/source3/lib/dbwrap/dbwrap.h
+++ b/source3/lib/dbwrap/dbwrap.h
@@ -37,6 +37,12 @@ struct db_record *dbwrap_fetch_locked(struct db_context *db,
struct db_record *dbwrap_try_fetch_locked(struct db_context *db,
TALLOC_CTX *mem_ctx,
TDB_DATA key);
+struct db_context *dbwrap_record_get_db(struct db_record *rec);
+void dbwrap_set_stored_callback(
+ struct db_context *db,
+ void (*cb)(struct db_context *db, struct db_record *rec,
+ void *private_data),
+ void *private_data);
NTSTATUS dbwrap_delete(struct db_context *db, TDB_DATA key);
NTSTATUS dbwrap_store(struct db_context *db, TDB_DATA key,
@@ -62,6 +68,7 @@ int dbwrap_get_flags(struct db_context *db);
int dbwrap_transaction_start(struct db_context *db);
int dbwrap_transaction_commit(struct db_context *db);
int dbwrap_transaction_cancel(struct db_context *db);
+void dbwrap_db_id(struct db_context *db, const uint8_t **id, size_t *idlen);
/* The following definitions come from lib/dbwrap_util.c */
diff --git a/source3/lib/dbwrap/dbwrap_ctdb.c b/source3/lib/dbwrap/dbwrap_ctdb.c
index 3d08d1c..933cad5 100644
--- a/source3/lib/dbwrap/dbwrap_ctdb.c
+++ b/source3/lib/dbwrap/dbwrap_ctdb.c
@@ -1490,6 +1490,16 @@ static int db_ctdb_get_flags(struct db_context *db)
return tdb_get_flags(ctx->wtdb->tdb);
}
+static void db_ctdb_id(struct db_context *db, const uint8_t **id,
+ size_t *idlen)
+{
+ struct db_ctdb_ctx *ctx = talloc_get_type_abort(
+ db->private_data, struct db_ctdb_ctx);
+
+ *id = (uint8_t *)&ctx->db_id;
+ *idlen = sizeof(ctx->db_id);
+}
+
struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx,
const char *name,
int hash_size, int tdb_flags,
@@ -1599,6 +1609,8 @@ struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx,
result->transaction_start = db_ctdb_transaction_start;
result->transaction_commit = db_ctdb_transaction_commit;
result->transaction_cancel = db_ctdb_transaction_cancel;
+ result->id = db_ctdb_id;
+ result->stored_callback = NULL;
DEBUG(3,("db_open_ctdb: opened database '%s' with dbid 0x%x\n",
name, db_ctdb->db_id));
diff --git a/source3/lib/dbwrap/dbwrap_private.h b/source3/lib/dbwrap/dbwrap_private.h
index f95e305..9499342 100644
--- a/source3/lib/dbwrap/dbwrap_private.h
+++ b/source3/lib/dbwrap/dbwrap_private.h
@@ -26,6 +26,7 @@
#include "dbwrap/dbwrap_open.h"
struct db_record {
+ struct db_context *db;
TDB_DATA key, value;
NTSTATUS (*store)(struct db_record *rec, TDB_DATA data, int flag);
NTSTATUS (*delete_rec)(struct db_record *rec);
@@ -58,9 +59,13 @@ struct db_context {
void *private_data);
int (*exists)(struct db_context *db,TDB_DATA key);
int (*wipe)(struct db_context *db);
+ void (*id)(struct db_context *db, const uint8_t **id, size_t *idlen);
void *private_data;
enum dbwrap_lock_order lock_order;
bool persistent;
+ void (*stored_callback)(struct db_context *db, struct db_record *rec,
+ void *private_data);
+ void *stored_callback_private_data;
};
#endif /* __DBWRAP_PRIVATE_H__ */
diff --git a/source3/lib/dbwrap/dbwrap_rbt.c b/source3/lib/dbwrap/dbwrap_rbt.c
index 95cd3e8..0d0fb02 100644
--- a/source3/lib/dbwrap/dbwrap_rbt.c
+++ b/source3/lib/dbwrap/dbwrap_rbt.c
@@ -408,6 +408,12 @@ static int db_rbt_trans_dummy(struct db_context *db)
return 0;
}
+static void db_rbt_id(struct db_context *db, const uint8_t **id, size_t *idlen)
+{
+ *id = (uint8_t *)db;
+ *idlen = sizeof(db);
+}
+
struct db_context *db_open_rbt(TALLOC_CTX *mem_ctx)
{
struct db_context *result;
@@ -437,6 +443,8 @@ struct db_context *db_open_rbt(TALLOC_CTX *mem_ctx)
result->wipe = db_rbt_wipe;
result->parse_record = db_rbt_parse_record;
result->lock_order = 0;
+ result->id = db_rbt_id;
+ result->stored_callback = NULL;
return result;
}
diff --git a/source3/lib/dbwrap/dbwrap_tdb.c b/source3/lib/dbwrap/dbwrap_tdb.c
index 59ed791..fbbe757 100644
--- a/source3/lib/dbwrap/dbwrap_tdb.c
+++ b/source3/lib/dbwrap/dbwrap_tdb.c
@@ -23,9 +23,15 @@
#include "dbwrap/dbwrap_tdb.h"
#include "lib/tdb_wrap/tdb_wrap.h"
#include "util_tdb.h"
+#include "system/filesys.h"
struct db_tdb_ctx {
struct tdb_wrap *wtdb;
+
+ struct {
+ dev_t dev;
+ ino_t ino;
+ } id;
};
static NTSTATUS db_tdb_store(struct db_record *rec, TDB_DATA data, int flag);
@@ -270,6 +276,7 @@ static int db_tdb_traverse_func(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
rec.store = db_tdb_store;
rec.delete_rec = db_tdb_delete;
rec.private_data = ctx->db->private_data;
+ rec.db = ctx->db;
return ctx->f(&rec, ctx->private_data);
}
@@ -310,6 +317,7 @@ static int db_tdb_traverse_read_func(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA d
rec.store = db_tdb_store_deny;
rec.delete_rec = db_tdb_delete_deny;
rec.private_data = ctx->db->private_data;
+ rec.db = ctx->db;
return ctx->f(&rec, ctx->private_data);
}
@@ -366,6 +374,14 @@ static int db_tdb_transaction_cancel(struct db_context *db)
return 0;
}
+static void db_tdb_id(struct db_context *db, const uint8_t **id, size_t *idlen)
+{
+ struct db_tdb_ctx *db_ctx =
+ talloc_get_type_abort(db->private_data, struct db_tdb_ctx);
+ *id = (uint8_t *)&db_ctx->id;
+ *idlen = sizeof(db_ctx->id);
+}
+
struct db_context *db_open_tdb(TALLOC_CTX *mem_ctx,
struct loadparm_context *lp_ctx,
const char *name,
@@ -375,6 +391,7 @@ struct db_context *db_open_tdb(TALLOC_CTX *mem_ctx,
{
struct db_context *result = NULL;
struct db_tdb_ctx *db_tdb;
+ struct stat st;
result = talloc_zero(mem_ctx, struct db_context);
if (result == NULL) {
@@ -396,6 +413,15 @@ struct db_context *db_open_tdb(TALLOC_CTX *mem_ctx,
goto fail;
}
+ ZERO_STRUCT(db_tdb->id);
+
+ if (fstat(tdb_fd(db_tdb->wtdb->tdb), &st) == -1) {
+ DEBUG(3, ("fstat failed: %s\n", strerror(errno)));
+ goto fail;
+ }
+ db_tdb->id.dev = st.st_dev;
+ db_tdb->id.ino = st.st_ino;
+
result->fetch_locked = db_tdb_fetch_locked;
result->try_fetch_locked = db_tdb_try_fetch_locked;
result->traverse = db_tdb_traverse;
@@ -409,6 +435,8 @@ struct db_context *db_open_tdb(TALLOC_CTX *mem_ctx,
result->transaction_cancel = db_tdb_transaction_cancel;
result->exists = db_tdb_exists;
result->wipe = db_tdb_wipe;
+ result->id = db_tdb_id;
+ result->stored_callback = NULL;
return result;
fail:
diff --git a/source3/lib/dbwrap/dbwrap_watch.c b/source3/lib/dbwrap/dbwrap_watch.c
new file mode 100644
index 0000000..0c13371
--- /dev/null
+++ b/source3/lib/dbwrap/dbwrap_watch.c
@@ -0,0 +1,486 @@
+/*
+ Unix SMB/CIFS implementation.
+ Watch dbwrap record changes
+ Copyright (C) Volker Lendecke 2012
+
+ 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "dbwrap.h"
+#include "dbwrap_watch.h"
+#include "dbwrap_open.h"
+#include "msg_channel.h"
+#include "lib/util/util_tdb.h"
+#include "lib/util/tevent_ntstatus.h"
+
+static struct db_context *dbwrap_record_watchers_db(void)
+{
+ static struct db_context *watchers_db;
+
+ if (watchers_db == NULL) {
+ watchers_db = db_open(NULL, lock_path("dbwrap_watchers.tdb"),
+ 0, TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT,
+ 0600, DBWRAP_LOCK_ORDER_3);
+ }
+ return watchers_db;
+}
+
+static TDB_DATA dbwrap_record_watchers_key(TALLOC_CTX *mem_ctx,
+ struct db_context *db,
+ struct db_record *rec,
+ TDB_DATA *rec_key)
+{
+ const uint8_t *db_id;
+ size_t db_id_len;
+ TDB_DATA key, wkey;
+
+ dbwrap_db_id(db, &db_id, &db_id_len);
+ key = dbwrap_record_get_key(rec);
+
+ wkey.dsize = sizeof(uint32_t) + db_id_len + key.dsize;
+ wkey.dptr = talloc_array(mem_ctx, uint8_t, wkey.dsize);
+ if (wkey.dptr == NULL) {
+ return make_tdb_data(NULL, 0);
+ }
+ SIVAL(wkey.dptr, 0, db_id_len);
+ memcpy(wkey.dptr + sizeof(uint32_t), db_id, db_id_len);
+ memcpy(wkey.dptr + sizeof(uint32_t) + db_id_len, key.dptr, key.dsize);
+
+ if (rec_key != NULL) {
+ rec_key->dptr = wkey.dptr + sizeof(uint32_t) + db_id_len;
+ rec_key->dsize = key.dsize;
+ }
+
+ return wkey;
+}
+
+static bool dbwrap_record_watchers_key_parse(
+ TDB_DATA wkey, uint8_t **p_db_id, size_t *p_db_id_len, TDB_DATA *key)
+{
+ size_t db_id_len;
+
+ if (wkey.dsize < sizeof(uint32_t)) {
+ DEBUG(1, ("Invalid watchers key\n"));
+ return false;
+ }
+ db_id_len = IVAL(wkey.dptr, 0);
+ if (db_id_len > (wkey.dsize - sizeof(uint32_t))) {
+ DEBUG(1, ("Invalid watchers key, wkey.dsize=%d, "
+ "db_id_len=%d\n", (int)wkey.dsize, (int)db_id_len));
+ return false;
+ }
+ *p_db_id = wkey.dptr + sizeof(uint32_t);
+ *p_db_id_len = db_id_len;
+ key->dptr = wkey.dptr + sizeof(uint32_t) + db_id_len;
+ key->dsize = wkey.dsize - sizeof(uint32_t) - db_id_len;
+ return true;
+}
+
+static NTSTATUS dbwrap_record_add_watcher(TDB_DATA w_key, struct server_id id)
+{
+ struct TALLOC_CTX *frame = talloc_stackframe();
+ struct db_context *db;
+ struct db_record *rec;
+ TDB_DATA value;
+ struct server_id *ids;
+ size_t num_ids;
+ NTSTATUS status;
+
+ db = dbwrap_record_watchers_db();
+ if (db == NULL) {
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+ rec = dbwrap_fetch_locked(db, talloc_tos(), w_key);
+ if (rec == NULL) {
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+ value = dbwrap_record_get_value(rec);
+
+ if ((value.dsize % sizeof(struct server_id)) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto fail;
+ }
+
+ ids = (struct server_id *)value.dptr;
+ num_ids = value.dsize / sizeof(struct server_id);
+
+ ids = talloc_realloc(talloc_tos(), ids, struct server_id,
+ num_ids + 1);
+ if (ids == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ ids[num_ids] = id;
+ num_ids += 1;
+
+ status = dbwrap_record_store(
+ rec, make_tdb_data((uint8_t *)ids, talloc_get_size(ids)), 0);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS dbwrap_record_del_watcher(TDB_DATA w_key, struct server_id id)
+{
+ struct TALLOC_CTX *frame = talloc_stackframe();
+ struct db_context *db;
+ struct db_record *rec;
+ struct server_id *ids;
+ size_t i, num_ids;
+ TDB_DATA value;
+ NTSTATUS status;
+
+ db = dbwrap_record_watchers_db();
+ if (db == NULL) {
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+ rec = dbwrap_fetch_locked(db, talloc_tos(), w_key);
+ if (rec == NULL) {
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+ value = dbwrap_record_get_value(rec);
+
+ if ((value.dsize % sizeof(struct server_id)) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto fail;
+ }
+
+ ids = (struct server_id *)value.dptr;
+ num_ids = value.dsize / sizeof(struct server_id);
+
+ for (i=0; i<num_ids; i++) {
+ if (procid_equal(&id, &ids[i])) {
+ ids[i] = ids[num_ids-1];
+ value.dsize -= sizeof(struct server_id);
+ break;
+ }
+ }
+ if (value.dsize == 0) {
+ status = dbwrap_record_delete(rec);
+ goto done;
+ }
+ status = dbwrap_record_store(rec, value, 0);
+fail:
+done:
+ TALLOC_FREE(frame);
+ return status;
+}
+
--
Samba Shared Repository
More information about the samba-cvs
mailing list