tdb: add overflow/ENOSPC handling to tdb_expand_file()
[obnox/samba/samba-obnox.git] / source3 / utils / net_serverid.c
1 /*
2    Samba Unix/Linux SMB client library
3    net serverid commands
4    Copyright (C) Volker Lendecke 2010
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 "utils/net.h"
22 #include "dbwrap/dbwrap.h"
23 #include "dbwrap/dbwrap_rbt.h"
24 #include "serverid.h"
25 #include "session.h"
26 #include "lib/conn_tdb.h"
27 #include "smbd/globals.h"
28 #include "util_tdb.h"
29
30 static int net_serverid_list_fn(const struct server_id *id,
31                                 uint32_t msg_flags, void *priv)
32 {
33         char *str = server_id_str(talloc_tos(), id);
34         d_printf("%s %llu 0x%x\n", str, (unsigned long long)id->unique_id,
35                  (unsigned int)msg_flags);
36         TALLOC_FREE(str);
37         return 0;
38 }
39
40 static int net_serverid_list(struct net_context *c, int argc,
41                              const char **argv)
42 {
43         d_printf("pid unique_id msg_flags\n");
44         return serverid_traverse_read(net_serverid_list_fn, NULL) ? 0 : -1;
45 }
46
47 static int net_serverid_wipe_fn(struct db_record *rec,
48                                 const struct server_id *id,
49                                 uint32_t msg_flags, void *private_data)
50 {
51         NTSTATUS status;
52
53         if (id->vnn != get_my_vnn()) {
54                 return 0;
55         }
56         status = dbwrap_record_delete(rec);
57         if (!NT_STATUS_IS_OK(status)) {
58                 char *str = server_id_str(talloc_tos(), id);
59                 DEBUG(1, ("Could not delete serverid.tdb record %s: %s\n",
60                           str, nt_errstr(status)));
61                 TALLOC_FREE(str);
62         }
63         return 0;
64 }
65
66 static int net_serverid_wipe(struct net_context *c, int argc,
67                              const char **argv)
68 {
69         return serverid_traverse(net_serverid_wipe_fn, NULL) ? 0 : -1;
70 }
71
72
73 struct wipedbs_record_marker {
74         struct wipedbs_record_marker *prev, *next;
75         TDB_DATA key, val;
76         const char *desc;
77 };
78
79 struct wipedbs_server_data {
80         struct server_id server_id;
81         const char *server_id_str;
82         bool exists;
83         struct wipedbs_record_marker *session_records;
84         struct wipedbs_record_marker *tcon_records;
85         struct wipedbs_record_marker *open_records;
86 };
87
88 struct wipedbs_state {
89         struct db_context *id2server_data;
90         struct {
91                 struct {
92                         int total;
93                         int existing;
94                         int disconnected;
95                 } server;
96                 struct {
97                         int total;
98                         int disconnected;
99                         int todelete;
100                         int failure;
101                 } session, tcon, open;
102                 int open_timed_out;
103         } stat;
104         struct server_id *server_ids;
105         bool *server_exists;
106         int idx;
107         struct db_context *session_db;
108         struct db_context *tcon_db;
109         struct db_context *open_db;
110         struct timeval now;
111         bool testmode;
112         bool verbose;
113 };
114
115 static struct wipedbs_server_data *get_server_data(struct wipedbs_state *state,
116                                                    const struct server_id *id)
117 {
118         struct wipedbs_server_data *ret = NULL;
119         TDB_DATA key, val = tdb_null;
120         NTSTATUS status;
121
122         key = make_tdb_data((const void*)&id->unique_id, sizeof(id->unique_id));
123         status = dbwrap_fetch(state->id2server_data, talloc_tos(), key, &val);
124         if (NT_STATUS_IS_OK(status)) {
125                 ret = *(struct wipedbs_server_data**) val.dptr;
126                 TALLOC_FREE(val.dptr);
127         } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
128                 ret = talloc_zero(state->id2server_data,
129                                   struct wipedbs_server_data);
130                 if (ret == NULL) {
131                         DEBUG(0, ("Failed to allocate server entry for %s\n",
132                                   server_id_str(talloc_tos(), id)));
133                         goto done;
134                 }
135                 ret->server_id = *id;
136                 ret->server_id_str = server_id_str(ret, id);
137                 ret->exists = true;
138                 val = make_tdb_data((const void*)&ret, sizeof(ret));
139                 status = dbwrap_store(state->id2server_data,
140                                       key, val, TDB_INSERT);
141                 if (!NT_STATUS_IS_OK(status)) {
142                         DEBUG(0, ("Failed to store server entry for %s: %s\n",
143                                   server_id_str(talloc_tos(), id),
144                                   nt_errstr(status)));
145                 }
146                 goto done;
147         } else {
148                 DEBUG(0, ("Failed to fetch server entry for %s: %s\n",
149                           server_id_str(talloc_tos(), id), nt_errstr(status)));
150                 goto done;
151         }
152         if (!server_id_equal(id, &ret->server_id)) {
153                 DEBUG(0, ("uniq id collision for %s and %s\n",
154                           server_id_str(talloc_tos(), id),
155                           server_id_str(talloc_tos(), &ret->server_id)));
156                 smb_panic("server_id->unique_id not unique!");
157         }
158 done:
159         return ret;
160 }
161
162 static int wipedbs_traverse_sessions(struct smbXsrv_session_global0 *session,
163                                      void *wipedbs_state)
164 {
165         struct wipedbs_state *state =
166                 talloc_get_type_abort(wipedbs_state,
167                 struct wipedbs_state);
168         struct wipedbs_server_data *sd;
169         struct wipedbs_record_marker *rec;
170         TDB_DATA tmp;
171         int ret = -1;
172
173         assert(session->num_channels == 1);
174
175         state->stat.session.total++;
176
177         sd = get_server_data(state, &session->channels[0].server_id);
178         if (sd == NULL) {
179                 goto done;
180         }
181
182         if (server_id_is_disconnected(&sd->server_id)) {
183                 state->stat.session.disconnected++;
184         }
185
186         rec = talloc_zero(sd, struct wipedbs_record_marker);
187         if (rec == NULL) {
188                 DEBUG(0, ("Out of memory!\n"));
189                 goto done;
190         }
191
192         tmp = dbwrap_record_get_key(session->db_rec);
193         rec->key = tdb_data_talloc_copy(rec, tmp);
194         tmp = dbwrap_record_get_value(session->db_rec);
195         rec->val = tdb_data_talloc_copy(rec, tmp);
196
197         rec->desc = talloc_asprintf(
198                 rec, "session[global: %u wire: %llu]",
199                 session->session_global_id,
200                 (long long unsigned)session->session_wire_id);
201
202         if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) ||
203             (rec->desc == NULL))
204         {
205                 DEBUG(0, ("Out of memory!\n"));
206                 goto done;
207         }
208
209         state->session_db = dbwrap_record_get_db(session->db_rec);
210
211         DLIST_ADD(sd->session_records, rec);
212         ret = 0;
213 done:
214         return ret;
215 }
216
217 static int wipedbs_traverse_tcon(struct smbXsrv_tcon_global0 *tcon,
218                                  void *wipedbs_state)
219 {
220         struct wipedbs_state *state =
221                 talloc_get_type_abort(wipedbs_state,
222                 struct wipedbs_state);
223         struct wipedbs_server_data *sd;
224         struct wipedbs_record_marker *rec;
225         TDB_DATA tmp;
226         int ret = -1;
227
228         state->stat.tcon.total++;
229
230         sd = get_server_data(state, &tcon->server_id);
231         if (sd == NULL) {
232                 goto done;
233         }
234
235         if (server_id_is_disconnected(&sd->server_id)) {
236                 state->stat.tcon.disconnected++;
237         }
238
239         rec = talloc_zero(sd, struct wipedbs_record_marker);
240         if (rec == NULL) {
241                 DEBUG(0, ("Out of memory!\n"));
242                 goto done;
243         }
244
245         tmp = dbwrap_record_get_key(tcon->db_rec);
246         rec->key = tdb_data_talloc_copy(rec, tmp);
247         tmp = dbwrap_record_get_value(tcon->db_rec);
248         rec->val = tdb_data_talloc_copy(rec, tmp);
249
250         rec->desc = talloc_asprintf(
251                 rec, "tcon[global: %u wire: %u session: %u share: %s]",
252                 tcon->tcon_global_id, tcon->tcon_wire_id,
253                 tcon->session_global_id, tcon->share_name);
254
255         if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) ||
256             (rec->desc == NULL))
257         {
258                 DEBUG(0, ("Out of memory!\n"));
259                 goto done;
260         }
261
262         state->tcon_db = dbwrap_record_get_db(tcon->db_rec);
263
264         DLIST_ADD(sd->tcon_records, rec);
265         ret = 0;
266
267 done:
268         return ret;
269 }
270
271 static int wipedbs_traverse_open(struct smbXsrv_open_global0 *open,
272                                  void *wipedbs_state)
273 {
274         struct wipedbs_state *state =
275                 talloc_get_type_abort(wipedbs_state,
276                 struct wipedbs_state);
277         struct wipedbs_server_data *sd;
278         struct wipedbs_record_marker *rec;
279         TDB_DATA tmp;
280         int ret = -1;
281
282         state->stat.open.total++;
283
284         sd = get_server_data(state, &open->server_id);
285         if (sd == NULL) {
286                 goto done;
287         }
288
289         if (server_id_is_disconnected(&sd->server_id)) {
290                 struct timeval disconnect_time;
291                 int64_t tdiff;
292                 bool reached;
293
294                 state->stat.open.disconnected++;
295
296                 nttime_to_timeval(&disconnect_time, open->disconnect_time);
297                 tdiff = usec_time_diff(&state->now, &disconnect_time);
298                 reached = (tdiff >= 1000*open->durable_timeout_msec);
299
300                 if (state->verbose) {
301                         TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
302                         d_printf("open[global: %u] disconnected at "
303                                  "[%s] %us ago with timeout of %us "
304                                  "-%s reached\n",
305                                  open->open_global_id,
306                                  nt_time_string(mem_ctx, open->disconnect_time),
307                                  (unsigned)(tdiff/1000000),
308                                  open->durable_timeout_msec / 1000,
309                                  reached ? "" : " not");
310                         talloc_free(mem_ctx);
311                 }
312
313                 if (!reached) {
314                         ret = 0;
315                         goto done;
316                 }
317                 state->stat.open_timed_out++;
318         }
319
320         rec = talloc_zero(sd, struct wipedbs_record_marker);
321         if (rec == NULL) {
322                 DEBUG(0, ("Out of memory!\n"));
323                 goto done;
324         }
325
326         tmp = dbwrap_record_get_key(open->db_rec);
327         rec->key = tdb_data_talloc_copy(rec, tmp);
328         tmp = dbwrap_record_get_value(open->db_rec);
329         rec->val = tdb_data_talloc_copy(rec, tmp);
330
331         rec->desc = talloc_asprintf(
332                 rec, "open[global: %u persistent: %llu volatile: %llu]",
333                 open->open_global_id,
334                 (long long unsigned)open->open_persistent_id,
335                 (long long unsigned)open->open_volatile_id);
336
337         if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) ||
338             (rec->desc == NULL))
339         {
340                 DEBUG(0, ("Out of memory!\n"));
341                 goto done;
342         }
343
344         state->open_db = dbwrap_record_get_db(open->db_rec);
345
346         DLIST_ADD(sd->open_records, rec);
347         ret = 0;
348
349 done:
350         return ret;
351 }
352
353 static int wipedbs_traverse_nop(struct db_record *rec, void *private_data)
354 {
355         return 0;
356 }
357
358 static int wipedbs_traverse_fill_ids(struct db_record *rec, void *wipedbs_state)
359 {
360         struct wipedbs_state *state = talloc_get_type_abort(
361                 wipedbs_state, struct wipedbs_state);
362
363         TDB_DATA val = dbwrap_record_get_value(rec);
364
365         struct wipedbs_server_data *sd = talloc_get_type_abort(
366                 *(void**)val.dptr, struct wipedbs_server_data);
367
368         state->server_ids[state->idx] = sd->server_id;
369         state->idx++;
370         return 0;
371 }
372
373 static int wipedbs_traverse_set_exists(struct db_record *rec,
374                                        void *wipedbs_state)
375 {
376         struct wipedbs_state *state = talloc_get_type_abort(
377                 wipedbs_state, struct wipedbs_state);
378
379         TDB_DATA val = dbwrap_record_get_value(rec);
380
381         struct wipedbs_server_data *sd = talloc_get_type_abort(
382                 *(void**)val.dptr, struct wipedbs_server_data);
383
384         /* assume a stable traverse order for rbt */
385         SMB_ASSERT(server_id_equal(&state->server_ids[state->idx],
386                                    &sd->server_id));
387         sd->exists = state->server_exists[state->idx];
388
389         if (sd->exists) {
390                 state->stat.server.existing++;
391         }
392         if (server_id_is_disconnected(&sd->server_id)) {
393                 state->stat.server.disconnected++;
394         }
395
396         state->idx++;
397         return 0;
398 }
399
400 static NTSTATUS wipedbs_check_server_exists(struct wipedbs_state *state)
401 {
402         NTSTATUS status;
403         bool ok;
404         int num_servers;
405
406         status = dbwrap_traverse_read(state->id2server_data,
407                                       wipedbs_traverse_nop, NULL, &num_servers);
408         if (!NT_STATUS_IS_OK(status)) {
409                 DEBUG(0, ("Failed to traverse temporary database\n"));
410                 goto done;
411         }
412         state->stat.server.total = num_servers;
413
414         state->server_ids = talloc_array(state, struct server_id, num_servers);
415         state->server_exists = talloc_array(state, bool, num_servers);
416         if (state->server_ids == NULL || state->server_exists == NULL) {
417                 DEBUG(0, ("Out of memory\n"));
418                 goto done;
419         }
420
421         state->idx = 0;
422         status = dbwrap_traverse_read(state->id2server_data,
423                                       wipedbs_traverse_fill_ids,
424                                       state, NULL);
425         if (!NT_STATUS_IS_OK(status)) {
426                 DEBUG(0, ("Failed to traverse temporary database\n"));
427                 goto done;
428         }
429
430         ok = serverids_exist(state->server_ids, num_servers, state->server_exists);
431         if (!ok) {
432                 DEBUG(0, ("Calling serverids_exist failed\n"));
433                 status = NT_STATUS_UNSUCCESSFUL;
434                 goto done;
435         }
436
437         state->idx = 0;
438         status = dbwrap_traverse_read(state->id2server_data,
439                                       wipedbs_traverse_set_exists, state, NULL);
440         if (!NT_STATUS_IS_OK(status)) {
441                 DEBUG(0, ("Failed to traverse temporary database\n"));
442                 goto done;
443         }
444 done:
445         TALLOC_FREE(state->server_ids);
446         TALLOC_FREE(state->server_exists);
447         return status;
448 }
449
450 static int wipedbs_delete_records(struct db_context *db,
451                                   struct wipedbs_record_marker *records,
452                                   bool dry_run, bool verbose, int *count)
453 {
454         struct wipedbs_record_marker *cur;
455         struct db_record *rec;
456         TDB_DATA val;
457         NTSTATUS status;
458         unsigned num=0, total=0;
459
460         if (db == NULL) {
461                 return 0;
462         }
463
464         for (cur = records; cur != NULL; cur = cur->next) {
465                 total++;
466                 rec = dbwrap_fetch_locked(db, talloc_tos(), cur->key);
467                 if (rec == NULL) {
468                         DEBUG(0, ("Failed to fetch record <%s> from %s",
469                                   cur->desc, dbwrap_name(db)));
470                         continue;
471                 }
472                 val = dbwrap_record_get_value(rec);
473                 if (tdb_data_equal(val, cur->val)) {
474                         if (dry_run) {
475                                 status = NT_STATUS_OK;
476                         } else {
477                                 status = dbwrap_record_delete(rec);
478                         }
479                         if (NT_STATUS_IS_OK(status)) {
480                                 num ++;
481                         } else {
482                                 DEBUG(0, ("Failed to delete record <%s> from %s"
483                                           ": %s\n", cur->desc, dbwrap_name(db),
484                                           nt_errstr(status)));
485                         }
486                 } else {
487                         DEBUG(0, ("Warning: record <%s> from %s changed"
488                                   ", skip record!\n",
489                                   cur->desc, dbwrap_name(db)));
490                 }
491                 if (verbose) {
492                         d_printf("deleting %s\n", cur->desc);
493                 }
494                 TALLOC_FREE(rec);
495         }
496
497         if (verbose) {
498                 d_printf("Deleted %u of %u records from %s\n",
499                          num, total, dbwrap_name(db));
500         }
501
502         if (count) {
503                 *count += total;
504         }
505
506         return total - num;
507 }
508
509 static int wipedbs_traverse_server_data(struct db_record *rec,
510                                         void *wipedbs_state)
511 {
512         struct wipedbs_state *state = talloc_get_type_abort(
513                 wipedbs_state, struct wipedbs_state);
514         bool dry_run = state->testmode;
515         TDB_DATA val = dbwrap_record_get_value(rec);
516         int ret;
517         struct wipedbs_server_data *sd = talloc_get_type_abort(
518                 *(void**)val.dptr, struct wipedbs_server_data);
519
520         if (state->verbose) {
521                 d_printf("Server: '%s' %s\n", sd->server_id_str,
522                          sd->exists ?
523                          "exists" :
524                          "does not exist, cleaning up...");
525         }
526
527         if (sd->exists) {
528                 return 0;
529         }
530
531         ret = wipedbs_delete_records(state->session_db, sd->session_records,
532                                      dry_run, state->verbose,
533                                      &state->stat.session.todelete);
534         state->stat.session.failure += ret;
535
536         ret = wipedbs_delete_records(state->tcon_db, sd->tcon_records,
537                                      dry_run, state->verbose,
538                                      &state->stat.tcon.todelete);
539         state->stat.tcon.failure += ret;
540
541         ret = wipedbs_delete_records(state->open_db, sd->open_records,
542                                      dry_run, state->verbose,
543                                      &state->stat.open.todelete);
544         state->stat.open.failure += ret;
545
546         return 0;
547 }
548
549 static int net_serverid_wipedbs(struct net_context *c, int argc,
550                                 const char **argv)
551 {
552         int ret = -1;
553         NTSTATUS status;
554         struct wipedbs_state *state = talloc_zero(talloc_tos(),
555                                                   struct wipedbs_state);
556
557         if (c->display_usage) {
558                 d_printf("%s\n%s",
559                          _("Usage:"),
560                          _("net serverid wipedbs [--test] [--verbose]\n"));
561                 d_printf("%s\n%s",
562                          _("Example:"),
563                          _("net serverid wipedbs -v\n"));
564                 return -1;
565         }
566
567         state->now = timeval_current();
568         state->testmode = c->opt_testmode;
569         state->verbose = c->opt_verbose;
570
571         state->id2server_data = db_open_rbt(state);
572         if (state->id2server_data == NULL) {
573                 DEBUG(0, ("Failed to open temporary database\n"));
574                 goto done;
575         }
576
577         status = smbXsrv_session_global_traverse(wipedbs_traverse_sessions,
578                                                  state);
579         if (!NT_STATUS_IS_OK(status)) {
580                 goto done;
581         }
582
583         status = smbXsrv_tcon_global_traverse(wipedbs_traverse_tcon, state);
584         if (!NT_STATUS_IS_OK(status)) {
585                 goto done;
586         }
587
588         status = smbXsrv_open_global_traverse(wipedbs_traverse_open, state);
589         if (!NT_STATUS_IS_OK(status)) {
590                 goto done;
591         }
592
593         status = wipedbs_check_server_exists(state);
594         if (!NT_STATUS_IS_OK(status)) {
595                 goto done;
596         }
597
598         status = dbwrap_traverse_read(state->id2server_data,
599                                       wipedbs_traverse_server_data,
600                                       state, NULL);
601         if (!NT_STATUS_IS_OK(status)) {
602                 DEBUG(0, ("Failed to traverse db: %s\n", nt_errstr(status)));
603                 goto done;
604         }
605
606         d_printf("Found %d serverids, %d alive and %d disconnected\n",
607                  state->stat.server.total,
608                  state->stat.server.existing,
609                  state->stat.server.disconnected);
610         d_printf("Found %d sessions, %d alive and %d disconnected"
611                  ", cleaned up %d of %d entries\n",
612                  state->stat.session.total,
613                  state->stat.session.total - state->stat.session.todelete,
614                  state->stat.session.disconnected,
615                  state->stat.session.todelete - state->stat.session.failure,
616                  state->stat.session.todelete);
617         d_printf("Found %d tcons, %d alive and %d disconnected"
618                  ", cleaned up %d of %d entries\n",
619                  state->stat.tcon.total,
620                  state->stat.tcon.total - state->stat.tcon.todelete,
621                  state->stat.tcon.disconnected,
622                  state->stat.tcon.todelete - state->stat.tcon.failure,
623                  state->stat.tcon.todelete);
624         d_printf("Found %d opens, %d alive, %d disconnected and %d timed out"
625                  ", cleaned up %d of %d entries\n",
626                  state->stat.open.total,
627                  state->stat.open.total - state->stat.open.todelete
628                  - (state->stat.open.disconnected - state->stat.open_timed_out),
629                  state->stat.open.disconnected,
630                  state->stat.open_timed_out,
631                  state->stat.open.todelete - state->stat.open.failure,
632                  state->stat.open.todelete);
633
634         ret = 0;
635 done:
636         talloc_free(state);
637         return ret;
638 }
639
640 int net_serverid(struct net_context *c, int argc, const char **argv)
641 {
642         struct functable func[] = {
643                 {
644                         "list",
645                         net_serverid_list,
646                         NET_TRANSPORT_LOCAL,
647                         N_("List all entries from serverid.tdb"),
648                         N_("net serverid list\n"
649                            "    List all entries from serverid.tdb")
650                 },
651                 {
652                         "wipe",
653                         net_serverid_wipe,
654                         NET_TRANSPORT_LOCAL,
655                         N_("Wipe the serverid.tdb for the current node"),
656                         N_("net serverid wipe\n"
657                            "    Wipe the serverid.tdb for the current node")
658                 },
659                 {
660                         "wipedbs",
661                         net_serverid_wipedbs,
662                         NET_TRANSPORT_LOCAL,
663                         N_("Clean dead entries from temporary databases"),
664                         N_("net serverid wipedbs\n"
665                            "    Clean dead entries from temporary databases")
666                 },
667                 {NULL, NULL, 0, NULL, NULL}
668         };
669
670         return net_run_function(c, argc, argv, "net serverid", func);
671 }