summaryrefslogtreecommitdiffstats
path: root/mojo/shell
diff options
context:
space:
mode:
authorcmasone@chromium.org <cmasone@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-28 11:53:16 +0000
committercmasone@chromium.org <cmasone@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-28 11:53:16 +0000
commit65eedf7cf94d19be6c996eb71a013b1128d7c951 (patch)
tree828de8b51075468f1c569d25d4ad21acd0caab3b /mojo/shell
parent2f383bbe683a27ef06bd97c8375008b1f98742d6 (diff)
downloadchromium_src-65eedf7cf94d19be6c996eb71a013b1128d7c951.zip
chromium_src-65eedf7cf94d19be6c996eb71a013b1128d7c951.tar.gz
chromium_src-65eedf7cf94d19be6c996eb71a013b1128d7c951.tar.bz2
Implement a trivial Mojo EchoService that can be connected to over DBus
Some systems might like to use Mojo IPC to talk to services that are running outside the auspices of the Mojo shell. DBus is one potential channel to use for bootstrapping such a connection. In order to allow the externally-running service to accept connections from a Mojo shell, we need to get it a ShellHandle. This change defines a DBusServiceLoader that implements the ServiceLoader interface. DBusServiceLoader creates a dedicated MessagePipe, passes a handle to one end to the desired service over DBus, and then passes the ShellHandle over that pipe. This class assumes the following: 1) Your service is already running, 2) Your service implements the Mojo ExternalService API (from external_service.mojom). 3) Your service exports an object that implements the org.chromium.Mojo DBus interface: <interface name="org.chromium.Mojo"> <method name="ConnectChannel"> <arg type="h" name="file_descriptor" direction="in" /> </method> </interface> This change also includes a trivial DBusEchoService that can be "loaded" using the DBusServiceLoader, as well as a mojo example app that connects to this service. BUG=364903 TEST=run mojo_dbus_echo_service in one shell, and then load mojo_dbus_echo in the mojo shell. "who" should successfully echo between the two. Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=265927 Review URL: https://codereview.chromium.org/242763003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266508 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'mojo/shell')
-rw-r--r--mojo/shell/DEPS1
-rw-r--r--mojo/shell/context.cc11
-rw-r--r--mojo/shell/dbus_service_loader_linux.cc185
-rw-r--r--mojo/shell/dbus_service_loader_linux.h90
-rw-r--r--mojo/shell/external_service.mojom16
5 files changed, 303 insertions, 0 deletions
diff --git a/mojo/shell/DEPS b/mojo/shell/DEPS
index 5cf2176..7b96bb6 100644
--- a/mojo/shell/DEPS
+++ b/mojo/shell/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+dbus",
"+mojo/embedder",
"+net",
"+ui/gl",
diff --git a/mojo/shell/context.cc b/mojo/shell/context.cc
index df86d14..d5bb30e 100644
--- a/mojo/shell/context.cc
+++ b/mojo/shell/context.cc
@@ -5,6 +5,7 @@
#include "mojo/shell/context.h"
#include "base/command_line.h"
+#include "build/build_config.h"
#include "mojo/embedder/embedder.h"
#include "mojo/gles2/gles2_support_impl.h"
#include "mojo/service_manager/service_loader.h"
@@ -17,6 +18,10 @@
#include "mojo/shell/switches.h"
#include "mojo/spy/spy.h"
+#if defined(OS_LINUX)
+#include "mojo/shell/dbus_service_loader_linux.h"
+#endif // defined(OS_LINUX)
+
namespace mojo {
namespace shell {
@@ -66,6 +71,12 @@ Context::Context()
scoped_ptr<ServiceLoader>(new NativeViewportServiceLoader(this)),
GURL("mojo:mojo_native_viewport_service"));
+#if defined(OS_LINUX)
+ service_manager_.SetLoaderForScheme(
+ scoped_ptr<ServiceLoader>(new DBusServiceLoader(this)),
+ "dbus");
+#endif // defined(OS_LINUX)
+
if (cmdline->HasSwitch(switches::kSpy)) {
spy_.reset(new mojo::Spy(&service_manager_,
cmdline->GetSwitchValueASCII(switches::kSpy)));
diff --git a/mojo/shell/dbus_service_loader_linux.cc b/mojo/shell/dbus_service_loader_linux.cc
new file mode 100644
index 0000000..a476d25
--- /dev/null
+++ b/mojo/shell/dbus_service_loader_linux.cc
@@ -0,0 +1,185 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/shell/dbus_service_loader_linux.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "dbus/bus.h"
+#include "dbus/file_descriptor.h"
+#include "dbus/message.h"
+#include "dbus/object_path.h"
+#include "dbus/object_proxy.h"
+#include "mojo/common/channel_init.h"
+#include "mojo/embedder/platform_channel_pair.h"
+#include "mojo/public/cpp/bindings/allocation_scope.h"
+#include "mojo/public/cpp/bindings/interface.h"
+#include "mojo/public/cpp/bindings/remote_ptr.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/external_service.mojom.h"
+#include "mojo/shell/keep_alive.h"
+
+namespace mojo {
+namespace shell {
+
+// Manages the connection to a single externally-running service.
+class DBusServiceLoader::LoadContext : public mojo::ExternalServiceHost {
+ public:
+ // Kicks off the attempt to bootstrap a connection to the externally-running
+ // service specified by url_.
+ // Creates a MessagePipe and passes one end over DBus to the service. Then,
+ // calls ExternalService::Activate(ShellHandle) over the now-shared pipe.
+ LoadContext(DBusServiceLoader* loader,
+ const scoped_refptr<dbus::Bus>& bus,
+ const GURL& url,
+ ScopedShellHandle shell_handle)
+ : loader_(loader),
+ bus_(bus),
+ service_dbus_proxy_(NULL),
+ url_(url),
+ shell_handle_(shell_handle.Pass()),
+ keep_alive_(loader->context_) {
+ base::PostTaskAndReplyWithResult(
+ loader_->context_->task_runners()->io_runner(),
+ FROM_HERE,
+ base::Bind(&LoadContext::CreateChannelOnIOThread,
+ base::Unretained(this)),
+ base::Bind(&LoadContext::ConnectChannel, base::Unretained(this)));
+ }
+
+ virtual ~LoadContext() {
+ }
+
+ private:
+ // Sets up a pipe to share with the externally-running service and returns
+ // the endpoint that should be sent over DBus.
+ // The FD for the endpoint must be validated on an IO thread.
+ scoped_ptr<dbus::FileDescriptor> CreateChannelOnIOThread() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ CHECK(bus_->Connect());
+ CHECK(bus_->SetUpAsyncOperations());
+
+ embedder::PlatformChannelPair channel_pair;
+ channel_init_.reset(new common::ChannelInit);
+ mojo::ScopedMessagePipeHandle bootstrap_message_pipe =
+ channel_init_->Init(channel_pair.PassServerHandle().release().fd,
+ loader_->context_->task_runners()->io_runner());
+ CHECK(bootstrap_message_pipe.is_valid());
+
+ external_service_.reset(
+ mojo::ScopedExternalServiceHandle::From(bootstrap_message_pipe.Pass()),
+ this);
+
+ scoped_ptr<dbus::FileDescriptor> client_fd(new dbus::FileDescriptor);
+ client_fd->PutValue(channel_pair.PassClientHandle().release().fd);
+ client_fd->CheckValidity(); // Must be run on an IO thread.
+ return client_fd.Pass();
+ }
+
+ // Sends client_fd over to the externally-running service. If that
+ // attempt is successful, the service will then be "activated" by
+ // sending it a ShellHandle.
+ void ConnectChannel(scoped_ptr<dbus::FileDescriptor> client_fd) {
+ size_t first_slash = url_.path().find_first_of('/');
+ DCHECK_NE(first_slash, std::string::npos);
+
+ const std::string service_name = url_.path().substr(0, first_slash);
+ const std::string object_path = url_.path().substr(first_slash);
+ service_dbus_proxy_ =
+ bus_->GetObjectProxy(service_name, dbus::ObjectPath(object_path));
+
+ dbus::MethodCall call("org.chromium.Mojo", "ConnectChannel");
+ dbus::MessageWriter writer(&call);
+ writer.AppendFileDescriptor(*client_fd.get());
+
+ // TODO(cmasone): handle errors!
+ service_dbus_proxy_->CallMethod(
+ &call,
+ dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&LoadContext::ActivateService, base::Unretained(this)));
+ }
+
+ // Sends a ShellHandle over to the now-connected externally-running service,
+ // using the Mojo ExternalService API.
+ void ActivateService(dbus::Response* response) {
+ mojo::AllocationScope scope;
+ external_service_->Activate(
+ mojo::ScopedMessagePipeHandle(
+ mojo::MessagePipeHandle(shell_handle_.release().value())));
+ }
+
+ // Should the ExternalService disappear completely, destroy connection state.
+ // NB: This triggers off of the service disappearing from
+ // DBus. Perhaps there's a way to watch at the Mojo layer instead,
+ // and that would be superior?
+ void HandleNameOwnerChanged(const std::string& old_owner,
+ const std::string& new_owner) {
+ DCHECK(loader_->context_->task_runners()->ui_runner()->
+ BelongsToCurrentThread());
+
+ if (new_owner.empty()) {
+ loader_->context_->task_runners()->ui_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&DBusServiceLoader::ForgetService,
+ base::Unretained(loader_), url_));
+ }
+ }
+
+ DBusServiceLoader* const loader_;
+ scoped_refptr<dbus::Bus> bus_;
+ dbus::ObjectProxy* service_dbus_proxy_; // Owned by bus_;
+ const GURL url_;
+ ScopedShellHandle shell_handle_;
+ KeepAlive keep_alive_;
+ scoped_ptr<common::ChannelInit> channel_init_;
+ mojo::RemotePtr<mojo::ExternalService> external_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoadContext);
+};
+
+DBusServiceLoader::DBusServiceLoader(Context* context) : context_(context) {
+ dbus::Bus::Options options;
+ options.bus_type = dbus::Bus::SESSION;
+ options.dbus_task_runner = context_->task_runners()->io_runner();
+ bus_ = new dbus::Bus(options);
+}
+
+DBusServiceLoader::~DBusServiceLoader() {
+ DCHECK(url_to_load_context_.empty());
+}
+
+void DBusServiceLoader::LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedShellHandle service_handle) {
+ DCHECK(url.SchemeIs("dbus"));
+ DCHECK(url_to_load_context_.find(url) == url_to_load_context_.end());
+ url_to_load_context_[url] =
+ new LoadContext(this, bus_, url, service_handle.Pass());
+}
+
+void DBusServiceLoader::OnServiceError(ServiceManager* manager,
+ const GURL& url) {
+ // TODO(cmasone): Anything at all in this method here.
+}
+
+void DBusServiceLoader::ForgetService(const GURL& url) {
+ DCHECK(context_->task_runners()->ui_runner()->BelongsToCurrentThread());
+ DVLOG(2) << "Forgetting service (url: " << url << ")";
+
+ LoadContextMap::iterator it = url_to_load_context_.find(url);
+ DCHECK(it != url_to_load_context_.end()) << url;
+
+ LoadContext* doomed = it->second;
+ url_to_load_context_.erase(it);
+
+ delete doomed;
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/mojo/shell/dbus_service_loader_linux.h b/mojo/shell/dbus_service_loader_linux.h
new file mode 100644
index 0000000..8cb3f53
--- /dev/null
+++ b/mojo/shell/dbus_service_loader_linux.h
@@ -0,0 +1,90 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SHELL_DBUS_SERVICE_LOADER_H_
+#define MOJO_SHELL_DBUS_SERVICE_LOADER_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/interfaces/shell/shell.mojom.h"
+#include "mojo/service_manager/service_loader.h"
+#include "mojo/shell/keep_alive.h"
+#include "url/gurl.h"
+
+namespace dbus {
+class Bus;
+} // namespace dbus
+
+namespace mojo {
+namespace shell {
+
+class Context;
+
+// An implementation of ServiceLoader that contacts a system service
+// and bootstraps a Mojo connection to it over DBus.
+//
+// In order to allow the externally-running service to accept connections from
+// a Mojo shell, we need to get it a ShellHandle. This class creates a
+// dedicated MessagePipe, passes a handle to one end to the desired service
+// over DBus, and then passes the ShellHandle over that pipe.
+//
+// This class assumes the following:
+// 1) Your service is already running.
+// 2) Your service implements the Mojo ExternalService API
+// (from external_service.mojom).
+// 3) Your service exports an object that implements the org.chromium.Mojo DBus
+// interface:
+// <interface name="org.chromium.Mojo">
+// <method name="ConnectChannel">
+// <arg type="h" name="file_descriptor" direction="in" />
+// </method>
+// </interface>
+class DBusServiceLoader : public ServiceLoader {
+ public:
+ DBusServiceLoader(Context* context);
+ virtual ~DBusServiceLoader();
+
+ // URL for DBus services are of the following format:
+ // dbus:tld.domain.ServiceName/path/to/DBusObject
+ //
+ // This is simply the scheme (dbus:) and then the DBus service name followed
+ // by the DBus object path of an object that implements the org.chromium.Mojo
+ // interface as discussed above.
+ //
+ // Example:
+ // dbus:org.chromium.EchoService/org/chromium/MojoImpl
+ //
+ // This will tell DBusServiceLoader to reach out to a service with
+ // the name "org.chromium.EchoService" and invoke the method
+ // "org.chromium.Mojo.ConnectChannel" on the object exported at
+ // "/org/chromium/MojoImpl".
+ virtual void LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedShellHandle service_handle) OVERRIDE;
+
+ virtual void OnServiceError(ServiceManager* manager, const GURL& url)
+ OVERRIDE;
+
+ private:
+ class LoadContext;
+
+ // Tosses out connection-related state to service at given URL.
+ void ForgetService(const GURL& url);
+
+ Context* const context_;
+ scoped_refptr<dbus::Bus> bus_;
+
+ typedef std::map<GURL, LoadContext*> LoadContextMap;
+ LoadContextMap url_to_load_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(DBusServiceLoader);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_DBUS_SERVICE_LOADER_H_
diff --git a/mojo/shell/external_service.mojom b/mojo/shell/external_service.mojom
new file mode 100644
index 0000000..f76f5fe
--- /dev/null
+++ b/mojo/shell/external_service.mojom
@@ -0,0 +1,16 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo {
+
+[Peer=ExternalServiceHost]
+interface ExternalService {
+ Activate(handle<message_pipe> shell_handle);
+};
+
+[Peer=ExternalService]
+interface ExternalServiceHost {
+};
+
+}