s3:cclean: fix memory allocation
[Samba.git] / source3 / utils / cclean.c
blobd98725e7fb4fcc7c4137ce3ad0987236f39585b6
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 %lu",
48 (unsigned)id.pid, (unsigned)id.vnn, 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) to \"%s\" from %u:%u@%s[%s] %s\n", msg,
57 k->cnum, idstr, d->servicename, (unsigned)d->uid,
58 (unsigned)d->gid, d->machine, d->addr, time_to_asc(d->start));
59 talloc_free(idstr);
62 static int read_connections_fn(const struct connections_key *key,
63 const struct connections_data *data,
64 void *cclean_ctx)
66 struct cclean_ctx *ctx = (struct cclean_ctx *)cclean_ctx;
67 unsigned length = talloc_array_length(ctx->cnums);
68 if (length <= ctx->num) {
69 int n = MAX(2*length, 16);
70 void *tmp;
72 tmp = talloc_realloc(ctx, ctx->ids, struct server_id, n);
73 if (tmp == NULL) {
74 goto talloc_failed;
76 ctx->ids = (struct server_id *)tmp;
78 tmp = talloc_realloc(ctx, ctx->cnums, int, n);
79 if (tmp == NULL) {
80 goto talloc_failed;
82 ctx->cnums = (int *)tmp;
84 tmp = talloc_realloc(ctx, ctx->names, const char *, n);
85 if (tmp == NULL) {
86 goto talloc_failed;
88 ctx->names = (const char **)tmp;
91 if (verbose) {
92 print_record("Read", key, data);
95 ctx->ids[ctx->num] = key->pid;
96 ctx->cnums[ctx->num] = key->cnum;
97 ctx->names[ctx->num] = talloc_strndup(ctx, key->name, FSTRING_LEN);
98 if (ctx->names[ctx->num] == NULL) {
99 goto talloc_failed;
101 ctx->num++;
103 return 0;
105 talloc_failed:
106 DEBUG(0, ("Out of memory\n"));
107 return -1;
110 static int read_connections(struct cclean_ctx *ctx)
112 int ret = connections_forall_read(
113 &read_connections_fn,
114 ctx);
115 if (ret < 0) {
116 return ret;
118 if (ret != ctx->num) {
119 DEBUG(0, ("Skipped %d invalid entries\n", ret - ctx->num));
121 return 0;
124 static int check_connections(struct cclean_ctx *ctx)
126 int i, ret = -1;
128 ctx->exists = talloc_realloc(ctx, ctx->exists, bool, MAX(1, ctx->num));
129 if (ctx->exists == NULL) {
130 DEBUG(0, ("Out of memory\n"));
131 goto done;
134 if (!serverids_exist(ctx->ids, ctx->num, ctx->exists)) {
135 DEBUG(0, ("serverids_exist() failed\n"));
136 goto done;
139 ctx->num_orphans = 0;
140 for (i=0; i<ctx->num; i++) {
141 if (!ctx->exists[i]) {
142 char *idstr = serverid_str(ctx->ids[i]);
143 d_printf("Orphaned entry: %s\n", idstr);
144 talloc_free(idstr);
145 ctx->num_orphans++;
148 ret = 0;
149 done:
150 return ret;
153 static int delete_orphans(struct cclean_ctx *ctx)
155 NTSTATUS status;
156 struct db_record *conn;
157 int i, ret = 0;
159 for (i=0; i<ctx->num; i++) {
160 if (!ctx->exists[i]) {
161 TDB_DATA key, value;
162 conn = connections_fetch_entry_ext(NULL,
163 ctx->ids[i],
164 ctx->cnums[i],
165 ctx->names[i]);
167 key = dbwrap_record_get_key(conn);
168 value = dbwrap_record_get_value(conn);
170 print_record("Delete record",
171 (struct connections_key *)key.dptr,
172 (struct connections_data *)value.dptr);
174 if (!dry_run) {
175 status = dbwrap_record_delete(conn);
176 if (!NT_STATUS_IS_OK(status)) {
177 DEBUG(0, ("Failed to delete record: %s\n",
178 nt_errstr(status)));
179 ret = -2;
182 TALLOC_FREE(conn);
185 return ret;
188 static int cclean(void)
190 int ret;
191 struct cclean_ctx *ctx = talloc_zero(talloc_tos(), struct cclean_ctx);
193 ret = read_connections(ctx);
194 if (ret != 0) {
195 d_printf("Failed to read connections\n");
196 goto done;
198 d_printf("Read %u connections\n", ctx->num);
200 ret = check_connections(ctx);
201 if (ret != 0) {
202 d_printf("Failed to check connections\n");
203 goto done;
205 d_printf("Found %u orphans\n", ctx->num_orphans);
207 if (ctx->num_orphans == 0) {
208 goto done;
211 if (!automatic) {
212 int act = interact_prompt("Delete ([y]es/[n]o)", "yn", 'n');
213 if (tolower(act) != 'y') {
214 ret = 0;
215 goto done;
218 ret = delete_orphans(ctx);
219 if (ret != 0) {
220 d_printf("Failed to delete all orphans\n");
222 done:
223 talloc_free(ctx);
224 return ret;
227 int main(int argc, const char *argv[])
229 int ret = -1;
230 TALLOC_CTX *frame = talloc_stackframe();
231 poptContext pc;
232 char opt;
233 struct poptOption long_options[] = {
234 POPT_AUTOHELP
235 {"verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Be verbose" },
236 {"auto", 'a', POPT_ARG_NONE, NULL, 'a', "Don't ask" },
237 {"test", 'T', POPT_ARG_NONE, NULL, 'T', "Dry run" },
238 POPT_COMMON_SAMBA
239 POPT_TABLEEND
241 struct tevent_context *evt_ctx = NULL;
242 struct messaging_context *msg_ctx = NULL;
244 load_case_tables();
245 setup_logging(argv[0], DEBUG_STDERR);
247 pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
248 POPT_CONTEXT_KEEP_FIRST);
249 while ((opt = poptGetNextOpt(pc)) != -1) {
250 switch (opt) {
251 case 'v':
252 verbose = true;
253 break;
254 case 'a':
255 automatic = true;
256 break;
257 case 'T':
258 dry_run = true;
259 break;
263 DEBUG(1, ("using configfile = %s\n", get_dyn_CONFIGFILE()));
265 if (!lp_load_initial_only(get_dyn_CONFIGFILE())) {
266 DEBUG(0, ("Can't load %s - run testparm to debug it\n",
267 get_dyn_CONFIGFILE()));
268 goto done;
271 if (lp_clustering()) {
272 evt_ctx = event_context_init(frame);
273 if (evt_ctx == NULL) {
274 DEBUG(0, ("tevent_context_init failed\n"));
275 goto done;
278 msg_ctx = messaging_init(frame, evt_ctx);
279 if (msg_ctx == NULL) {
280 DEBUG(0, ("messaging_init failed\n"));
281 goto done;
285 if (!lp_load_global(get_dyn_CONFIGFILE())) {
286 DEBUG(0, ("Can't load %s - run testparm to debug it\n",
287 get_dyn_CONFIGFILE()));
288 goto done;
291 if (!connections_init(!dry_run)) {
292 DEBUG(0, ("Failed to open connections tdb\n"));
293 goto done;
296 ret = cclean();
297 done:
298 talloc_free(frame);
299 return ret;