lib: Remove server_id_str()
[samba.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 NTSTATUS wipedbs_check_server_exists(struct wipedbs_state *state)
405 {
406         NTSTATUS status;
407         bool ok;
408         int num_servers;
409
410         status = dbwrap_traverse_read(state->id2server_data,
411                                       wipedbs_traverse_nop, NULL, &num_servers);
412         if (!NT_STATUS_IS_OK(status)) {
413                 DEBUG(0, ("Failed to traverse temporary database\n"));
414                 goto done;
415         }
416         state->stat.server.total = num_servers;
417
418         state->server_ids = talloc_array(state, struct server_id, num_servers);
419         state->server_exists = talloc_array(state, bool, num_servers);
420         if (state->server_ids == NULL || state->server_exists == NULL) {
421                 DEBUG(0, ("Out of memory\n"));
422                 goto done;
423         }
424
425         state->idx = 0;
426         status = dbwrap_traverse_read(state->id2server_data,
427                                       wipedbs_traverse_fill_ids,
428                                       state, NULL);
429         if (!NT_STATUS_IS_OK(status)) {
430                 DEBUG(0, ("Failed to traverse temporary database\n"));
431                 goto done;
432         }
433
434         ok = serverids_exist(state->server_ids, num_servers, state->server_exists);
435         if (!ok) {
436                 DEBUG(0, ("Calling serverids_exist failed\n"));
437                 status = NT_STATUS_UNSUCCESSFUL;
438                 goto done;
439         }
440
441         state->idx = 0;
442         status = dbwrap_traverse_read(state->id2server_data,
443                                       wipedbs_traverse_set_exists, state, NULL);
444         if (!NT_STATUS_IS_OK(status)) {
445                 DEBUG(0, ("Failed to traverse temporary database\n"));
446                 goto done;
447         }
448 done:
449         TALLOC_FREE(state->server_ids);
450         TALLOC_FREE(state->server_exists);
451         return status;
452 }
453
454 static int wipedbs_delete_records(struct db_context *db,
455                                   struct wipedbs_record_marker *records,
456                                   bool dry_run, bool verbose, int *count)
457 {
458         struct wipedbs_record_marker *cur;
459         struct db_record *rec;
460         TDB_DATA val;
461         NTSTATUS status;
462         unsigned num=0, total=0;
463
464         if (db == NULL) {
465                 return 0;
466         }
467
468         for (cur = records; cur != NULL; cur = cur->next) {
469                 total++;
470                 rec = dbwrap_fetch_locked(db, talloc_tos(), cur->key);
471                 if (rec == NULL) {
472                         DEBUG(0, ("Failed to fetch record <%s> from %s",
473                                   cur->desc, dbwrap_name(db)));
474                         continue;
475                 }
476                 val = dbwrap_record_get_value(rec);
477                 if (tdb_data_equal(val, cur->val)) {
478                         if (dry_run) {
479                                 status = NT_STATUS_OK;
480                         } else {
481                                 status = dbwrap_record_delete(rec);
482                         }
483                         if (NT_STATUS_IS_OK(status)) {
484                                 num ++;
485                         } else {
486                                 DEBUG(0, ("Failed to delete record <%s> from %s"
487                                           ": %s\n", cur->desc, dbwrap_name(db),
488                                           nt_errstr(status)));
489                         }
490                 } else {
491                         DEBUG(0, ("Warning: record <%s> from %s changed"
492                                   ", skip record!\n",
493                                   cur->desc, dbwrap_name(db)));
494                 }
495                 if (verbose) {
496                         d_printf("deleting %s\n", cur->desc);
497                 }
498                 TALLOC_FREE(rec);
499         }
500
501         if (verbose) {
502                 d_printf("Deleted %u of %u records from %s\n",
503                          num, total, dbwrap_name(db));
504         }
505
506         if (count) {
507                 *count += total;
508         }
509
510         return total - num;
511 }
512
513 static int wipedbs_traverse_server_data(struct db_record *rec,
514                                         void *wipedbs_state)
515 {
516         struct wipedbs_state *state = talloc_get_type_abort(
517                 wipedbs_state, struct wipedbs_state);
518         bool dry_run = state->testmode;
519         TDB_DATA val = dbwrap_record_get_value(rec);
520         int ret;
521         struct wipedbs_server_data *sd = talloc_get_type_abort(
522                 *(void**)val.dptr, struct wipedbs_server_data);
523
524         if (state->verbose) {
525                 d_printf("Server: '%s' %s\n", sd->server_id_str,
526                          sd->exists ?
527                          "exists" :
528                          "does not exist, cleaning up...");
529         }
530
531         if (sd->exists) {
532                 return 0;
533         }
534
535         ret = wipedbs_delete_records(state->session_db, sd->session_records,
536                                      dry_run, state->verbose,
537                                      &state->stat.session.todelete);
538         state->stat.session.failure += ret;
539
540         ret = wipedbs_delete_records(state->tcon_db, sd->tcon_records,
541                                      dry_run, state->verbose,
542                                      &state->stat.tcon.todelete);
543         state->stat.tcon.failure += ret;
544
545         ret = wipedbs_delete_records(state->open_db, sd->open_records,
546                                      dry_run, state->verbose,
547                                      &state->stat.open.todelete);
548         state->stat.open.failure += ret;
549
550         return 0;
551 }
552
553 static int net_serverid_wipedbs(struct net_context *c, int argc,
554                                 const char **argv)
555 {
556         int ret = -1;
557         NTSTATUS status;
558         struct wipedbs_state *state = talloc_zero(talloc_tos(),
559                                                   struct wipedbs_state);
560
561         if (c->display_usage) {
562                 d_printf("%s\n%s",
563                          _("Usage:"),
564                          _("net serverid wipedbs [--test] [--verbose]\n"));
565                 d_printf("%s\n%s",
566                          _("Example:"),
567                          _("net serverid wipedbs -v\n"));
568                 return -1;
569         }
570
571         state->now = timeval_current();
572         state->testmode = c->opt_testmode;
573         state->verbose = c->opt_verbose;
574
575         state->id2server_data = db_open_rbt(state);
576         if (state->id2server_data == NULL) {
577                 DEBUG(0, ("Failed to open temporary database\n"));
578                 goto done;
579         }
580
581         status = smbXsrv_session_global_traverse(wipedbs_traverse_sessions,
582                                                  state);
583         if (!NT_STATUS_IS_OK(status)) {
584                 goto done;
585         }
586
587         status = smbXsrv_tcon_global_traverse(wipedbs_traverse_tcon, state);
588         if (!NT_STATUS_IS_OK(status)) {
589                 goto done;
590         }
591
592         status = smbXsrv_open_global_traverse(wipedbs_traverse_open, state);
593         if (!NT_STATUS_IS_OK(status)) {
594                 goto done;
595         }
596
597         status = wipedbs_check_server_exists(state);
598         if (!NT_STATUS_IS_OK(status)) {
599                 goto done;
600         }
601
602         status = dbwrap_traverse_read(state->id2server_data,
603                                       wipedbs_traverse_server_data,
604                                       state, NULL);
605         if (!NT_STATUS_IS_OK(status)) {
606                 DEBUG(0, ("Failed to traverse db: %s\n", nt_errstr(status)));
607                 goto done;
608         }
609
610         d_printf("Found %d serverids, %d alive and %d disconnected\n",
611                  state->stat.server.total,
612                  state->stat.server.existing,
613                  state->stat.server.disconnected);
614         d_printf("Found %d sessions, %d alive and %d disconnected"
615                  ", cleaned up %d of %d entries\n",
616                  state->stat.session.total,
617                  state->stat.session.total - state->stat.session.todelete,
618                  state->stat.session.disconnected,
619                  state->stat.session.todelete - state->stat.session.failure,
620                  state->stat.session.todelete);
621         d_printf("Found %d tcons, %d alive and %d disconnected"
622                  ", cleaned up %d of %d entries\n",
623                  state->stat.tcon.total,
624                  state->stat.tcon.total - state->stat.tcon.todelete,
625                  state->stat.tcon.disconnected,
626                  state->stat.tcon.todelete - state->stat.tcon.failure,
627                  state->stat.tcon.todelete);
628         d_printf("Found %d opens, %d alive, %d disconnected and %d timed out"
629                  ", cleaned up %d of %d entries\n",
630                  state->stat.open.total,
631                  state->stat.open.total - state->stat.open.todelete
632                  - (state->stat.open.disconnected - state->stat.open_timed_out),
633                  state->stat.open.disconnected,
634                  state->stat.open_timed_out,
635                  state->stat.open.todelete - state->stat.open.failure,
636                  state->stat.open.todelete);
637
638         ret = 0;
639 done:
640         talloc_free(state);
641         return ret;
642 }
643
644 int net_serverid(struct net_context *c, int argc, const char **argv)
645 {
646         struct functable func[] = {
647                 {
648                         "list",
649                         net_serverid_list,
650                         NET_TRANSPORT_LOCAL,
651                         N_("List all entries from serverid.tdb"),
652                         N_("net serverid list\n"
653                            "    List all entries from serverid.tdb")
654                 },
655                 {
656                         "wipe",
657                         net_serverid_wipe,
658                         NET_TRANSPORT_LOCAL,
659                         N_("Wipe the serverid.tdb for the current node"),
660                         N_("net serverid wipe\n"
661                            "    Wipe the serverid.tdb for the current node")
662                 },
663                 {
664                         "wipedbs",
665                         net_serverid_wipedbs,
666                         NET_TRANSPORT_LOCAL,
667                         N_("Clean dead entries from temporary databases"),
668                         N_("net serverid wipedbs\n"
669                            "    Clean dead entries from temporary databases")
670                 },
671                 {NULL, NULL, 0, NULL, NULL}
672         };
673
674         return net_run_function(c, argc, argv, "net serverid", func);
675 }