diff options
author | cmasone@chromium.org <cmasone@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-24 16:17:57 +0000 |
---|---|---|
committer | cmasone@chromium.org <cmasone@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-24 16:17:57 +0000 |
commit | 196c60bbf6fb74be48726da4f96097490826f67d (patch) | |
tree | 5fa0d20ff75307add5f3c2752c1b3b1bb0d673f2 /mojo | |
parent | 4765890cc2e2b69bbb356f238bf26f8a6d2bf574 (diff) | |
download | chromium_src-196c60bbf6fb74be48726da4f96097490826f67d.zip chromium_src-196c60bbf6fb74be48726da4f96097490826f67d.tar.gz chromium_src-196c60bbf6fb74be48726da4f96097490826f67d.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.
Review URL: https://codereview.chromium.org/242763003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@265927 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'mojo')
-rw-r--r-- | mojo/examples/dbus_echo/dbus_echo_app.cc | 74 | ||||
-rw-r--r-- | mojo/mojo.gyp | 28 | ||||
-rw-r--r-- | mojo/mojo_examples.gypi | 20 | ||||
-rw-r--r-- | mojo/mojo_services.gypi | 39 | ||||
-rw-r--r-- | mojo/services/dbus_echo/DEPS | 4 | ||||
-rw-r--r-- | mojo/services/dbus_echo/dbus_echo_service.cc | 167 | ||||
-rw-r--r-- | mojo/services/dbus_echo/echo.mojom | 16 | ||||
-rw-r--r-- | mojo/shell/DEPS | 1 | ||||
-rw-r--r-- | mojo/shell/context.cc | 11 | ||||
-rw-r--r-- | mojo/shell/dbus_service_loader_linux.cc | 186 | ||||
-rw-r--r-- | mojo/shell/dbus_service_loader_linux.h | 90 | ||||
-rw-r--r-- | mojo/shell/external_service.mojom | 16 |
12 files changed, 652 insertions, 0 deletions
diff --git a/mojo/examples/dbus_echo/dbus_echo_app.cc b/mojo/examples/dbus_echo/dbus_echo_app.cc new file mode 100644 index 0000000..fefd72c --- /dev/null +++ b/mojo/examples/dbus_echo/dbus_echo_app.cc @@ -0,0 +1,74 @@ +// 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 <stdio.h> +#include <string> + +#include "base/bind.h" +#include "base/logging.h" +#include "mojo/public/cpp/bindings/allocation_scope.h" +#include "mojo/public/cpp/bindings/remote_ptr.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/shell/application.h" +#include "mojo/public/cpp/system/core.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/utility/run_loop.h" +#include "mojo/public/interfaces/shell/shell.mojom.h" +#include "mojo/services/dbus_echo/echo.mojom.h" + +#if defined(WIN32) +#if !defined(CDECL) +#define CDECL __cdecl +#endif +#define DBUS_ECHO_APP_EXPORT __declspec(dllexport) +#else +#define CDECL +#define DBUS_ECHO_APP_EXPORT __attribute__((visibility("default"))) +#endif + +namespace mojo { +namespace examples { + +class DBusEchoApp : public Application, public mojo::EchoClient { + public: + explicit DBusEchoApp(MojoHandle shell_handle) : Application(shell_handle) { + InterfacePipe<EchoService, AnyInterface> echo_pipe; + mojo::AllocationScope scope; + shell()->Connect("dbus:org.chromium.EchoService/org/chromium/MojoImpl", + echo_pipe.handle_to_peer.Pass()); + echo_service_.reset(echo_pipe.handle_to_self.Pass(), this); + echo_service_->Echo("who", base::Bind(&DBusEchoApp::OnEcho, + base::Unretained(this))); + } + + virtual ~DBusEchoApp() { + } + + virtual void OnCreated() MOJO_OVERRIDE { + } + + virtual void OnDestroyed() MOJO_OVERRIDE { + RunLoop::current()->Quit(); + } + + private: + void OnEcho(const mojo::String& echoed) { + LOG(INFO) << "echo'd " << echoed.To<std::string>(); + } + + RemotePtr<EchoService> echo_service_; +}; + +} // namespace examples +} // namespace mojo + +extern "C" DBUS_ECHO_APP_EXPORT MojoResult CDECL MojoMain( + MojoHandle shell_handle) { + mojo::Environment env; + mojo::RunLoop loop; + + mojo::examples::DBusEchoApp app(shell_handle); + loop.Run(); + return MOJO_RESULT_OK; +} diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp index 4db1476..1fddb33 100644 --- a/mojo/mojo.gyp +++ b/mojo/mojo.gyp @@ -75,6 +75,23 @@ ] }, { + 'target_name': 'mojo_external_service_bindings', + 'type': 'static_library', + 'sources': [ + 'shell/external_service.mojom', + ], + 'variables': { + 'mojom_base_output_dir': 'mojo', + }, + 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], + 'export_dependent_settings': [ + 'mojo_bindings', + ], + 'dependencies': [ + 'mojo_bindings', + ], + }, + { 'target_name': 'mojo_run_all_unittests', 'type': 'static_library', 'dependencies': [ @@ -422,6 +439,7 @@ '../base/base.gyp:base_static', '../net/net.gyp:net', '../url/url.gyp:url_lib', + 'mojo_external_service_bindings', 'mojo_gles2_impl', 'mojo_service_manager', 'mojo_shell_bindings', @@ -445,6 +463,8 @@ 'shell/child_process_host.h', 'shell/context.cc', 'shell/context.h', + 'shell/dbus_service_loader_linux.cc', + 'shell/dbus_service_loader_linux.h', 'shell/dynamic_service_loader.cc', 'shell/dynamic_service_loader.h', 'shell/dynamic_service_runner.h', @@ -473,6 +493,14 @@ 'shell/url_request_context_getter.cc', 'shell/url_request_context_getter.h', ], + 'conditions': [ + ['OS=="linux"', { + 'dependencies': [ + '../build/linux/system.gyp:dbus', + '../dbus/dbus.gyp:dbus', + ], + }], + ], }, { 'target_name': 'mojo_shell', diff --git a/mojo/mojo_examples.gypi b/mojo/mojo_examples.gypi index 4de6fbd..5bece9b 100644 --- a/mojo/mojo_examples.gypi +++ b/mojo/mojo_examples.gypi @@ -297,5 +297,25 @@ }, ], }], + ['OS=="linux"', { + 'targets': [ + { + 'target_name': 'mojo_dbus_echo', + 'type': 'shared_library', + 'dependencies': [ + '../base/base.gyp:base', + 'mojo_bindings', + 'mojo_environment_standalone', + 'mojo_echo_bindings', + 'mojo_shell_client', + 'mojo_system', + 'mojo_utility', + ], + 'sources': [ + 'examples/dbus_echo/dbus_echo_app.cc', + ], + }, + ], + }], ], } diff --git a/mojo/mojo_services.gypi b/mojo/mojo_services.gypi index 3911a21..ee45085 100644 --- a/mojo/mojo_services.gypi +++ b/mojo/mojo_services.gypi @@ -1,6 +1,23 @@ { 'targets': [ { + 'target_name': 'mojo_echo_bindings', + 'type': 'static_library', + 'sources': [ + 'services/dbus_echo/echo.mojom', + ], + 'variables': { + 'mojom_base_output_dir': 'mojo', + }, + 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], + 'export_dependent_settings': [ + 'mojo_bindings', + ], + 'dependencies': [ + 'mojo_bindings', + ], + }, + { 'target_name': 'mojo_gles2_bindings', 'type': 'static_library', 'sources': [ @@ -220,5 +237,27 @@ }, ], }], + ['OS=="linux"', { + 'targets': [ + { + 'target_name': 'mojo_dbus_echo_service', + 'type': 'executable', + 'dependencies': [ + '../base/base.gyp:base', + '../build/linux/system.gyp:dbus', + '../dbus/dbus.gyp:dbus', + 'mojo_external_service_bindings', + 'mojo_common_lib', + 'mojo_environment_chromium', + 'mojo_echo_bindings', + 'mojo_shell_client', + 'mojo_system_impl', + ], + 'sources': [ + 'services/dbus_echo/dbus_echo_service.cc', + ], + }, + ], + }], ], } diff --git a/mojo/services/dbus_echo/DEPS b/mojo/services/dbus_echo/DEPS new file mode 100644 index 0000000..7260d76 --- /dev/null +++ b/mojo/services/dbus_echo/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+dbus", + "+mojo/embedder", +] diff --git a/mojo/services/dbus_echo/dbus_echo_service.cc b/mojo/services/dbus_echo/dbus_echo_service.cc new file mode 100644 index 0000000..189563d --- /dev/null +++ b/mojo/services/dbus_echo/dbus_echo_service.cc @@ -0,0 +1,167 @@ +// 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 "base/at_exit.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/run_loop.h" +#include "dbus/bus.h" +#include "dbus/exported_object.h" +#include "dbus/file_descriptor.h" +#include "dbus/message.h" +#include "dbus/object_path.h" +#include "mojo/common/mojo_channel_init.h" +#include "mojo/embedder/embedder.h" +#include "mojo/public/cpp/bindings/interface.h" +#include "mojo/public/cpp/bindings/remote_ptr.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/shell/application.h" +#include "mojo/public/interfaces/shell/shell.mojom.h" +#include "mojo/services/dbus_echo/echo.mojom.h" +#include "mojo/shell/external_service.mojom.h" + +class EchoServiceImpl : public mojo::ServiceConnection<mojo::EchoService, + EchoServiceImpl> { + public: + EchoServiceImpl() {} + virtual ~EchoServiceImpl() {} + + protected: + virtual void Echo( + const mojo::String& in_to_echo, + const mojo::Callback<void(mojo::String)>& callback) OVERRIDE { + DVLOG(1) << "Asked to echo " << in_to_echo.To<std::string>(); + callback.Run(in_to_echo); + } +}; + + +class DBusEchoService : public mojo::ExternalService { + public: + static const char kMojoImplPath[]; + static const char kMojoInterface[]; + static const char kServiceName[]; + + DBusEchoService() : exported_object_(NULL) { + } + ~DBusEchoService() {} + + void Start() { + InitializeDBus(); + ExportMethods(); + TakeDBusServiceOwnership(); + DVLOG(1) << "Echo service started"; + } + + private: + // TODO(cmasone): Use a Delegate pattern to allow the ConnectChannel + // code to be shared among all Mojo services that wish to be + // connected to over DBus. + virtual void Activate(mojo::ScopedMessagePipeHandle client_handle) OVERRIDE { + mojo::ScopedShellHandle shell_handle( + mojo::ShellHandle(client_handle.release().value())); + app_.reset(new mojo::Application(shell_handle.Pass())); + app_->AddServiceConnector(new mojo::ServiceConnector<EchoServiceImpl>()); + } + + // Implementation of org.chromium.Mojo.ConnectChannel, exported over DBus. + // Takes a file descriptor and uses it to create a MessagePipe that is then + // hooked to a RemotePtr<mojo::ExternalServiceHost>. + void ConnectChannel(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender sender) { + dbus::MessageReader reader(method_call); + dbus::FileDescriptor wrapped_fd; + if (!reader.PopFileDescriptor(&wrapped_fd)) { + sender.Run( + dbus::ErrorResponse::FromMethodCall( + method_call, + "org.chromium.Mojo.BadHandle", + "Invalid FD.").PassAs<dbus::Response>()); + return; + } + wrapped_fd.CheckValidity(); + channel_init_.reset(new mojo::common::MojoChannelInit); + channel_init_->Init(wrapped_fd.TakeValue(), + base::MessageLoopProxy::current()); + CHECK(channel_init_->is_handle_valid()); + + // TODO(cmasone): Figure out how to correctly clean up the channel when + // the peer disappears. Passing an ErrorHandler when constructing the + // RemotePtr<> below didn't seem to work. OnError wasn't called. + mojo::ScopedExternalServiceHostHandle client_handle( + mojo::ExternalServiceHostHandle( + channel_init_->bootstrap_message_pipe().release().value())); + external_service_host_.reset(client_handle.Pass(), this); + + sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + void ExportMethods() { + CHECK(exported_object_); + CHECK(exported_object_->ExportMethodAndBlock( + kMojoInterface, "ConnectChannel", + base::Bind(&DBusEchoService::ConnectChannel, + base::Unretained(this)))); + } + + void InitializeDBus() { + CHECK(!bus_); + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SESSION; + bus_ = new dbus::Bus(options); + CHECK(bus_->Connect()); + CHECK(bus_->SetUpAsyncOperations()); + + exported_object_ = bus_->GetExportedObject(dbus::ObjectPath(kMojoImplPath)); + } + + void TakeDBusServiceOwnership() { + CHECK(bus_->RequestOwnershipAndBlock( + kServiceName, + dbus::Bus::REQUIRE_PRIMARY_ALLOW_REPLACEMENT)) + << "Unable to take ownership of " << kServiceName; + } + + scoped_refptr<dbus::Bus> bus_; + dbus::ExportedObject* exported_object_; // Owned by bus_; + scoped_ptr<mojo::common::MojoChannelInit> channel_init_; + mojo::RemotePtr<mojo::ExternalServiceHost> external_service_host_; + scoped_ptr<mojo::Application> app_; +}; + +// TODO(cmasone): Figure out where these "well-known" names should be defined +// so they can be shared with DBusServiceLoader and such. +const char DBusEchoService::kMojoImplPath[] = "/org/chromium/MojoImpl"; +const char DBusEchoService::kMojoInterface[] = "org.chromium.Mojo"; +const char DBusEchoService::kServiceName[] = "org.chromium.EchoService"; + +int main(int argc, char** argv) { + base::AtExitManager exit_manager; + CommandLine::Init(argc, argv); + + logging::LoggingSettings settings; + settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; + logging::InitLogging(settings); + logging::SetLogItems(false, // Process ID + false, // Thread ID + false, // Timestamp + false); // Tick count + + mojo::Environment env; + mojo::embedder::Init(); + + base::MessageLoopForIO message_loop; + base::RunLoop run_loop; + + DBusEchoService echo_service; + echo_service.Start(); + + run_loop.Run(); + return 0; +} diff --git a/mojo/services/dbus_echo/echo.mojom b/mojo/services/dbus_echo/echo.mojom new file mode 100644 index 0000000..c2ad42a --- /dev/null +++ b/mojo/services/dbus_echo/echo.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=EchoClient] +interface EchoService { + Echo(string to_echo) => (string echoed); +}; + +[Peer=EchoService] +interface EchoClient { +}; + +} 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..496ccd3 --- /dev/null +++ b/mojo/shell/dbus_service_loader_linux.cc @@ -0,0 +1,186 @@ +// 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/mojo_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::MojoChannelInit); + channel_init_->Init( + channel_pair.PassServerHandle().release().fd, + loader_->context_->task_runners()->io_runner()); + CHECK(channel_init_->is_handle_valid()); + + 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::ScopedExternalServiceHandle external_service_handle( + mojo::ExternalServiceHandle( + channel_init_->bootstrap_message_pipe().release().value())); + external_service_.reset(external_service_handle.Pass(), this); + + 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::MojoChannelInit> 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 { +}; + +} |