aboutsummaryrefslogtreecommitdiffstats
path: root/src/neighbor/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/neighbor/plugins')
-rw-r--r--src/neighbor/plugins/SmbclientNeighborPlugin.cxx285
-rw-r--r--src/neighbor/plugins/SmbclientNeighborPlugin.hxx27
2 files changed, 312 insertions, 0 deletions
diff --git a/src/neighbor/plugins/SmbclientNeighborPlugin.cxx b/src/neighbor/plugins/SmbclientNeighborPlugin.cxx
new file mode 100644
index 000000000..8dc537e94
--- /dev/null
+++ b/src/neighbor/plugins/SmbclientNeighborPlugin.cxx
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "SmbclientNeighborPlugin.hxx"
+#include "lib/smbclient/Init.hxx"
+#include "neighbor/NeighborPlugin.hxx"
+#include "neighbor/Explorer.hxx"
+#include "neighbor/Listener.hxx"
+#include "neighbor/Info.hxx"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+#include "thread/Thread.hxx"
+#include "thread/Name.hxx"
+#include "util/Macros.hxx"
+#include "util/Domain.hxx"
+#include "util/Error.hxx"
+#include "Log.hxx"
+
+#include <libsmbclient.h>
+
+#include <list>
+#include <algorithm>
+
+static constexpr Domain smbclient_domain("smbclient");
+
+class SmbclientNeighborExplorer final : public NeighborExplorer {
+ struct Server {
+ std::string name, comment;
+
+ bool alive;
+
+ Server(std::string &&_name, std::string &&_comment)
+ :name(std::move(_name)), comment(std::move(_comment)),
+ alive(true) {}
+ Server(const Server &) = delete;
+
+ gcc_pure
+ bool operator==(const Server &other) const {
+ return name == other.name;
+ }
+
+ gcc_pure
+ NeighborInfo Export() const {
+ return { "smb://" + name + "/", comment };
+ }
+ };
+
+ Thread thread;
+
+ mutable Mutex mutex;
+ Cond cond;
+
+ List list;
+
+ bool quit;
+
+public:
+ SmbclientNeighborExplorer(NeighborListener &_listener)
+ :NeighborExplorer(_listener) {}
+
+ /* virtual methods from class NeighborExplorer */
+ virtual bool Open(Error &error) override;
+ virtual void Close() override;
+ virtual List GetList() const override;
+
+private:
+ void Run();
+ void ThreadFunc();
+ static void ThreadFunc(void *ctx);
+};
+
+bool
+SmbclientNeighborExplorer::Open(Error &error)
+{
+ quit = false;
+ return thread.Start(ThreadFunc, this, error);
+}
+
+void
+SmbclientNeighborExplorer::Close()
+{
+ mutex.lock();
+ quit = true;
+ cond.signal();
+ mutex.unlock();
+
+ thread.Join();
+}
+
+NeighborExplorer::List
+SmbclientNeighborExplorer::GetList() const
+{
+ const ScopeLock protect(mutex);
+ /*
+ List list;
+ for (const auto &i : servers)
+ list.emplace_front(i.Export());
+ */
+ return list;
+}
+
+static void
+ReadServer(NeighborExplorer::List &list, const smbc_dirent &e)
+{
+ const std::string name(e.name, e.namelen);
+ const std::string comment(e.comment, e.commentlen);
+
+ NeighborInfo info{
+ "smb://" + name,
+ name + " (" + comment + ")",
+ };
+
+ list.emplace_front(std::move(info));
+}
+
+static void
+ReadServers(NeighborExplorer::List &list, const char *uri);
+
+static void
+ReadWorkgroup(NeighborExplorer::List &list, const std::string &name)
+{
+ std::string uri = "smb://" + name;
+ ReadServers(list, uri.c_str());
+}
+
+static void
+ReadEntry(NeighborExplorer::List &list, const smbc_dirent &e)
+{
+ switch (e.smbc_type) {
+ case SMBC_WORKGROUP:
+ ReadWorkgroup(list, std::string(e.name, e.namelen));
+ break;
+
+ case SMBC_SERVER:
+ ReadServer(list, e);
+ break;
+ }
+}
+
+static void
+ReadServers(NeighborExplorer::List &list, int fd)
+{
+ smbc_dirent *e;
+ while ((e = smbc_readdir(fd)) != nullptr)
+ ReadEntry(list, *e);
+
+ smbc_closedir(fd);
+}
+
+static void
+ReadServers(NeighborExplorer::List &list, const char *uri)
+{
+ int fd = smbc_opendir(uri);
+ if (fd >= 0) {
+ ReadServers(list, fd);
+ smbc_closedir(fd);
+ } else
+ FormatErrno(smbclient_domain, "smbc_opendir('%s') failed",
+ uri);
+}
+
+gcc_pure
+static NeighborExplorer::List
+DetectServers()
+{
+ NeighborExplorer::List list;
+ ReadServers(list, "smb://");
+ return list;
+}
+
+gcc_pure
+static NeighborExplorer::List::const_iterator
+FindBeforeServerByURI(NeighborExplorer::List::const_iterator prev,
+ NeighborExplorer::List::const_iterator end,
+ const std::string &uri)
+{
+ for (auto i = std::next(prev); i != end; prev = i, i = std::next(prev))
+ if (i->uri == uri)
+ return prev;
+
+ return end;
+}
+
+inline void
+SmbclientNeighborExplorer::Run()
+{
+ List found = DetectServers(), lost;
+
+ mutex.lock();
+
+ const auto found_before_begin = found.before_begin();
+ const auto found_end = found.end();
+
+ for (auto prev = list.before_begin(), i = std::next(prev), end = list.end();
+ i != end; i = std::next(prev)) {
+ auto f = FindBeforeServerByURI(found_before_begin, found_end,
+ i->uri);
+ if (f != found_end) {
+ /* still visible: remove from "found" so we
+ don't believe it's a new one */
+ *i = std::move(*std::next(f));
+ found.erase_after(f);
+ prev = i;
+ } else {
+ /* can't see it anymore: move to "lost" */
+ lost.splice_after(lost.before_begin(), list, prev);
+ }
+ }
+
+ for (auto prev = found_before_begin, i = std::next(prev);
+ i != found_end; prev = i, i = std::next(prev))
+ list.push_front(*i);
+
+ mutex.unlock();
+
+ for (auto &i : lost)
+ listener.LostNeighbor(i);
+
+ for (auto &i : found)
+ listener.FoundNeighbor(i);
+}
+
+inline void
+SmbclientNeighborExplorer::ThreadFunc()
+{
+ mutex.lock();
+
+ while (!quit) {
+ mutex.unlock();
+
+ Run();
+
+ mutex.lock();
+ if (quit)
+ break;
+
+ // TODO: sleep for how long?
+ cond.timed_wait(mutex, 10000);
+ }
+
+ mutex.unlock();
+}
+
+void
+SmbclientNeighborExplorer::ThreadFunc(void *ctx)
+{
+ SetThreadName("smbclient");
+
+ SmbclientNeighborExplorer &e = *(SmbclientNeighborExplorer *)ctx;
+ e.ThreadFunc();
+}
+
+static NeighborExplorer *
+smbclient_neighbor_create(gcc_unused EventLoop &loop,
+ NeighborListener &listener,
+ gcc_unused const config_param &param,
+ gcc_unused Error &error)
+{
+ if (!SmbclientInit(error))
+ return nullptr;
+
+ return new SmbclientNeighborExplorer(listener);
+}
+
+const NeighborPlugin smbclient_neighbor_plugin = {
+ "smbclient",
+ smbclient_neighbor_create,
+};
diff --git a/src/neighbor/plugins/SmbclientNeighborPlugin.hxx b/src/neighbor/plugins/SmbclientNeighborPlugin.hxx
new file mode 100644
index 000000000..12ec9c0cd
--- /dev/null
+++ b/src/neighbor/plugins/SmbclientNeighborPlugin.hxx
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_NEIGHBOR_SMBCLIENT_HXX
+#define MPD_NEIGHBOR_SMBCLIENT_HXX
+
+struct NeighborPlugin;
+
+extern const NeighborPlugin smbclient_neighbor_plugin;
+
+#endif