From: Stefan Metzmacher Date: Fri, 15 Dec 2023 13:49:37 +0000 (+0100) Subject: s3:utils: add 'net witness list' command X-Git-Tag: talloc-2.4.2~19 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=46fdeca696e3fc308d8978ae6a2fc62bd13c5dcf;p=thirdparty%2Fsamba.git s3:utils: add 'net witness list' command It lists the entries from the rpcd_witness_registration.tdb. Signed-off-by: Stefan Metzmacher Reviewed-by: Günther Deschner --- diff --git a/source3/utils/net.c b/source3/utils/net.c index 72878f4280d..8d90d380bed 100644 --- a/source3/utils/net.c +++ b/source3/utils/net.c @@ -905,6 +905,14 @@ static struct functable net_func[] = { "'net vfs' commands.") }, + { "witness", + net_witness, + NET_TRANSPORT_LOCAL, + N_("Manage witness registrations"), + N_(" Use 'net help witness' to get more information about " + "'net witness' commands.") + }, + #ifdef WITH_FAKE_KASERVER { "afs", net_afs, @@ -1233,6 +1241,37 @@ static struct functable net_func[] = { .arg = &c->opt_dns_ttl, .descrip = "TTL in seconds of DNS records", }, + /* Options for 'net witness {list,...}' */ + { + .longName = "witness-registration", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_registration, + }, + { + .longName = "witness-net-name", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_net_name, + }, + { + .longName = "witness-share-name", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_share_name, + }, + { + .longName = "witness-ip-address", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_ip_address, + }, + { + .longName = "witness-client-computer-name", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_client_computer_name, + }, POPT_COMMON_SAMBA POPT_COMMON_CONNECTION POPT_COMMON_CREDENTIALS diff --git a/source3/utils/net.h b/source3/utils/net.h index b21cf410529..c6042b6cbae 100644 --- a/source3/utils/net.h +++ b/source3/utils/net.h @@ -91,6 +91,11 @@ struct net_context { int opt_recursive; int opt_follow_symlink; int opt_dns_ttl; + const char *opt_witness_registration; + const char *opt_witness_net_name; + const char *opt_witness_share_name; + const char *opt_witness_ip_address; + const char *opt_witness_client_computer_name; int opt_have_ip; struct sockaddr_storage opt_dest_ip; diff --git a/source3/utils/net_proto.h b/source3/utils/net_proto.h index b945c220762..ccfa89ae105 100644 --- a/source3/utils/net_proto.h +++ b/source3/utils/net_proto.h @@ -483,4 +483,6 @@ int net_tdb(struct net_context *c, int argc, const char **argv); int net_vfs(struct net_context *c, int argc, const char **argv); +int net_witness(struct net_context *c, int argc, const char **argv); + #endif /* _NET_PROTO_H_ */ diff --git a/source3/utils/net_witness.c b/source3/utils/net_witness.c new file mode 100644 index 00000000000..7d1b0cf904d --- /dev/null +++ b/source3/utils/net_witness.c @@ -0,0 +1,960 @@ +/* + * Samba Unix/Linux client library + * net witness commands to manage smb witness registrations + * Copyright (C) 2023 Stefan Metzmacher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "includes.h" +#include "utils/net.h" +#include "messages.h" +#include "serverid.h" +#include "lib/util/util_tdb.h" +#include "source3/include/util_tdb.h" +#include "libcli/security/dom_sid.h" +#include "lib/dbwrap/dbwrap.h" +#include "lib/dbwrap/dbwrap_rbt.h" +#include "lib/dbwrap/dbwrap_open.h" +#include "lib/param/param.h" +#include "librpc/gen_ndr/ndr_rpcd_witness.h" +#include + +struct json_object; + +#ifdef HAVE_JANSSON +#include +#include "audit_logging.h" /* various JSON helpers */ +#endif /* HAVE_JANSSON */ + +#undef strcasecmp + +static struct db_context *net_witness_open_registration_db(void) +{ + static struct db_context *db; + char *global_path = NULL; + + if (db != NULL) { + return db; + } + + global_path = lock_path(talloc_tos(), "rpcd_witness_registration.tdb"); + if (global_path == NULL) { + return NULL; + } + + db = db_open(NULL, + global_path, + 0, /* hash_size */ + TDB_DEFAULT | + TDB_CLEAR_IF_FIRST | + TDB_INCOMPATIBLE_HASH, + O_RDONLY, + 0600, + DBWRAP_LOCK_ORDER_1, + DBWRAP_FLAG_NONE); + TALLOC_FREE(global_path); + if (db == NULL) { + return NULL; + } + + return db; +} + +struct net_witness_scan_registrations_action_state { + bool (*prepare_fn)(void *private_data); + bool (*match_fn)(void *private_data, const struct rpcd_witness_registration *rg); + NTSTATUS (*process_fn)(void *private_data, const struct rpcd_witness_registration *rg); + void *private_data; +}; + +struct net_witness_scan_registrations_regex { + regex_t regex; + bool valid; +}; + +struct net_witness_scan_registrations_state { + struct net_context *c; + struct net_witness_scan_registrations_regex net_name; + struct net_witness_scan_registrations_regex share_name; + struct net_witness_scan_registrations_regex ip_address; + struct net_witness_scan_registrations_regex client_computer; + struct json_object *message_json; +#ifdef HAVE_JANSSON + struct json_object filters_json; + struct json_object registrations_json; +#endif + const struct net_witness_scan_registrations_action_state *action; + NTSTATUS error; +}; + +static bool net_witness_scan_registrations_regex_init( + struct net_witness_scan_registrations_state *state, + struct net_witness_scan_registrations_regex *r, + const char *option, const char *value); +static bool net_witness_scan_registrations_regex_match( + struct net_witness_scan_registrations_regex *r, + const char *name, const char *value); +static void net_witness_scan_registrations_regex_free( + struct net_witness_scan_registrations_regex *r); + +static bool net_witness_scan_registrations_match( + struct net_witness_scan_registrations_state *state, + const struct rpcd_witness_registration *rg) +{ + if (state->net_name.valid) { + bool match; + + match = net_witness_scan_registrations_regex_match( + &state->net_name, + "net_name", + rg->net_name); + if (!match) { + return false; + } + } + + if (state->share_name.valid) { + bool match; + + match = net_witness_scan_registrations_regex_match( + &state->share_name, + "share_name", + rg->share_name); + if (!match) { + return false; + } + } + + if (state->ip_address.valid) { + bool match; + + match = net_witness_scan_registrations_regex_match( + &state->ip_address, + "ip_address", + rg->ip_address); + if (!match) { + return false; + } + } + + if (state->client_computer.valid) { + bool match; + + match = net_witness_scan_registrations_regex_match( + &state->client_computer, + "client_computer_name", + rg->client_computer_name); + if (!match) { + return false; + } + } + + return true; +} + +static bool net_witness_scan_registrations_regex_init( + struct net_witness_scan_registrations_state *state, + struct net_witness_scan_registrations_regex *r, + const char *option, const char *value) +{ +#ifdef HAVE_JANSSON + struct net_context *c = state->c; +#endif /* HAVE_JANSSON */ + int ret; + + r->valid = false; + + if (value == NULL) { + return true; + } + + ret = regcomp(&r->regex, value, REG_EXTENDED|REG_ICASE|REG_NOSUB); + if (ret != 0) { + fstring buf = { 0,}; + regerror(ret, &r->regex, buf, sizeof(buf)); + d_printf("regcomp(%s) failed for %s: " + "%d: %s\n", value, option, ret, buf); + return false; + } + +#ifdef HAVE_JANSSON + if (c->opt_json) { + ret = json_add_string(&state->filters_json, + option, + value); + if (ret != 0) { + return false; + } + } +#endif /* HAVE_JANSSON */ + + r->valid = true; + return true; +} + +static bool net_witness_scan_registrations_regex_match( + struct net_witness_scan_registrations_regex *r, + const char *name, const char *value) +{ + int ret; + + if (!r->valid) { + return false; + } + + if (value == NULL) { + /* + * without a share name, + * we match against an empty + * string. + */ + value = ""; + } + + ret = regexec(&r->regex, value, 0, NULL, 0); + if (ret == REG_NOMATCH) { + return false; + } + + return true; +} + +static void net_witness_scan_registrations_regex_free( + struct net_witness_scan_registrations_regex *r) +{ + if (r->valid) { + regfree(&r->regex); + r->valid = false; + } +} + +static bool net_witness_scan_registrations_init( + struct net_witness_scan_registrations_state *state) +{ + struct net_context *c = state->c; + bool ok; + + if (c->opt_json) { +#ifdef HAVE_JANSSON + state->filters_json = json_new_object(); + if (json_is_invalid(&state->filters_json)) { + return false; + } + + if (c->opt_witness_registration != NULL) { + int ret; + + ret = json_add_string(&state->filters_json, + "--witness-registration", + c->opt_witness_registration); + if (ret != 0) { + return false; + } + } + + state->registrations_json = json_new_object(); + if (json_is_invalid(&state->registrations_json)) { + return false; + } +#else /* not HAVE_JANSSON */ + d_fprintf(stderr, _("JSON support not available\n")); + return false; +#endif /* not HAVE_JANSSON */ + } + + ok = net_witness_scan_registrations_regex_init(state, + &state->net_name, + "--witness-net-name", + c->opt_witness_net_name); + if (!ok) { + return false; + } + + ok = net_witness_scan_registrations_regex_init(state, + &state->share_name, + "--witness-share-name", + c->opt_witness_share_name); + if (!ok) { + return false; + } + + ok = net_witness_scan_registrations_regex_init(state, + &state->ip_address, + "--witness-ip-address", + c->opt_witness_ip_address); + if (!ok) { + return false; + } + + ok = net_witness_scan_registrations_regex_init(state, + &state->client_computer, + "--witness-client-computer-name", + c->opt_witness_client_computer_name); + if (!ok) { + return false; + } + + ok = state->action->prepare_fn(state->action->private_data); + if (!ok) { + return false; + } + + if (!c->opt_json) { + d_printf("%-36s %-20s %-15s %-20s %s\n", + "Registration-UUID:", + "NetName", + "ShareName", + "IpAddress", + "ClientComputerName"); + d_printf("%-36s-%-20s-%-15s-%-20s-%s\n", + "------------------------------------", + "--------------------", + "------------------", + "--------------------", + "------------------"); + } + + return true; +} + +static bool net_witness_scan_registrations_finish( + struct net_witness_scan_registrations_state *state) +{ +#ifdef HAVE_JANSSON + struct net_context *c = state->c; + struct json_object root_json = json_empty_object; + TALLOC_CTX *frame = NULL; + const char *json_str = NULL; + int ret; + + if (!c->opt_json) { + return true; + } + + frame = talloc_stackframe(); + + root_json = json_new_object(); + if (json_is_invalid(&root_json)) { + TALLOC_FREE(frame); + return false; + } + + ret = json_add_object(&root_json, + "filters", + &state->filters_json); + if (ret != 0) { + json_free(&root_json); + TALLOC_FREE(frame); + return false; + } + state->filters_json = json_empty_object; + + if (state->message_json != NULL) { + ret = json_add_object(&root_json, + "message", + state->message_json); + if (ret != 0) { + json_free(&root_json); + TALLOC_FREE(frame); + return false; + } + *state->message_json = json_empty_object; + } + + ret = json_add_object(&root_json, + "registrations", + &state->registrations_json); + if (ret != 0) { + json_free(&root_json); + TALLOC_FREE(frame); + return false; + } + state->registrations_json = json_empty_object; + + json_str = json_to_string(frame, &root_json); + json_free(&root_json); + if (json_str == NULL) { + TALLOC_FREE(frame); + return false; + } + + d_printf("%s\n", json_str); + TALLOC_FREE(frame); + return true; +#else /* not HAVE_JANSSON */ + return true; +#endif /* not HAVE_JANSSON */ +} + +static void net_witness_scan_registrations_free( + struct net_witness_scan_registrations_state *state) +{ +#ifdef HAVE_JANSSON + if (!json_is_invalid(&state->filters_json)) { + json_free(&state->filters_json); + } + if (!json_is_invalid(&state->registrations_json)) { + json_free(&state->registrations_json); + } +#endif /* HAVE_JANSSON */ + + net_witness_scan_registrations_regex_free(&state->net_name); + net_witness_scan_registrations_regex_free(&state->share_name); + net_witness_scan_registrations_regex_free(&state->ip_address); + net_witness_scan_registrations_regex_free(&state->client_computer); +} + +#ifdef HAVE_JANSSON +static int dump_registration_json(struct json_object *registrations_json, + const char *key_str, + const struct rpcd_witness_registration *rg) +{ + struct json_object jsobj = json_empty_object; + struct json_object flags_json = json_empty_object; + struct json_object context_json = json_empty_object; + struct json_object serverid_json = json_empty_object; + struct json_object auth_json = json_empty_object; + struct json_object connection_json = json_empty_object; + struct timeval tv; + struct dom_sid_buf sid_buf; + int ret = 0; + + jsobj = json_new_object(); + if (json_is_invalid(&jsobj)) { + d_fprintf(stderr, _("error setting up JSON value\n")); + goto failure; + } + + ret = json_add_flags32(&jsobj, "version", rg->version); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "net_name", rg->net_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "share_name", rg->share_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "ip_address", rg->ip_address); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "client_computer_name", rg->client_computer_name); + if (ret != 0) { + goto failure; + } + + flags_json = json_new_object(); + if (json_is_invalid(&flags_json)) { + goto failure; + } + + ret = json_add_bool(&flags_json, "WITNESS_REGISTER_IP_NOTIFICATION", + (rg->flags & WITNESS_REGISTER_IP_NOTIFICATION) ? + true : false); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&flags_json, "int", rg->flags); + if (ret != 0) { + goto failure; + } + + ret = json_add_flags32(&flags_json, "hex", rg->flags); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "flags", &flags_json); + if (ret != 0) { + goto failure; + } + flags_json = json_empty_object; + + ret = json_add_int(&jsobj, "timeout", rg->timeout); + if (ret != 0) { + goto failure; + } + + context_json = json_new_object(); + if (json_is_invalid(&context_json)) { + goto failure; + } + + ret = json_add_int(&context_json, "handle_type", rg->context_handle.handle_type); + if (ret != 0) { + goto failure; + } + + ret = json_add_guid(&context_json, "uuid", &rg->context_handle.uuid); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "context_handle", &context_json); + if (ret != 0) { + goto failure; + } + context_json = json_empty_object; + + serverid_json = json_new_object(); + if (json_is_invalid(&serverid_json)) { + goto failure; + } + + ret = json_add_int(&serverid_json, "pid", rg->server_id.pid); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&serverid_json, "task_id", rg->server_id.task_id); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&serverid_json, "vnn", rg->server_id.vnn); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&serverid_json, "unique_id", rg->server_id.unique_id); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "server_id", &serverid_json); + if (ret != 0) { + goto failure; + } + serverid_json = json_empty_object; + + auth_json = json_new_object(); + if (json_is_invalid(&auth_json)) { + goto failure; + } + + ret = json_add_string(&auth_json, "account_name", rg->account_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&auth_json, "domain_name", rg->domain_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&auth_json, + "account_sid", + dom_sid_str_buf(&rg->account_sid, &sid_buf)); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "auth", &auth_json); + if (ret != 0) { + goto failure; + } + auth_json = json_empty_object; + + connection_json = json_new_object(); + if (json_is_invalid(&connection_json)) { + goto failure; + } + + ret = json_add_string(&connection_json, "local_address", rg->local_address); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&connection_json, "remote_address", rg->remote_address); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "connection", &connection_json); + if (ret != 0) { + goto failure; + } + connection_json = json_empty_object; + + nttime_to_timeval(&tv, rg->registration_time); + ret = json_add_time(&jsobj, "registration_time", tv); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(registrations_json, key_str, &jsobj); + if (ret != 0) { + goto failure; + } + jsobj = json_empty_object; + +failure: + if (!json_is_invalid(&connection_json)) { + json_free(&connection_json); + } + if (!json_is_invalid(&auth_json)) { + json_free(&auth_json); + } + if (!json_is_invalid(&serverid_json)) { + json_free(&serverid_json); + } + if (!json_is_invalid(&context_json)) { + json_free(&context_json); + } + if (!json_is_invalid(&flags_json)) { + json_free(&flags_json); + } + if (!json_is_invalid(&jsobj)) { + json_free(&jsobj); + } + + return ret; +} +#endif /* HAVE_JANSSON */ + +static NTSTATUS net_witness_scan_registrations_dump_rg( + struct net_witness_scan_registrations_state *state, + const struct rpcd_witness_registration *rg) +{ + struct net_context *c = state->c; + struct GUID_txt_buf key_buf; + const char *key_str = GUID_buf_string(&rg->context_handle.uuid, &key_buf); + + if (c->opt_json) { +#ifdef HAVE_JANSSON + int ret; + + ret = dump_registration_json(&state->registrations_json, + key_str, + rg); + if (ret != 0) { + d_fprintf(stderr, "dump_registration_json(%s) failed\n", + key_str); + return NT_STATUS_INTERNAL_ERROR; + } +#endif /* HAVE_JANSSON */ + return NT_STATUS_OK; + } + + d_printf("%-36s %-20s %-15s %-20s %s\n", + key_str, + rg->net_name, + rg->share_name ? rg->share_name : "''", + rg->ip_address, + rg->client_computer_name); + + return NT_STATUS_OK; +} + +static void net_witness_scan_registrations_parser(TDB_DATA key, + TDB_DATA val, + void *private_data) +{ + struct net_witness_scan_registrations_state *state = + (struct net_witness_scan_registrations_state *)private_data; + DATA_BLOB val_blob = data_blob_const(val.dptr, val.dsize); + struct rpcd_witness_registration rg; + enum ndr_err_code ndr_err; + TALLOC_CTX *frame = NULL; + bool match = false; + + if (val_blob.length == 0) { + return; + } + + frame = talloc_stackframe(); + + ndr_err = ndr_pull_struct_blob(&val_blob, frame, &rg, + (ndr_pull_flags_fn_t)ndr_pull_rpcd_witness_registration); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DBG_WARNING("Invalid record in rpcd_witness_registration.tdb:" + "key '%s' ndr_pull_struct_blob - %s\n", + tdb_data_dbg(key), + ndr_errstr(ndr_err)); + state->error = ndr_map_error2ntstatus(ndr_err); + TALLOC_FREE(frame); + return; + } + + if (!serverid_exists(&rg.server_id)) { + TALLOC_FREE(frame); + return; + } + + if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) { + NDR_PRINT_DEBUG(rpcd_witness_registration, &rg); + } + + match = net_witness_scan_registrations_match(state, &rg); + if (!NT_STATUS_IS_OK(state->error)) { + TALLOC_FREE(frame); + return; + } + if (!match) { + TALLOC_FREE(frame); + return; + } + + match = state->action->match_fn(state->action->private_data, &rg); + if (!match) { + TALLOC_FREE(frame); + return; + } + + state->error = state->action->process_fn(state->action->private_data, &rg); + if (NT_STATUS_IS_OK(state->error)) { + state->error = net_witness_scan_registrations_dump_rg(state, + &rg); + } + TALLOC_FREE(frame); +} + +static int net_witness_scan_registrations_traverse_cb(struct db_record *rec, void *private_data) +{ + struct net_witness_scan_registrations_state *state = + (struct net_witness_scan_registrations_state *)private_data; + TDB_DATA key = dbwrap_record_get_key(rec); + TDB_DATA val = dbwrap_record_get_value(rec); + + net_witness_scan_registrations_parser(key, val, private_data); + + if (!NT_STATUS_IS_OK(state->error)) { + return -1; + } + + return 0; +} + +static int net_witness_scan_registrations(struct net_context *c, + struct json_object *message_json, + const struct net_witness_scan_registrations_action_state *action) +{ + struct net_witness_scan_registrations_state state = { + .c = c, + .message_json = message_json, + .action = action, + }; + struct db_context *db = NULL; + NTSTATUS status; + bool ok; + + db = net_witness_open_registration_db(); + if (db == NULL) { + d_printf("net_witness_open_registration_db() failed\n"); + return -1; + } + + ok = net_witness_scan_registrations_init(&state); + if (!ok) { + d_printf("net_witness_scan_registrations_init() failed\n"); + return -1; + } + + if (c->opt_witness_registration != NULL) { + const char *key_str = c->opt_witness_registration; + DATA_BLOB key_blob = data_blob_string_const(key_str); + TDB_DATA key = make_tdb_data(key_blob.data, key_blob.length); + + status = dbwrap_parse_record(db, + key, + net_witness_scan_registrations_parser, + &state); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + status = NT_STATUS_OK; + } + if (!NT_STATUS_IS_OK(status)) { + d_printf("dbwrap_parse_record(%s) failed: %s\n", + key_str, nt_errstr(status)); + net_witness_scan_registrations_free(&state); + return -1; + } + if (!NT_STATUS_IS_OK(state.error)) { + d_printf("net_witness_scan_registrations_parser(%s) failed: %s\n", + key_str, nt_errstr(state.error)); + net_witness_scan_registrations_free(&state); + return -1; + } + } else { + status = dbwrap_traverse_read(db, + net_witness_scan_registrations_traverse_cb, + &state, + NULL); /* count */ + if (!NT_STATUS_IS_OK(status)) { + d_printf("dbwrap_traverse_read() failed\n"); + net_witness_scan_registrations_free(&state); + return -1; + } + if (!NT_STATUS_IS_OK(state.error)) { + d_printf("net_witness_scan_registrations_traverse_cb() failed: %s\n", + nt_errstr(state.error)); + net_witness_scan_registrations_free(&state); + return -1; + } + } + + ok = net_witness_scan_registrations_finish(&state); + if (!ok) { + d_printf("net_witness_scan_registrations_finish() failed\n"); + return -1; + } + + net_witness_scan_registrations_free(&state); + return 0; +} + +struct net_witness_list_state { + struct net_context *c; +}; + +static bool net_witness_list_prepare_fn(void *private_data) +{ + return true; +} + +static bool net_witness_list_match_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + return true; +} + +static NTSTATUS net_witness_list_process_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + return NT_STATUS_OK; +} + +static void net_witness_filter_usage(void) +{ + d_printf(" Note: Only supported with clustering=yes!\n\n"); + d_printf(" Machine readable output can be generated with " + "the following option:\n" + "\n" + " --json\n" + "\n"); + d_printf(" The selection of registrations can be limited by " + "the following options:\n" + "\n" + " --witness-registration=REGISTRATION_UUID\n" + " This does a direct lookup for REGISTRATION_UUID\n" + " instead of doing a database traversal.\n" + "\n" + " The following options all take a " + "POSIX Extended Regular Expression,\n" + " which can further filter the selection of " + "registrations.\n" + " These options are applied as logical AND, " + "but each REGEX \n" + " allows specifying multiple strings using " + "the pipe symbol.\n" + "\n" + " --witness-net-name=REGEX\n" + " This specifies the 'server name' the client\n" + " registered for monitoring.\n" + "\n" + " --witness-share-name=REGEX\n" + " This specifies the 'share name' the client\n" + " registered for monitoring.\n" + " Note that the share name is optional in the\n" + " registration, otherwise an empty string is \n" + " matched.\n" + "\n" + " --witness-ip-address=REGEX\n" + " This specifies the ip address the client\n" + " registered for monitoring.\n" + "\n" + " --witness-client-computer-name=REGEX\n" + " This specifies the client computer name the client\n" + " specified in the registration.\n" + " Note it is just a string chosen by the " + "client itself.\n" + "\n"); +} + +static void net_witness_list_usage(void) +{ + d_printf("%s\n" + "net witness list\n" + " %s\n\n", + _("Usage:"), + _("List witness registrations " + "from rpcd_witness_registration.tdb")); + net_witness_filter_usage(); +} + +static int net_witness_list(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct net_witness_list_state state = { .c = c, }; + struct net_witness_scan_registrations_action_state action = { + .prepare_fn = net_witness_list_prepare_fn, + .match_fn = net_witness_list_match_fn, + .process_fn = net_witness_list_process_fn, + .private_data = &state, + }; + int ret = -1; + + if (c->display_usage) { + net_witness_list_usage(); + goto out; + } + + if (argc != 0) { + net_witness_list_usage(); + goto out; + } + + if (!lp_clustering()) { + d_printf("ERROR: Only supported with clustering=yes!\n\n"); + goto out; + } + + ret = net_witness_scan_registrations(c, NULL, &action); + if (ret != 0) { + d_printf("net_witness_scan_registrations() failed\n"); + goto out; + } + + ret = 0; +out: + TALLOC_FREE(frame); + return ret; +} + +int net_witness(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + net_witness_list, + NET_TRANSPORT_LOCAL, + N_("List witness registrations " + "from rpcd_witness_registration.tdb"), + N_("net witness list\n" + " List witness registrations " + "from rpcd_witness_registration.tdb"), + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net witness", func); +} diff --git a/source3/utils/wscript_build b/source3/utils/wscript_build index 7a28de857e1..ca57e8004f2 100644 --- a/source3/utils/wscript_build +++ b/source3/utils/wscript_build @@ -229,6 +229,7 @@ bld.SAMBA3_BINARY('net', net_afs.c net_notify.c net_tdb.c + net_witness.c net_vfs.c ../registry/reg_format.c ../registry/reg_import.c