s3:utils: add 'net witness list' command
[Samba.git] / source3 / utils / net_witness.c
blob7d1b0cf904d49b6dfd7f4e2b472971b09963a799
1 /*
2 * Samba Unix/Linux client library
3 * net witness commands to manage smb witness registrations
4 * Copyright (C) 2023 Stefan Metzmacher
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 "utils/net.h"
22 #include "messages.h"
23 #include "serverid.h"
24 #include "lib/util/util_tdb.h"
25 #include "source3/include/util_tdb.h"
26 #include "libcli/security/dom_sid.h"
27 #include "lib/dbwrap/dbwrap.h"
28 #include "lib/dbwrap/dbwrap_rbt.h"
29 #include "lib/dbwrap/dbwrap_open.h"
30 #include "lib/param/param.h"
31 #include "librpc/gen_ndr/ndr_rpcd_witness.h"
32 #include <regex.h>
34 struct json_object;
36 #ifdef HAVE_JANSSON
37 #include <jansson.h>
38 #include "audit_logging.h" /* various JSON helpers */
39 #endif /* HAVE_JANSSON */
41 #undef strcasecmp
43 static struct db_context *net_witness_open_registration_db(void)
45 static struct db_context *db;
46 char *global_path = NULL;
48 if (db != NULL) {
49 return db;
52 global_path = lock_path(talloc_tos(), "rpcd_witness_registration.tdb");
53 if (global_path == NULL) {
54 return NULL;
57 db = db_open(NULL,
58 global_path,
59 0, /* hash_size */
60 TDB_DEFAULT |
61 TDB_CLEAR_IF_FIRST |
62 TDB_INCOMPATIBLE_HASH,
63 O_RDONLY,
64 0600,
65 DBWRAP_LOCK_ORDER_1,
66 DBWRAP_FLAG_NONE);
67 TALLOC_FREE(global_path);
68 if (db == NULL) {
69 return NULL;
72 return db;
75 struct net_witness_scan_registrations_action_state {
76 bool (*prepare_fn)(void *private_data);
77 bool (*match_fn)(void *private_data, const struct rpcd_witness_registration *rg);
78 NTSTATUS (*process_fn)(void *private_data, const struct rpcd_witness_registration *rg);
79 void *private_data;
82 struct net_witness_scan_registrations_regex {
83 regex_t regex;
84 bool valid;
87 struct net_witness_scan_registrations_state {
88 struct net_context *c;
89 struct net_witness_scan_registrations_regex net_name;
90 struct net_witness_scan_registrations_regex share_name;
91 struct net_witness_scan_registrations_regex ip_address;
92 struct net_witness_scan_registrations_regex client_computer;
93 struct json_object *message_json;
94 #ifdef HAVE_JANSSON
95 struct json_object filters_json;
96 struct json_object registrations_json;
97 #endif
98 const struct net_witness_scan_registrations_action_state *action;
99 NTSTATUS error;
102 static bool net_witness_scan_registrations_regex_init(
103 struct net_witness_scan_registrations_state *state,
104 struct net_witness_scan_registrations_regex *r,
105 const char *option, const char *value);
106 static bool net_witness_scan_registrations_regex_match(
107 struct net_witness_scan_registrations_regex *r,
108 const char *name, const char *value);
109 static void net_witness_scan_registrations_regex_free(
110 struct net_witness_scan_registrations_regex *r);
112 static bool net_witness_scan_registrations_match(
113 struct net_witness_scan_registrations_state *state,
114 const struct rpcd_witness_registration *rg)
116 if (state->net_name.valid) {
117 bool match;
119 match = net_witness_scan_registrations_regex_match(
120 &state->net_name,
121 "net_name",
122 rg->net_name);
123 if (!match) {
124 return false;
128 if (state->share_name.valid) {
129 bool match;
131 match = net_witness_scan_registrations_regex_match(
132 &state->share_name,
133 "share_name",
134 rg->share_name);
135 if (!match) {
136 return false;
140 if (state->ip_address.valid) {
141 bool match;
143 match = net_witness_scan_registrations_regex_match(
144 &state->ip_address,
145 "ip_address",
146 rg->ip_address);
147 if (!match) {
148 return false;
152 if (state->client_computer.valid) {
153 bool match;
155 match = net_witness_scan_registrations_regex_match(
156 &state->client_computer,
157 "client_computer_name",
158 rg->client_computer_name);
159 if (!match) {
160 return false;
164 return true;
167 static bool net_witness_scan_registrations_regex_init(
168 struct net_witness_scan_registrations_state *state,
169 struct net_witness_scan_registrations_regex *r,
170 const char *option, const char *value)
172 #ifdef HAVE_JANSSON
173 struct net_context *c = state->c;
174 #endif /* HAVE_JANSSON */
175 int ret;
177 r->valid = false;
179 if (value == NULL) {
180 return true;
183 ret = regcomp(&r->regex, value, REG_EXTENDED|REG_ICASE|REG_NOSUB);
184 if (ret != 0) {
185 fstring buf = { 0,};
186 regerror(ret, &r->regex, buf, sizeof(buf));
187 d_printf("regcomp(%s) failed for %s: "
188 "%d: %s\n", value, option, ret, buf);
189 return false;
192 #ifdef HAVE_JANSSON
193 if (c->opt_json) {
194 ret = json_add_string(&state->filters_json,
195 option,
196 value);
197 if (ret != 0) {
198 return false;
201 #endif /* HAVE_JANSSON */
203 r->valid = true;
204 return true;
207 static bool net_witness_scan_registrations_regex_match(
208 struct net_witness_scan_registrations_regex *r,
209 const char *name, const char *value)
211 int ret;
213 if (!r->valid) {
214 return false;
217 if (value == NULL) {
219 * without a share name,
220 * we match against an empty
221 * string.
223 value = "";
226 ret = regexec(&r->regex, value, 0, NULL, 0);
227 if (ret == REG_NOMATCH) {
228 return false;
231 return true;
234 static void net_witness_scan_registrations_regex_free(
235 struct net_witness_scan_registrations_regex *r)
237 if (r->valid) {
238 regfree(&r->regex);
239 r->valid = false;
243 static bool net_witness_scan_registrations_init(
244 struct net_witness_scan_registrations_state *state)
246 struct net_context *c = state->c;
247 bool ok;
249 if (c->opt_json) {
250 #ifdef HAVE_JANSSON
251 state->filters_json = json_new_object();
252 if (json_is_invalid(&state->filters_json)) {
253 return false;
256 if (c->opt_witness_registration != NULL) {
257 int ret;
259 ret = json_add_string(&state->filters_json,
260 "--witness-registration",
261 c->opt_witness_registration);
262 if (ret != 0) {
263 return false;
267 state->registrations_json = json_new_object();
268 if (json_is_invalid(&state->registrations_json)) {
269 return false;
271 #else /* not HAVE_JANSSON */
272 d_fprintf(stderr, _("JSON support not available\n"));
273 return false;
274 #endif /* not HAVE_JANSSON */
277 ok = net_witness_scan_registrations_regex_init(state,
278 &state->net_name,
279 "--witness-net-name",
280 c->opt_witness_net_name);
281 if (!ok) {
282 return false;
285 ok = net_witness_scan_registrations_regex_init(state,
286 &state->share_name,
287 "--witness-share-name",
288 c->opt_witness_share_name);
289 if (!ok) {
290 return false;
293 ok = net_witness_scan_registrations_regex_init(state,
294 &state->ip_address,
295 "--witness-ip-address",
296 c->opt_witness_ip_address);
297 if (!ok) {
298 return false;
301 ok = net_witness_scan_registrations_regex_init(state,
302 &state->client_computer,
303 "--witness-client-computer-name",
304 c->opt_witness_client_computer_name);
305 if (!ok) {
306 return false;
309 ok = state->action->prepare_fn(state->action->private_data);
310 if (!ok) {
311 return false;
314 if (!c->opt_json) {
315 d_printf("%-36s %-20s %-15s %-20s %s\n",
316 "Registration-UUID:",
317 "NetName",
318 "ShareName",
319 "IpAddress",
320 "ClientComputerName");
321 d_printf("%-36s-%-20s-%-15s-%-20s-%s\n",
322 "------------------------------------",
323 "--------------------",
324 "------------------",
325 "--------------------",
326 "------------------");
329 return true;
332 static bool net_witness_scan_registrations_finish(
333 struct net_witness_scan_registrations_state *state)
335 #ifdef HAVE_JANSSON
336 struct net_context *c = state->c;
337 struct json_object root_json = json_empty_object;
338 TALLOC_CTX *frame = NULL;
339 const char *json_str = NULL;
340 int ret;
342 if (!c->opt_json) {
343 return true;
346 frame = talloc_stackframe();
348 root_json = json_new_object();
349 if (json_is_invalid(&root_json)) {
350 TALLOC_FREE(frame);
351 return false;
354 ret = json_add_object(&root_json,
355 "filters",
356 &state->filters_json);
357 if (ret != 0) {
358 json_free(&root_json);
359 TALLOC_FREE(frame);
360 return false;
362 state->filters_json = json_empty_object;
364 if (state->message_json != NULL) {
365 ret = json_add_object(&root_json,
366 "message",
367 state->message_json);
368 if (ret != 0) {
369 json_free(&root_json);
370 TALLOC_FREE(frame);
371 return false;
373 *state->message_json = json_empty_object;
376 ret = json_add_object(&root_json,
377 "registrations",
378 &state->registrations_json);
379 if (ret != 0) {
380 json_free(&root_json);
381 TALLOC_FREE(frame);
382 return false;
384 state->registrations_json = json_empty_object;
386 json_str = json_to_string(frame, &root_json);
387 json_free(&root_json);
388 if (json_str == NULL) {
389 TALLOC_FREE(frame);
390 return false;
393 d_printf("%s\n", json_str);
394 TALLOC_FREE(frame);
395 return true;
396 #else /* not HAVE_JANSSON */
397 return true;
398 #endif /* not HAVE_JANSSON */
401 static void net_witness_scan_registrations_free(
402 struct net_witness_scan_registrations_state *state)
404 #ifdef HAVE_JANSSON
405 if (!json_is_invalid(&state->filters_json)) {
406 json_free(&state->filters_json);
408 if (!json_is_invalid(&state->registrations_json)) {
409 json_free(&state->registrations_json);
411 #endif /* HAVE_JANSSON */
413 net_witness_scan_registrations_regex_free(&state->net_name);
414 net_witness_scan_registrations_regex_free(&state->share_name);
415 net_witness_scan_registrations_regex_free(&state->ip_address);
416 net_witness_scan_registrations_regex_free(&state->client_computer);
419 #ifdef HAVE_JANSSON
420 static int dump_registration_json(struct json_object *registrations_json,
421 const char *key_str,
422 const struct rpcd_witness_registration *rg)
424 struct json_object jsobj = json_empty_object;
425 struct json_object flags_json = json_empty_object;
426 struct json_object context_json = json_empty_object;
427 struct json_object serverid_json = json_empty_object;
428 struct json_object auth_json = json_empty_object;
429 struct json_object connection_json = json_empty_object;
430 struct timeval tv;
431 struct dom_sid_buf sid_buf;
432 int ret = 0;
434 jsobj = json_new_object();
435 if (json_is_invalid(&jsobj)) {
436 d_fprintf(stderr, _("error setting up JSON value\n"));
437 goto failure;
440 ret = json_add_flags32(&jsobj, "version", rg->version);
441 if (ret != 0) {
442 goto failure;
445 ret = json_add_string(&jsobj, "net_name", rg->net_name);
446 if (ret != 0) {
447 goto failure;
450 ret = json_add_string(&jsobj, "share_name", rg->share_name);
451 if (ret != 0) {
452 goto failure;
455 ret = json_add_string(&jsobj, "ip_address", rg->ip_address);
456 if (ret != 0) {
457 goto failure;
460 ret = json_add_string(&jsobj, "client_computer_name", rg->client_computer_name);
461 if (ret != 0) {
462 goto failure;
465 flags_json = json_new_object();
466 if (json_is_invalid(&flags_json)) {
467 goto failure;
470 ret = json_add_bool(&flags_json, "WITNESS_REGISTER_IP_NOTIFICATION",
471 (rg->flags & WITNESS_REGISTER_IP_NOTIFICATION) ?
472 true : false);
473 if (ret != 0) {
474 goto failure;
477 ret = json_add_int(&flags_json, "int", rg->flags);
478 if (ret != 0) {
479 goto failure;
482 ret = json_add_flags32(&flags_json, "hex", rg->flags);
483 if (ret != 0) {
484 goto failure;
487 ret = json_add_object(&jsobj, "flags", &flags_json);
488 if (ret != 0) {
489 goto failure;
491 flags_json = json_empty_object;
493 ret = json_add_int(&jsobj, "timeout", rg->timeout);
494 if (ret != 0) {
495 goto failure;
498 context_json = json_new_object();
499 if (json_is_invalid(&context_json)) {
500 goto failure;
503 ret = json_add_int(&context_json, "handle_type", rg->context_handle.handle_type);
504 if (ret != 0) {
505 goto failure;
508 ret = json_add_guid(&context_json, "uuid", &rg->context_handle.uuid);
509 if (ret != 0) {
510 goto failure;
513 ret = json_add_object(&jsobj, "context_handle", &context_json);
514 if (ret != 0) {
515 goto failure;
517 context_json = json_empty_object;
519 serverid_json = json_new_object();
520 if (json_is_invalid(&serverid_json)) {
521 goto failure;
524 ret = json_add_int(&serverid_json, "pid", rg->server_id.pid);
525 if (ret != 0) {
526 goto failure;
529 ret = json_add_int(&serverid_json, "task_id", rg->server_id.task_id);
530 if (ret != 0) {
531 goto failure;
534 ret = json_add_int(&serverid_json, "vnn", rg->server_id.vnn);
535 if (ret != 0) {
536 goto failure;
539 ret = json_add_int(&serverid_json, "unique_id", rg->server_id.unique_id);
540 if (ret != 0) {
541 goto failure;
544 ret = json_add_object(&jsobj, "server_id", &serverid_json);
545 if (ret != 0) {
546 goto failure;
548 serverid_json = json_empty_object;
550 auth_json = json_new_object();
551 if (json_is_invalid(&auth_json)) {
552 goto failure;
555 ret = json_add_string(&auth_json, "account_name", rg->account_name);
556 if (ret != 0) {
557 goto failure;
560 ret = json_add_string(&auth_json, "domain_name", rg->domain_name);
561 if (ret != 0) {
562 goto failure;
565 ret = json_add_string(&auth_json,
566 "account_sid",
567 dom_sid_str_buf(&rg->account_sid, &sid_buf));
568 if (ret != 0) {
569 goto failure;
572 ret = json_add_object(&jsobj, "auth", &auth_json);
573 if (ret != 0) {
574 goto failure;
576 auth_json = json_empty_object;
578 connection_json = json_new_object();
579 if (json_is_invalid(&connection_json)) {
580 goto failure;
583 ret = json_add_string(&connection_json, "local_address", rg->local_address);
584 if (ret != 0) {
585 goto failure;
588 ret = json_add_string(&connection_json, "remote_address", rg->remote_address);
589 if (ret != 0) {
590 goto failure;
593 ret = json_add_object(&jsobj, "connection", &connection_json);
594 if (ret != 0) {
595 goto failure;
597 connection_json = json_empty_object;
599 nttime_to_timeval(&tv, rg->registration_time);
600 ret = json_add_time(&jsobj, "registration_time", tv);
601 if (ret != 0) {
602 goto failure;
605 ret = json_add_object(registrations_json, key_str, &jsobj);
606 if (ret != 0) {
607 goto failure;
609 jsobj = json_empty_object;
611 failure:
612 if (!json_is_invalid(&connection_json)) {
613 json_free(&connection_json);
615 if (!json_is_invalid(&auth_json)) {
616 json_free(&auth_json);
618 if (!json_is_invalid(&serverid_json)) {
619 json_free(&serverid_json);
621 if (!json_is_invalid(&context_json)) {
622 json_free(&context_json);
624 if (!json_is_invalid(&flags_json)) {
625 json_free(&flags_json);
627 if (!json_is_invalid(&jsobj)) {
628 json_free(&jsobj);
631 return ret;
633 #endif /* HAVE_JANSSON */
635 static NTSTATUS net_witness_scan_registrations_dump_rg(
636 struct net_witness_scan_registrations_state *state,
637 const struct rpcd_witness_registration *rg)
639 struct net_context *c = state->c;
640 struct GUID_txt_buf key_buf;
641 const char *key_str = GUID_buf_string(&rg->context_handle.uuid, &key_buf);
643 if (c->opt_json) {
644 #ifdef HAVE_JANSSON
645 int ret;
647 ret = dump_registration_json(&state->registrations_json,
648 key_str,
649 rg);
650 if (ret != 0) {
651 d_fprintf(stderr, "dump_registration_json(%s) failed\n",
652 key_str);
653 return NT_STATUS_INTERNAL_ERROR;
655 #endif /* HAVE_JANSSON */
656 return NT_STATUS_OK;
659 d_printf("%-36s %-20s %-15s %-20s %s\n",
660 key_str,
661 rg->net_name,
662 rg->share_name ? rg->share_name : "''",
663 rg->ip_address,
664 rg->client_computer_name);
666 return NT_STATUS_OK;
669 static void net_witness_scan_registrations_parser(TDB_DATA key,
670 TDB_DATA val,
671 void *private_data)
673 struct net_witness_scan_registrations_state *state =
674 (struct net_witness_scan_registrations_state *)private_data;
675 DATA_BLOB val_blob = data_blob_const(val.dptr, val.dsize);
676 struct rpcd_witness_registration rg;
677 enum ndr_err_code ndr_err;
678 TALLOC_CTX *frame = NULL;
679 bool match = false;
681 if (val_blob.length == 0) {
682 return;
685 frame = talloc_stackframe();
687 ndr_err = ndr_pull_struct_blob(&val_blob, frame, &rg,
688 (ndr_pull_flags_fn_t)ndr_pull_rpcd_witness_registration);
689 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
690 DBG_WARNING("Invalid record in rpcd_witness_registration.tdb:"
691 "key '%s' ndr_pull_struct_blob - %s\n",
692 tdb_data_dbg(key),
693 ndr_errstr(ndr_err));
694 state->error = ndr_map_error2ntstatus(ndr_err);
695 TALLOC_FREE(frame);
696 return;
699 if (!serverid_exists(&rg.server_id)) {
700 TALLOC_FREE(frame);
701 return;
704 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
705 NDR_PRINT_DEBUG(rpcd_witness_registration, &rg);
708 match = net_witness_scan_registrations_match(state, &rg);
709 if (!NT_STATUS_IS_OK(state->error)) {
710 TALLOC_FREE(frame);
711 return;
713 if (!match) {
714 TALLOC_FREE(frame);
715 return;
718 match = state->action->match_fn(state->action->private_data, &rg);
719 if (!match) {
720 TALLOC_FREE(frame);
721 return;
724 state->error = state->action->process_fn(state->action->private_data, &rg);
725 if (NT_STATUS_IS_OK(state->error)) {
726 state->error = net_witness_scan_registrations_dump_rg(state,
727 &rg);
729 TALLOC_FREE(frame);
732 static int net_witness_scan_registrations_traverse_cb(struct db_record *rec, void *private_data)
734 struct net_witness_scan_registrations_state *state =
735 (struct net_witness_scan_registrations_state *)private_data;
736 TDB_DATA key = dbwrap_record_get_key(rec);
737 TDB_DATA val = dbwrap_record_get_value(rec);
739 net_witness_scan_registrations_parser(key, val, private_data);
741 if (!NT_STATUS_IS_OK(state->error)) {
742 return -1;
745 return 0;
748 static int net_witness_scan_registrations(struct net_context *c,
749 struct json_object *message_json,
750 const struct net_witness_scan_registrations_action_state *action)
752 struct net_witness_scan_registrations_state state = {
753 .c = c,
754 .message_json = message_json,
755 .action = action,
757 struct db_context *db = NULL;
758 NTSTATUS status;
759 bool ok;
761 db = net_witness_open_registration_db();
762 if (db == NULL) {
763 d_printf("net_witness_open_registration_db() failed\n");
764 return -1;
767 ok = net_witness_scan_registrations_init(&state);
768 if (!ok) {
769 d_printf("net_witness_scan_registrations_init() failed\n");
770 return -1;
773 if (c->opt_witness_registration != NULL) {
774 const char *key_str = c->opt_witness_registration;
775 DATA_BLOB key_blob = data_blob_string_const(key_str);
776 TDB_DATA key = make_tdb_data(key_blob.data, key_blob.length);
778 status = dbwrap_parse_record(db,
779 key,
780 net_witness_scan_registrations_parser,
781 &state);
782 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
783 status = NT_STATUS_OK;
785 if (!NT_STATUS_IS_OK(status)) {
786 d_printf("dbwrap_parse_record(%s) failed: %s\n",
787 key_str, nt_errstr(status));
788 net_witness_scan_registrations_free(&state);
789 return -1;
791 if (!NT_STATUS_IS_OK(state.error)) {
792 d_printf("net_witness_scan_registrations_parser(%s) failed: %s\n",
793 key_str, nt_errstr(state.error));
794 net_witness_scan_registrations_free(&state);
795 return -1;
797 } else {
798 status = dbwrap_traverse_read(db,
799 net_witness_scan_registrations_traverse_cb,
800 &state,
801 NULL); /* count */
802 if (!NT_STATUS_IS_OK(status)) {
803 d_printf("dbwrap_traverse_read() failed\n");
804 net_witness_scan_registrations_free(&state);
805 return -1;
807 if (!NT_STATUS_IS_OK(state.error)) {
808 d_printf("net_witness_scan_registrations_traverse_cb() failed: %s\n",
809 nt_errstr(state.error));
810 net_witness_scan_registrations_free(&state);
811 return -1;
815 ok = net_witness_scan_registrations_finish(&state);
816 if (!ok) {
817 d_printf("net_witness_scan_registrations_finish() failed\n");
818 return -1;
821 net_witness_scan_registrations_free(&state);
822 return 0;
825 struct net_witness_list_state {
826 struct net_context *c;
829 static bool net_witness_list_prepare_fn(void *private_data)
831 return true;
834 static bool net_witness_list_match_fn(void *private_data,
835 const struct rpcd_witness_registration *rg)
837 return true;
840 static NTSTATUS net_witness_list_process_fn(void *private_data,
841 const struct rpcd_witness_registration *rg)
843 return NT_STATUS_OK;
846 static void net_witness_filter_usage(void)
848 d_printf(" Note: Only supported with clustering=yes!\n\n");
849 d_printf(" Machine readable output can be generated with "
850 "the following option:\n"
851 "\n"
852 " --json\n"
853 "\n");
854 d_printf(" The selection of registrations can be limited by "
855 "the following options:\n"
856 "\n"
857 " --witness-registration=REGISTRATION_UUID\n"
858 " This does a direct lookup for REGISTRATION_UUID\n"
859 " instead of doing a database traversal.\n"
860 "\n"
861 " The following options all take a "
862 "POSIX Extended Regular Expression,\n"
863 " which can further filter the selection of "
864 "registrations.\n"
865 " These options are applied as logical AND, "
866 "but each REGEX \n"
867 " allows specifying multiple strings using "
868 "the pipe symbol.\n"
869 "\n"
870 " --witness-net-name=REGEX\n"
871 " This specifies the 'server name' the client\n"
872 " registered for monitoring.\n"
873 "\n"
874 " --witness-share-name=REGEX\n"
875 " This specifies the 'share name' the client\n"
876 " registered for monitoring.\n"
877 " Note that the share name is optional in the\n"
878 " registration, otherwise an empty string is \n"
879 " matched.\n"
880 "\n"
881 " --witness-ip-address=REGEX\n"
882 " This specifies the ip address the client\n"
883 " registered for monitoring.\n"
884 "\n"
885 " --witness-client-computer-name=REGEX\n"
886 " This specifies the client computer name the client\n"
887 " specified in the registration.\n"
888 " Note it is just a string chosen by the "
889 "client itself.\n"
890 "\n");
893 static void net_witness_list_usage(void)
895 d_printf("%s\n"
896 "net witness list\n"
897 " %s\n\n",
898 _("Usage:"),
899 _("List witness registrations "
900 "from rpcd_witness_registration.tdb"));
901 net_witness_filter_usage();
904 static int net_witness_list(struct net_context *c, int argc, const char **argv)
906 TALLOC_CTX *frame = talloc_stackframe();
907 struct net_witness_list_state state = { .c = c, };
908 struct net_witness_scan_registrations_action_state action = {
909 .prepare_fn = net_witness_list_prepare_fn,
910 .match_fn = net_witness_list_match_fn,
911 .process_fn = net_witness_list_process_fn,
912 .private_data = &state,
914 int ret = -1;
916 if (c->display_usage) {
917 net_witness_list_usage();
918 goto out;
921 if (argc != 0) {
922 net_witness_list_usage();
923 goto out;
926 if (!lp_clustering()) {
927 d_printf("ERROR: Only supported with clustering=yes!\n\n");
928 goto out;
931 ret = net_witness_scan_registrations(c, NULL, &action);
932 if (ret != 0) {
933 d_printf("net_witness_scan_registrations() failed\n");
934 goto out;
937 ret = 0;
938 out:
939 TALLOC_FREE(frame);
940 return ret;
943 int net_witness(struct net_context *c, int argc, const char **argv)
945 struct functable func[] = {
947 "list",
948 net_witness_list,
949 NET_TRANSPORT_LOCAL,
950 N_("List witness registrations "
951 "from rpcd_witness_registration.tdb"),
952 N_("net witness list\n"
953 " List witness registrations "
954 "from rpcd_witness_registration.tdb"),
956 {NULL, NULL, 0, NULL, NULL}
959 return net_run_function(c, argc, argv, "net witness", func);