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