From 609867ef7c76265a422ade6819ad98288c2d6257 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 22 Oct 2020 17:23:44 +0200 Subject: [PATCH] test: Add a first unit test for notifyd Use the notifyd "messaging" protocol to check if notifyd works at all Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/selftest/tests.py | 5 + source3/smbd/notifyd/test_notifyd.c | 262 ++++++++++++++++++++++++++++ source3/smbd/notifyd/wscript_build | 12 ++ 3 files changed, 279 insertions(+) create mode 100644 source3/smbd/notifyd/test_notifyd.c diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index b9c44398e81..6cfb98e2bb6 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -905,6 +905,11 @@ plantestsuite( [os.path.join(samba3srcdir, "../nsswitch/tests/test_ticket_expiry.sh"), '$DOMAIN']) +plansmbtorture4testsuite( + "notifyd", + "fileserver:local", + '//foo/bar -U%') + test = 'rpc.lsa.lookupsids' auth_options = ["", "ntlm", "spnego", "spnego,ntlm", "spnego,smb1", "spnego,smb2"] signseal_options = ["", ",connect", ",packet", ",sign", ",seal"] diff --git a/source3/smbd/notifyd/test_notifyd.c b/source3/smbd/notifyd/test_notifyd.c new file mode 100644 index 00000000000..c9d223fdd53 --- /dev/null +++ b/source3/smbd/notifyd/test_notifyd.c @@ -0,0 +1,262 @@ +/* + * 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 "replace.h" +#include "fcn_wait.h" +#include "notifyd.h" +#include "notifyd_db.h" +#include "messages.h" +#include "lib/util/server_id.h" +#include "lib/util/server_id_db.h" +#include "lib/util/tevent_ntstatus.h" +#include "lib/torture/torture.h" +#include "torture/local/proto.h" +#include "lib/param/loadparm.h" +#include "source3/param/loadparm.h" +#include "source4/torture/smbtorture.h" + +struct fcn_test_state { + struct tevent_req *fcn_req; + bool got_trigger; +}; + +static void fcn_test_done(struct tevent_req *subreq); + +static struct tevent_req *fcn_test_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct messaging_context *msg_ctx, + struct server_id notifyd, + const char *fcn_path, + uint32_t fcn_filter, + uint32_t fcn_subdir_filter, + const char *trigger_path, + uint32_t trigger_action, + uint32_t trigger_filter) +{ + struct tevent_req *req = NULL; + struct fcn_test_state *state = NULL; + struct notify_trigger_msg msg; + struct iovec iov[2]; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, struct fcn_test_state); + if (req == NULL) { + return NULL; + } + + state->fcn_req = fcn_wait_send( + state, + ev, + msg_ctx, + notifyd, + fcn_path, + fcn_filter, + fcn_subdir_filter); + if (tevent_req_nomem(state->fcn_req, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->fcn_req, fcn_test_done, req); + + msg = (struct notify_trigger_msg) { + .when = timespec_current(), + .action = trigger_action, + .filter = trigger_filter, + }; + iov[0] = (struct iovec) { + .iov_base = &msg, + .iov_len = offsetof(struct notify_trigger_msg, path), + }; + iov[1] = (struct iovec) { + .iov_base = discard_const_p(char, trigger_path), + .iov_len = strlen(trigger_path)+1, + }; + + status = messaging_send_iov( + msg_ctx, + notifyd, + MSG_SMB_NOTIFY_TRIGGER, + iov, + ARRAY_SIZE(iov), + NULL, + 0); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + return req; +} + +static void fcn_test_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct fcn_test_state *state = tevent_req_data( + req, struct fcn_test_state); + NTSTATUS status; + bool ok; + + SMB_ASSERT(subreq == state->fcn_req); + + status = fcn_wait_recv(subreq, NULL, NULL, NULL, NULL); + + if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) { + TALLOC_FREE(subreq); + state->fcn_req = NULL; + tevent_req_done(req); + return; + } + + if (tevent_req_nterror(req, status)) { + TALLOC_FREE(subreq); + state->fcn_req = NULL; + return; + } + + state->got_trigger = true; + + ok = tevent_req_cancel(subreq); + if (!ok) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } +} + +static NTSTATUS fcn_test_recv(struct tevent_req *req, bool *got_trigger) +{ + struct fcn_test_state *state = tevent_req_data( + req, struct fcn_test_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (got_trigger != NULL) { + *got_trigger = state->got_trigger; + } + + return NT_STATUS_OK; +} + +static NTSTATUS fcn_test( + struct messaging_context *msg_ctx, + struct server_id notifyd, + const char *fcn_path, + uint32_t fcn_filter, + uint32_t fcn_subdir_filter, + const char *trigger_path, + uint32_t trigger_action, + uint32_t trigger_filter, + bool *got_trigger) +{ + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(msg_ctx); + if (ev == NULL) { + goto fail; + } + req = fcn_test_send( + ev, + ev, + msg_ctx, + notifyd, + fcn_path, + fcn_filter, + fcn_subdir_filter, + trigger_path, + trigger_action, + trigger_filter); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = fcn_test_recv(req, got_trigger); +fail: + TALLOC_FREE(ev); + return status; +} + +static bool test_notifyd_trigger1(struct torture_context *tctx) +{ + struct messaging_context *msg_ctx = NULL; + struct server_id_db *names = NULL; + struct server_id notifyd; + NTSTATUS status; + bool got_trigger = false; + bool ok; + + /* + * Basic filechangenotify test: Wait for /home, trigger on + * /home/foo, check an event was received + */ + + lp_load_global(tctx->lp_ctx->szConfigFile); + + msg_ctx = messaging_init(tctx, tctx->ev); + torture_assert_not_null(tctx, msg_ctx, "messaging_init"); + + names = messaging_names_db(msg_ctx); + ok = server_id_db_lookup_one(names, "notify-daemon", ¬ifyd); + torture_assert(tctx, ok, "server_id_db_lookup_one"); + + status = fcn_test( + msg_ctx, + notifyd, + "/home", + UINT32_MAX, + UINT32_MAX, + "/home/foo", + UINT32_MAX, + UINT32_MAX, + &got_trigger); + torture_assert_ntstatus_ok(tctx, status, "fcn_test"); + torture_assert(tctx, got_trigger, "got_trigger"); + + return true; +} + +NTSTATUS torture_notifyd_init(TALLOC_CTX *mem_ctx); +NTSTATUS torture_notifyd_init(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = NULL; + struct torture_tcase *tcase = NULL; + bool ok; + + suite = torture_suite_create(mem_ctx, "notifyd"); + if (suite == NULL) { + goto fail; + } + + tcase = torture_suite_add_simple_test( + suite, "trigger1", test_notifyd_trigger1); + if (tcase == NULL) { + goto fail; + } + + suite->description = "notifyd unit tests"; + + ok = torture_register_suite(mem_ctx, suite); + if (!ok) { + goto fail; + } + return NT_STATUS_OK; +fail: + TALLOC_FREE(suite); + return NT_STATUS_NO_MEMORY; +} diff --git a/source3/smbd/notifyd/wscript_build b/source3/smbd/notifyd/wscript_build index 43b9b76843e..cd56ba7763f 100644 --- a/source3/smbd/notifyd/wscript_build +++ b/source3/smbd/notifyd/wscript_build @@ -30,3 +30,15 @@ bld.SAMBA3_BINARY('notifydd', deps='''notifyd smbconf ''') + +TORTURE_NOTIFYD_SOURCE='test_notifyd.c' +TORTURE_NOTIFYD_DEPS='fcn_wait' + +bld.SAMBA_MODULE('TORTURE_NOTIFYD', + source=TORTURE_NOTIFYD_SOURCE, + subsystem='smbtorture', + init_function='torture_notifyd_init', + deps=TORTURE_NOTIFYD_DEPS, + internal_module=True, + enabled=bld.PYTHON_BUILD_IS_ENABLED() + )