s3:torture:delete: untangle function call from result check
[Samba.git] / source3 / utils / cclean.c
blob20841173436049c7ee575e6867ea395e03e4c15b
1 /*
2 Unix SMB/CIFS implementation.
3 cleanup connections tdb
4 Copyright (C) Gregor Beck 2012
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
21 #include "serverid.h"
22 #include "popt_common.h"
23 #include "dbwrap/dbwrap.h"
24 #include "util_tdb.h"
25 #include "messages.h"
26 #include "system/filesys.h"
27 #include "interact.h"
28 #include "lib/conn_tdb.h"
30 static bool verbose = false;
31 static bool dry_run = false;
32 static bool automatic = false;
34 struct cclean_ctx {
35 struct server_id *ids;
36 int *cnums;
37 const char **names;
38 unsigned num;
40 bool *exists;
41 unsigned num_orphans;
45 static char *serverid_str(const struct server_id id)
47 return talloc_asprintf(talloc_tos(), "pid %u, vnn %u, uid %llu",
48 (unsigned)id.pid, (unsigned)id.vnn, (unsigned long long)id.unique_id);
51 static void print_record(const char *msg,
52 const struct connections_key *k,
53 const struct connections_data *d)
55 char *idstr = serverid_str(k->pid);
56 d_printf("%s: connection %d (%s) ", msg, k->cnum, idstr);
57 if (d == NULL) {
58 d_printf("<no data>\n");
59 } else {
60 d_printf("to \"%s\" from %u:%u@%s[%s] %s\n", d->servicename,
61 (unsigned)d->uid, (unsigned)d->gid, d->machine,
62 d->addr, time_to_asc(d->start));
64 talloc_free(idstr);
67 static int read_connections_fn(const struct connections_key *key,
68 const struct connections_data *data,
69 void *cclean_ctx)
71 struct cclean_ctx *ctx = (struct cclean_ctx *)cclean_ctx;
72 unsigned length = talloc_array_length(ctx->cnums);
73 if (length <= ctx->num) {
74 int n = MAX(2*length, 16);
75 void *tmp;
77 tmp = talloc_realloc(ctx, ctx->ids, struct server_id, n);
78 if (tmp == NULL) {
79 goto talloc_failed;
81 ctx->ids = (struct server_id *)tmp;
83 tmp = talloc_realloc(ctx, ctx->cnums, int, n);
84 if (tmp == NULL) {
85 goto talloc_failed;
87 ctx->cnums = (int *)tmp;
89 tmp = talloc_realloc(ctx, ctx->names, const char *, n);
90 if (tmp == NULL) {
91 goto talloc_failed;
93 ctx->names = (const char **)tmp;
96 if (verbose) {
97 print_record("Read", key, data);
100 ctx->ids[ctx->num] = key->pid;
101 ctx->cnums[ctx->num] = key->cnum;
102 ctx->names[ctx->num] = talloc_strndup(ctx, key->name, FSTRING_LEN);
103 if (ctx->names[ctx->num] == NULL) {
104 goto talloc_failed;
106 ctx->num++;
108 return 0;
110 talloc_failed:
111 DEBUG(0, ("Out of memory\n"));
112 return -1;
115 static int read_connections(struct cclean_ctx *ctx)
117 int ret = connections_forall_read(
118 &read_connections_fn,
119 ctx);
120 if (ret < 0) {
121 return ret;
123 if (ret != ctx->num) {
124 DEBUG(0, ("Skipped %d invalid entries\n", ret - ctx->num));
126 return 0;
129 static int check_connections(struct cclean_ctx *ctx)
131 int i, ret = -1;
133 ctx->exists = talloc_realloc(ctx, ctx->exists, bool, MAX(1, ctx->num));
134 if (ctx->exists == NULL) {
135 DEBUG(0, ("Out of memory\n"));
136 goto done;
139 if (!serverids_exist(ctx->ids, ctx->num, ctx->exists)) {
140 DEBUG(0, ("serverids_exist() failed\n"));
141 goto done;
144 ctx->num_orphans = 0;
145 for (i=0; i<ctx->num; i++) {
146 if (!ctx->exists[i]) {
147 char *idstr = serverid_str(ctx->ids[i]);
148 d_printf("Orphaned entry: %s\n", idstr);
149 talloc_free(idstr);
150 ctx->num_orphans++;
153 ret = 0;
154 done:
155 return ret;
158 static int delete_orphans(struct cclean_ctx *ctx)
160 NTSTATUS status;
161 struct db_record *conn;
162 int i, ret = 0;
164 for (i=0; i<ctx->num; i++) {
165 if (!ctx->exists[i]) {
166 TDB_DATA key, value;
167 conn = connections_fetch_entry_ext(NULL,
168 ctx->ids[i],
169 ctx->cnums[i],
170 ctx->names[i]);
172 key = dbwrap_record_get_key(conn);
173 value = dbwrap_record_get_value(conn);
175 print_record("Delete record",
176 (struct connections_key *)key.dptr,
177 (struct connections_data *)value.dptr);
179 if (!dry_run) {
180 status = dbwrap_record_delete(conn);
181 if (!NT_STATUS_IS_OK(status)) {
182 DEBUG(0, ("Failed to delete record: %s\n",
183 nt_errstr(status)));
184 ret = -2;
187 TALLOC_FREE(conn);
190 return ret;
193 static int cclean(void)
195 int ret;
196 struct cclean_ctx *ctx = talloc_zero(talloc_tos(), struct cclean_ctx);
198 ret = read_connections(ctx);
199 if (ret != 0) {
200 d_printf("Failed to read connections\n");
201 goto done;
203 d_printf("Read %u connections\n", ctx->num);
205 ret = check_connections(ctx);
206 if (ret != 0) {
207 d_printf("Failed to check connections\n");
208 goto done;
210 d_printf("Found %u orphans\n", ctx->num_orphans);
212 if (ctx->num_orphans == 0) {
213 goto done;
216 if (!automatic) {
217 int act = interact_prompt("Delete ([y]es/[n]o)", "yn", 'n');
218 if (tolower(act) != 'y') {
219 ret = 0;
220 goto done;
223 ret = delete_orphans(ctx);
224 if (ret != 0) {
225 d_printf("Failed to delete all orphans\n");
227 done:
228 talloc_free(ctx);
229 return ret;
232 int main(int argc, const char *argv[])
234 int ret = -1;
235 TALLOC_CTX *frame = talloc_stackframe();
236 poptContext pc;
237 char opt;
238 struct poptOption long_options[] = {
239 POPT_AUTOHELP
240 {"verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Be verbose" },
241 {"auto", 'a', POPT_ARG_NONE, NULL, 'a', "Don't ask" },
242 {"test", 'T', POPT_ARG_NONE, NULL, 'T', "Dry run" },
243 POPT_COMMON_SAMBA
244 POPT_TABLEEND
246 struct tevent_context *evt_ctx = NULL;
247 struct messaging_context *msg_ctx = NULL;
249 load_case_tables();
250 setup_logging(argv[0], DEBUG_STDERR);
252 pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
253 POPT_CONTEXT_KEEP_FIRST);
254 while ((opt = poptGetNextOpt(pc)) != -1) {
255 switch (opt) {
256 case 'v':
257 verbose = true;
258 break;
259 case 'a':
260 automatic = true;
261 break;
262 case 'T':
263 dry_run = true;
264 break;
268 DEBUG(1, ("using configfile = %s\n", get_dyn_CONFIGFILE()));
270 if (!lp_load_initial_only(get_dyn_CONFIGFILE())) {
271 DEBUG(0, ("Can't load %s - run testparm to debug it\n",
272 get_dyn_CONFIGFILE()));
273 goto done;
276 if (lp_clustering()) {
277 evt_ctx = event_context_init(frame);
278 if (evt_ctx == NULL) {
279 DEBUG(0, ("tevent_context_init failed\n"));
280 goto done;
283 msg_ctx = messaging_init(frame, evt_ctx);
284 if (msg_ctx == NULL) {
285 DEBUG(0, ("messaging_init failed\n"));
286 goto done;
290 if (!lp_load_global(get_dyn_CONFIGFILE())) {
291 DEBUG(0, ("Can't load %s - run testparm to debug it\n",
292 get_dyn_CONFIGFILE()));
293 goto done;
296 if (!connections_init(!dry_run)) {
297 DEBUG(0, ("Failed to open connections tdb\n"));
298 goto done;
301 ret = cclean();
302 done:
303 talloc_free(frame);
304 return ret;