s3:utils: add mdfind
authorRalph Boehme <slow@samba.org>
Thu, 2 May 2019 19:33:46 +0000 (21:33 +0200)
committerRalph Boehme <slow@samba.org>
Wed, 9 Oct 2019 14:35:29 +0000 (14:35 +0000)
A small command line tool to run macOS Spotlight searches against an SMB server
that runs the Spotlight mdssvc RPC service, including macOS and Samba.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Noel Power <noel.power@suse.com>
docs-xml/manpages/mdfind.1.xml [new file with mode: 0644]
docs-xml/wscript_build
source3/utils/mdfind.c [new file with mode: 0644]
source3/utils/wscript_build

diff --git a/docs-xml/manpages/mdfind.1.xml b/docs-xml/manpages/mdfind.1.xml
new file mode 100644 (file)
index 0000000..0deef06
--- /dev/null
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="mdfind.1">
+
+  <refmeta>
+    <refentrytitle>mdfind</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo class="source">Samba</refmiscinfo>
+    <refmiscinfo class="manual">User Commands</refmiscinfo>
+    <refmiscinfo class="version">&doc.version;</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>mdfind</refname>
+    <refpurpose>Run Spotlight searches against an SMB server</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>mvxattr</command>
+      <arg choice="req">server</arg>
+      <arg choice="req">sharename</arg>
+      <arg choice="req">query</arg>
+      <arg choice="opt">-p, --path</arg>
+      <arg choice="opt">-L, --live</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+
+    <para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle>
+    <manvolnum>1</manvolnum></citerefentry> suite.</para>
+
+    <para>mdfind is a simple utility to run Spotlight searches against an SMB server
+    that runs the Spotlight <emphasis>mdssvc</emphasis> RPC service.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist>
+      <varlistentry>
+       <term>server</term>
+       <listitem>
+         <para>The SMB server name or IP address to connect to.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>sharename</term>
+       <listitem>
+         <para>The name of a share on the server.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>query</term>
+       <listitem>
+         <para>The query expression syntax is a simplified form of filename
+         globbing familiar to shell users. Queries have the following
+         format:</para>
+
+         <para>attribute=="value"</para>
+
+         <para>For queries against a Samba server with Spotlight enabled using
+         the Elasticsearch backend, the list of supported metadata attributes
+         is given by the JSON attribute mapping file, typically installed at
+         <filename>/usr/share/samba/mdssvc/elasticsearch_mappings.json</filename>
+         </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-p PATH, --path=PATH</term>
+       <listitem>
+         <para>Server side path to search, defaults to
+         <emphasis>"/"</emphasis></para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-L, --live</term>
+       <listitem><para>Query remains running.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>EXAMPLES</title>
+
+    <para>Search all indexed metadata attributes, exact match:</para>
+    <programlisting>
+      '*=="Samba"'
+    </programlisting>
+
+    <para>Search all indexed metadata attributes, prefix match:</para>
+    <programlisting>
+      '*=="Samba*"'
+    </programlisting>
+
+    <para>Search by filename:</para>
+    <programlisting>
+      'kMDItemFSName=="Samba*"'
+    </programlisting>
+
+    <para>Search by date:</para>
+    <programlisting>
+      'kMDItemFSContentChangeDate&lt;$time.iso(2018-10-01T10:00:00Z)'
+    </programlisting>
+
+    <para>Search files's content:</para>
+    <programlisting>
+      'kMDItemTextContent=="Samba*"'
+    </programlisting>
+
+    <para>Expressions:</para>
+    <programlisting>
+      kMDItemFSName=="Samba*"||kMDItemTextContent=="Tango*"'
+    </programlisting>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+
+    <para>File Metadata Search Programming Guide
+    <ulink url="https://developer.apple.com/library/archive/documentation/Carbon/Conceptual/SpotlightQuery/Concepts/Introduction.html">
+    https://developer.apple.com/library/archive/documentation/Carbon/Conceptual/SpotlightQuery/Concepts/Introduction.html</ulink>
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>VERSION</title>
+
+    <para>This man page is part of version &doc.version; of the Samba suite.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>AUTHOR</title>
+
+    <para>The original Samba software and related utilities were created by
+    Andrew Tridgell. Samba is now developed by the Samba Team as an Open
+    Source project similar to the way the Linux kernel is developed.</para>
+
+    <para>The mdfind manpage was written by Ralph Boehme.</para>
+  </refsect1>
+</refentry>
index b85309d4eade4faabf87086d7816d641c22136c9..70f5b43dd3329d9b124961c5a2c4ed41b5ba1a4c 100644 (file)
@@ -19,6 +19,7 @@ manpages='''
          manpages/libsmbclient.7
          manpages/lmhosts.5
          manpages/log2pcap.1
+         manpages/mdfind.1
          manpages/mvxattr.1
          manpages/net.8
          manpages/nmbd.8
diff --git a/source3/utils/mdfind.c b/source3/utils/mdfind.c
new file mode 100644 (file)
index 0000000..d8ede52
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2019, Ralph Boehme <slow@samba.org.>
+ *
+ * This library 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 2 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "includes.h"
+#include "lib/util/debug.h"
+#include "popt_common.h"
+#include "popt_common_cmdline.h"
+#include "lib/cmdline_contexts.h"
+#include "param.h"
+#include "auth_info.h"
+#include "client.h"
+#include "libsmb/proto.h"
+#include "librpc/rpc/rpc_common.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/cli_mdssvc.h"
+#include "librpc/gen_ndr/ndr_mdssvc_c.h"
+
+static char *opt_path;
+static int opt_live;
+
+static struct poptOption long_options[] = {
+       POPT_AUTOHELP
+       {
+               .longName  = "path",
+               .shortName = 'p',
+               .argInfo   = POPT_ARG_STRING,
+               .arg       = &opt_path,
+               .descrip   = "Server-relative search path",
+       },
+       {
+               .longName  = "live",
+               .shortName = 'L',
+               .argInfo   = POPT_ARG_NONE,
+               .arg       = &opt_live,
+               .descrip   = "live query",
+       },
+       POPT_COMMON_SAMBA
+       POPT_COMMON_CREDENTIALS
+       POPT_TABLEEND
+};
+
+int main(int argc, char **argv)
+{
+       const char **const_argv = discard_const_p(const char *, argv);
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_context *ev = NULL;
+       struct user_auth_info *auth = NULL;
+       struct rpc_pipe_client *rpccli = NULL;
+       struct mdscli_ctx *mdscli_ctx = NULL;
+       struct mdscli_search_ctx *search = NULL;
+       const char *auth_username = NULL;
+       const char *auth_passwd = NULL;
+       const char *auth_domain = NULL;
+       const char *server = NULL;
+       const char *share = NULL;
+       const char *mds_query = NULL;
+       struct cli_state *cli = NULL;
+       char *basepath = NULL;
+       uint32_t flags = 0;
+       int signing_state = SMB_SIGNING_IPC_DEFAULT;
+       uint64_t *cnids = NULL;
+       size_t ncnids;
+       size_t i;
+       int opt;
+       poptContext pc;
+       NTSTATUS status;
+       bool ok;
+
+       setup_logging(argv[0], DEBUG_STDERR);
+       smb_init_locale();
+       lp_set_cmdline("log level", "1");
+
+       pc = poptGetContext(argv[0],
+                           argc,
+                           const_argv,
+                           long_options,
+                           POPT_CONTEXT_KEEP_FIRST);
+
+       poptSetOtherOptionHelp(pc, "mdfind [OPTIONS] <server> <share> <query>\n");
+
+       while ((opt = poptGetNextOpt(pc)) != -1) {
+               DBG_ERR("Invalid option %s: %s\n",
+                       poptBadOption(pc, 0),
+                       poptStrerror(opt));
+               poptPrintHelp(pc, stderr, 0);
+               goto fail;
+       }
+
+       poptGetArg(pc); /* Drop argv[0], the program name */
+       server = poptGetArg(pc);
+       share = poptGetArg(pc);
+       mds_query = poptGetArg(pc);
+
+       if (server == NULL || mds_query == NULL) {
+               poptPrintHelp(pc, stderr, 0);
+               goto fail;
+       }
+
+       popt_burn_cmdline_password(argc, argv);
+
+       if ((server[0] == '/' && server[1] == '/') ||
+           (server[0] == '\\' && server[1] ==  '\\'))
+       {
+               server += 2;
+       }
+
+       auth = popt_get_cmdline_auth_info();
+       auth_username = get_cmdline_auth_info_username(auth);
+       auth_passwd = get_cmdline_auth_info_password(auth);
+       auth_domain = get_cmdline_auth_info_domain(auth);
+
+       signing_state = get_cmdline_auth_info_signing_state(auth);
+       switch (signing_state) {
+       case SMB_SIGNING_OFF:
+               lp_set_cmdline("client ipc signing", "no");
+               break;
+       case SMB_SIGNING_REQUIRED:
+               lp_set_cmdline("client ipc signing", "required");
+               break;
+       }
+
+       if (get_cmdline_auth_info_use_kerberos(auth)) {
+               flags |= CLI_FULL_CONNECTION_USE_KERBEROS |
+                        CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
+       }
+       if (get_cmdline_auth_info_use_ccache(auth)) {
+               flags |= CLI_FULL_CONNECTION_USE_CCACHE;
+       }
+       if (get_cmdline_auth_info_use_pw_nt_hash(auth)) {
+               flags |= CLI_FULL_CONNECTION_USE_NT_HASH;
+       }
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+
+       cmdline_messaging_context(get_dyn_CONFIGFILE());
+
+       ok = lp_load_client(get_dyn_CONFIGFILE());
+       if (!ok) {
+               fprintf(stderr, "ERROR: Can't load %s\n",
+                       get_dyn_CONFIGFILE());
+               exit(1);
+       }
+
+       status = cli_full_connection(&cli,
+                                    lp_netbios_name(),
+                                    server,
+                                    NULL,
+                                    0,
+                                    "IPC$",
+                                    "IPC",
+                                    auth_username,
+                                    auth_domain,
+                                    auth_passwd,
+                                    flags,
+                                    SMB_SIGNING_IPC_DEFAULT);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("Cannot connect to server: %s\n", nt_errstr(status));
+               goto fail;
+       }
+
+       if (get_cmdline_auth_info_smb_encrypt(auth)) {
+               status = cli_cm_force_encryption(cli,
+                                                auth_username,
+                                                auth_passwd,
+                                                auth_domain,
+                                                "IPC$");
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto fail;
+               }
+       }
+
+       status = cli_rpc_pipe_open_noauth_transport(cli,
+                                                   NCACN_NP,
+                                                   &ndr_table_mdssvc,
+                                                   &rpccli);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       status = mdscli_connect(frame,
+                               rpccli->binding_handle,
+                               share,
+                               "/foo/bar",
+                               &mdscli_ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("Failed to connect mdssvc\n");
+               goto fail;
+       }
+
+       if (opt_path == NULL) {
+               basepath = mdscli_get_basepath(frame, mdscli_ctx);
+       } else {
+               basepath = talloc_strdup(frame, opt_path);
+       }
+       if (basepath == NULL) {
+               goto fail;
+       }
+
+       status = mdscli_search(frame,
+                              mdscli_ctx,
+                              mds_query,
+                              basepath,
+                              opt_live == 1 ? true : false,
+                              &search);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("mdscli_search failed\n");
+               goto fail;
+       }
+
+       if (!opt_live) {
+               sleep(1);
+       }
+
+       while (true) {
+               status = mdscli_get_results(frame,
+                                           search,
+                                           &cnids);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES)) {
+                       if (opt_live) {
+                               sleep(1);
+                               continue;
+                       }
+                       break;
+               }
+               if (!NT_STATUS_IS_OK(status)) {
+                       printf("mdscli_get_results failed\n");
+                       goto fail;
+               }
+
+               ncnids = talloc_array_length(cnids);
+               if (ncnids == 0) {
+                       break;
+               }
+
+               for (i = 0; i < ncnids; i++) {
+                       char *path = NULL;
+
+                       status = mdscli_get_path(frame,
+                                                mdscli_ctx,
+                                                cnids[i],
+                                                &path);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               printf("Get path for CNID 0x%"PRIx64" failed\n",
+                                      cnids[i]);
+                               goto fail;
+                       }
+                       printf("%s\n", path);
+                       TALLOC_FREE(path);
+               }
+       }
+
+       status = mdscli_close_search(&search);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("mdscli_close_search failed\n");
+               goto fail;
+       }
+
+       status = mdscli_disconnect(mdscli_ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("mdscli_disconnect failed\n");
+               goto fail;
+       }
+
+       TALLOC_FREE(frame);
+       poptFreeContext(pc);
+       return 0;
+
+fail:
+       TALLOC_FREE(frame);
+       return 1;
+}
index b6ff3697ca0efec77e63a8aeb79d9bf4c5279ade..3e0539b2cea5b94f2f88e5fba5611ad2784036be 100644 (file)
@@ -310,3 +310,17 @@ bld.SAMBA3_BINARY('smbstatus',
                       PROFILE
                       CONN_TDB
                       ''')
+
+bld.SAMBA3_BINARY('mdfind',
+                 source='mdfind.c',
+                 deps='''
+                 talloc
+                 tevent
+                 smbconf
+                 popt_samba3
+                 popt_samba3_cmdline
+                 libsmb
+                 msrpc3
+                 RPCCLI_MDSSVC
+                 mdssvc
+                 ''')