diff --git a/source3/Makefile.in b/source3/Makefile.in index 148de92b370..41f668358bf 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -212,7 +212,7 @@ SBIN_PROGS = bin/smbd bin/nmbd @SWAT_SBIN_TARGETS@ @EXTRA_SBIN_PROGS@ BIN_PROGS1 = bin/smbclient bin/net bin/smbspool \ bin/testparm bin/smbstatus bin/smbget \ - bin/smbta-util + bin/smbta-util bin/cclean BIN_PROGS2 = bin/smbcontrol bin/smbtree $(TDBBACKUP) \ bin/nmblookup bin/pdbedit $(TDBDUMP) \ $(TDBRESTORE) $(TDBTOOL) bin/dbwrap_tool @@ -1049,6 +1049,12 @@ STATUS_OBJ = utils/status.o utils/status_profile.o \ $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) \ $(LIBSMB_ERR_OBJ) $(FNAME_UTIL_OBJ) +CCLEAN_OBJ = utils/cclean.o utils/interact.o \ + $(LOCKING_OBJ) $(PARAM_OBJ) \ + $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) \ + $(LIBSMB_ERR_OBJ) $(FNAME_UTIL_OBJ) + + SMBCONTROL_OBJ = utils/smbcontrol.o $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \ $(LIBSMB_ERR_OBJ) $(POPT_LIB_OBJ) $(PRINTBASE_OBJ) @@ -1965,6 +1971,11 @@ bin/smbstatus: $(BINARY_PREREQS) $(STATUS_OBJ) @BUILD_POPT@ $(LIBTALLOC) $(LIBTD @$(CC) -o $@ $(STATUS_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) \ $(LDAP_LIBS) $(POPT_LIBS) $(LIBTALLOC_LIBS) $(LIBTDB_LIBS) +bin/cclean: $(BINARY_PREREQS) $(CCLEAN_OBJ) @BUILD_POPT@ $(LIBTALLOC) $(LIBTDB) + @echo Linking $@ + @$(CC) -o $@ $(CCLEAN_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) \ + $(LDAP_LIBS) $(POPT_LIBS) $(LIBTALLOC_LIBS) $(LIBTDB_LIBS) + bin/smbcontrol: $(BINARY_PREREQS) $(SMBCONTROL_OBJ) @BUILD_POPT@ $(LIBTALLOC) $(LIBTDB) @echo Linking $@ @$(CC) -DUSING_SMBCONTROL -o $@ \ diff --git a/source3/utils/cclean.c b/source3/utils/cclean.c new file mode 100644 index 00000000000..b035d33c031 --- /dev/null +++ b/source3/utils/cclean.c @@ -0,0 +1,300 @@ +/* + Unix SMB/CIFS implementation. + cleanup connections tdb + Copyright (C) Gregor Beck 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 . +*/ + +#include "includes.h" +#include "serverid.h" +#include "popt_common.h" +#include "dbwrap/dbwrap.h" +#include "util_tdb.h" +#include "messages.h" +#include "system/filesys.h" +#include "interact.h" +#include "lib/conn_tdb.h" + +static bool verbose = false; +static bool dry_run = false; +static bool automatic = false; + +struct cclean_ctx { + struct server_id *ids; + int *cnums; + const char **names; + unsigned num; + + bool *exists; + unsigned num_orphans; +}; + + +static char *serverid_str(const struct server_id id) +{ + return talloc_asprintf(talloc_tos(), "pid %u, vnn %u, uid %lu", + (unsigned)id.pid, (unsigned)id.vnn, id.unique_id); +} + +static void print_record(const char *msg, + const struct connections_key *k, + const struct connections_data *d) +{ + char *idstr = serverid_str(k->pid); + d_printf("%s: connection %d (%s) to \"%s\" from %u:%u@%s[%s] %s\n", msg, + k->cnum, idstr, d->servicename, (unsigned)d->uid, + (unsigned)d->gid, d->machine, d->addr, time_to_asc(d->start)); + talloc_free(idstr); +} + +static int read_connections_fn(const struct connections_key *key, + const struct connections_data *data, + void *cclean_ctx) +{ + struct cclean_ctx *ctx = (struct cclean_ctx *)cclean_ctx; + unsigned length = talloc_array_length(ctx->cnums); + if (length <= ctx->num) { + int n = 2*length; + void *tmp; + + tmp = talloc_realloc(ctx, ctx->ids, struct server_id, n); + if (tmp == NULL) { + goto talloc_failed; + } + ctx->ids = (struct server_id *)tmp; + + tmp = talloc_realloc(ctx, ctx->cnums, int, n); + if (tmp == NULL) { + goto talloc_failed; + } + ctx->cnums = (int *)tmp; + + tmp = talloc_realloc(ctx, ctx->names, const char *, n); + if (tmp == NULL) { + goto talloc_failed; + } + ctx->names = (const char **)tmp; + } + + if (verbose) { + print_record("Read", key, data); + } + + ctx->ids[ctx->num] = key->pid; + ctx->cnums[ctx->num] = key->cnum; + ctx->names[ctx->num] = talloc_strndup(ctx, key->name, FSTRING_LEN); + if (ctx->names[ctx->num]) { + goto talloc_failed; + } + ctx->num++; + + return 0; + +talloc_failed: + DEBUG(0, ("Out of memory\n")); + return -1; +} + +static int read_connections(struct cclean_ctx *ctx) +{ + int ret = connections_forall_read( + &read_connections_fn, + ctx); + if (ret < 0) { + return ret; + } + if (ret != ctx->num) { + DEBUG(0, ("Skipped %d invalid entries\n", ret - ctx->num)); + } + return 0; +} + +static int check_connections(struct cclean_ctx *ctx) +{ + int i, ret = -1; + + ctx->exists = talloc_realloc(ctx, ctx->exists, bool, MAX(1, ctx->num)); + if (ctx->exists == NULL) { + DEBUG(0, ("Out of memory\n")); + goto done; + } + + if (!serverids_exist(ctx->ids, ctx->num, ctx->exists)) { + DEBUG(0, ("serverids_exist() failed\n")); + goto done; + } + + ctx->num_orphans = 0; + for (i=0; inum; i++) { + if (!ctx->exists[i]) { + char *idstr = serverid_str(ctx->ids[i]); + d_printf("Orphaned entry: %s\n", idstr); + talloc_free(idstr); + ctx->num_orphans++; + } + } + ret = 0; +done: + return ret; +} + +static int delete_orphans(struct cclean_ctx *ctx) +{ + NTSTATUS status; + struct db_record *conn; + int i, ret = 0; + + for (i=0; inum; i++) { + if (!ctx->exists[i]) { + TDB_DATA key, value; + conn = connections_fetch_entry_ext(NULL, + ctx->ids[i], + ctx->cnums[i], + ctx->names[i]); + + key = dbwrap_record_get_key(conn); + value = dbwrap_record_get_value(conn); + + print_record("Delete record", + (struct connections_key *)key.dptr, + (struct connections_data *)value.dptr); + + if (!dry_run) { + status = dbwrap_record_delete(conn); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to delete record: %s\n", + nt_errstr(status))); + ret = -2; + } + } + TALLOC_FREE(conn); + } + } + return ret; +} + +static int cclean(void) +{ + int ret; + struct cclean_ctx *ctx = talloc_zero(talloc_tos(), struct cclean_ctx); + + ret = read_connections(ctx); + if (ret != 0) { + d_printf("Failed to read connections\n"); + goto done; + } + d_printf("Read %u connections\n", ctx->num); + + ret = check_connections(ctx); + if (ret != 0) { + d_printf("Failed to check connections\n"); + goto done; + } + d_printf("Found %u orphans\n", ctx->num_orphans); + + if (ctx->num_orphans == 0) { + goto done; + } + + if (!automatic) { + int act = interact_prompt("Delete ([y]es/[n]o)", "yn", 'n'); + if (tolower(act) != 'y') { + ret = 0; + goto done; + } + } + ret = delete_orphans(ctx); + if (ret != 0) { + d_printf("Failed to delete all orphans\n"); + } +done: + talloc_free(ctx); + return ret; +} + +int main(int argc, const char *argv[]) +{ + int ret = -1; + TALLOC_CTX *frame = talloc_stackframe(); + poptContext pc; + char opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + {"verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Be verbose" }, + {"auto", 'a', POPT_ARG_NONE, NULL, 'a', "Don't ask" }, + {"test", 'T', POPT_ARG_NONE, NULL, 'T', "Dry run" }, + POPT_COMMON_SAMBA + POPT_TABLEEND + }; + struct tevent_context *evt_ctx = NULL; + struct messaging_context *msg_ctx = NULL; + + load_case_tables(); + setup_logging(argv[0], DEBUG_STDERR); + + pc = poptGetContext(NULL, argc, (const char **) argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'v': + verbose = true; + break; + case 'a': + automatic = true; + break; + case 'T': + dry_run = true; + break; + } + } + + DEBUG(1, ("using configfile = %s\n", get_dyn_CONFIGFILE())); + + if (!lp_load_initial_only(get_dyn_CONFIGFILE())) { + DEBUG(0, ("Can't load %s - run testparm to debug it\n", + get_dyn_CONFIGFILE())); + goto done; + } + + if (lp_clustering()) { + evt_ctx = event_context_init(frame); + if (evt_ctx == NULL) { + DEBUG(0, ("tevent_context_init failed\n")); + goto done; + } + + msg_ctx = messaging_init(frame, evt_ctx); + if (msg_ctx == NULL) { + DEBUG(0, ("messaging_init failed\n")); + goto done; + } + } + + if (!lp_load_global(get_dyn_CONFIGFILE())) { + DEBUG(0, ("Can't load %s - run testparm to debug it\n", + get_dyn_CONFIGFILE())); + goto done; + } + + if (!connections_init(!dry_run)) { + DEBUG(0, ("Failed to open connections tdb\n")); + goto done; + } + + ret = cclean(); +done: + talloc_free(frame); + return ret; +} diff --git a/source3/wscript_build b/source3/wscript_build index 3be9ed52145..b8c186593b8 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -420,6 +420,8 @@ SWAT_SRC = '''${SWAT_SRC1}''' STATUS_SRC = '''utils/status.c utils/status_profile.c smbd/notify_internal.c''' +CCLEAN_SRC = '''utils/cclean.c utils/interact.c''' + SMBCONTROL_SRC = '''utils/smbcontrol.c''' SMBTREE_SRC = '''utils/smbtree.c''' @@ -1320,6 +1322,16 @@ bld.SAMBA3_BINARY('smbstatus', PROFILE''', vars=locals()) +bld.SAMBA3_BINARY('cclean', + source=CCLEAN_SRC, + deps=''' + talloc + param + popt_samba3 + LOCKING + PROFILE''', + vars=locals()) + bld.SAMBA3_BINARY('smbcontrol', source=SMBCONTROL_SRC, deps='''