s3:cclean: fix memory allocation
[mat/samba.git] / source3 / utils / cclean.c
1 /*
2    Unix SMB/CIFS implementation.
3    cleanup connections tdb
4    Copyright (C) Gregor Beck 2012
5
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.
10
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.
15
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/>.
18 */
19
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"
29
30 static bool verbose = false;
31 static bool dry_run = false;
32 static bool automatic = false;
33
34 struct cclean_ctx {
35         struct server_id *ids;
36         int *cnums;
37         const char **names;
38         unsigned num;
39
40         bool *exists;
41         unsigned num_orphans;
42 };
43
44
45 static char *serverid_str(const struct server_id id)
46 {
47         return talloc_asprintf(talloc_tos(), "pid %u, vnn %u, uid %lu",
48                                (unsigned)id.pid, (unsigned)id.vnn, id.unique_id);
49 }
50
51 static void print_record(const char *msg,
52                          const struct connections_key *k,
53                          const struct connections_data *d)
54 {
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);
60 }
61
62 static int read_connections_fn(const struct connections_key *key,
63                                const struct connections_data *data,
64                                void *cclean_ctx)
65 {
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;
71
72                 tmp = talloc_realloc(ctx, ctx->ids, struct server_id, n);
73                 if (tmp == NULL) {
74                         goto talloc_failed;
75                 }
76                 ctx->ids = (struct server_id *)tmp;
77
78                 tmp = talloc_realloc(ctx, ctx->cnums, int, n);
79                 if (tmp == NULL) {
80                         goto talloc_failed;
81                 }
82                 ctx->cnums = (int *)tmp;
83
84                 tmp = talloc_realloc(ctx, ctx->names, const char *, n);
85                 if (tmp == NULL) {
86                         goto talloc_failed;
87                 }
88                 ctx->names = (const char **)tmp;
89         }
90
91         if (verbose) {
92                 print_record("Read", key, data);
93         }
94
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;
100         }
101         ctx->num++;
102
103         return 0;
104
105 talloc_failed:
106         DEBUG(0, ("Out of memory\n"));
107         return -1;
108 }
109
110 static int read_connections(struct cclean_ctx *ctx)
111 {
112         int ret = connections_forall_read(
113                 &read_connections_fn,
114                 ctx);
115         if (ret < 0) {
116                 return ret;
117         }
118         if (ret != ctx->num) {
119                 DEBUG(0, ("Skipped %d invalid entries\n", ret - ctx->num));
120         }
121         return 0;
122 }
123
124 static int check_connections(struct cclean_ctx *ctx)
125 {
126         int i, ret = -1;
127
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;
132         }
133
134         if (!serverids_exist(ctx->ids, ctx->num, ctx->exists)) {
135                 DEBUG(0, ("serverids_exist() failed\n"));
136                 goto done;
137         }
138
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++;
146                 }
147         }
148         ret = 0;
149 done:
150         return ret;
151 }
152
153 static int delete_orphans(struct cclean_ctx *ctx)
154 {
155         NTSTATUS status;
156         struct db_record *conn;
157         int i, ret = 0;
158
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]);
166
167                         key = dbwrap_record_get_key(conn);
168                         value = dbwrap_record_get_value(conn);
169
170                         print_record("Delete record",
171                                      (struct connections_key *)key.dptr,
172                                      (struct connections_data *)value.dptr);
173
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;
180                                 }
181                         }
182                         TALLOC_FREE(conn);
183                 }
184         }
185         return ret;
186 }
187
188 static int cclean(void)
189 {
190         int ret;
191         struct cclean_ctx *ctx = talloc_zero(talloc_tos(), struct cclean_ctx);
192
193         ret = read_connections(ctx);
194         if (ret != 0) {
195                 d_printf("Failed to read connections\n");
196                 goto done;
197         }
198         d_printf("Read %u connections\n", ctx->num);
199
200         ret = check_connections(ctx);
201         if (ret != 0) {
202                 d_printf("Failed to check connections\n");
203                 goto done;
204         }
205         d_printf("Found %u orphans\n", ctx->num_orphans);
206
207         if (ctx->num_orphans == 0) {
208                 goto done;
209         }
210
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;
216                 }
217         }
218         ret = delete_orphans(ctx);
219         if (ret != 0) {
220                 d_printf("Failed to delete all orphans\n");
221         }
222 done:
223         talloc_free(ctx);
224         return ret;
225 }
226
227 int main(int argc, const char *argv[])
228 {
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
240         };
241         struct tevent_context *evt_ctx = NULL;
242         struct messaging_context *msg_ctx = NULL;
243
244         load_case_tables();
245         setup_logging(argv[0], DEBUG_STDERR);
246
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;
260                 }
261         }
262
263         DEBUG(1, ("using configfile = %s\n", get_dyn_CONFIGFILE()));
264
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;
269         }
270
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;
276                 }
277
278                 msg_ctx = messaging_init(frame, evt_ctx);
279                 if (msg_ctx == NULL) {
280                         DEBUG(0, ("messaging_init failed\n"));
281                         goto done;
282                 }
283         }
284
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;
289         }
290
291         if (!connections_init(!dry_run)) {
292                 DEBUG(0, ("Failed to open connections tdb\n"));
293                 goto done;
294         }
295
296         ret = cclean();
297 done:
298         talloc_free(frame);
299         return ret;
300 }