diff options
Diffstat (limited to 'chromium/chrome/common/service_process_util_win.cc')
-rw-r--r-- | chromium/chrome/common/service_process_util_win.cc | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/chromium/chrome/common/service_process_util_win.cc b/chromium/chrome/common/service_process_util_win.cc new file mode 100644 index 00000000000..47302608826 --- /dev/null +++ b/chromium/chrome/common/service_process_util_win.cc @@ -0,0 +1,268 @@ +// Copyright (c) 2011 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 "chrome/common/service_process_util.h" + +#include <windows.h> + +#include <algorithm> +#include <memory> + +#include "base/callback.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/memory/platform_shared_memory_region.h" +#include "base/memory/writable_shared_memory_region.h" +#include "base/path_service.h" +#include "base/strings/string16.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/unguessable_token.h" +#include "base/win/object_watcher.h" +#include "base/win/scoped_handle.h" +#include "base/win/win_util.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" + +namespace { + +const char kTerminateEventSuffix[] = "_service_terminate_evt"; + +base::string16 GetServiceProcessReadyEventName() { + return base::UTF8ToWide( + GetServiceProcessScopedVersionedName("_service_ready")); +} + +base::string16 GetServiceProcessTerminateEventName() { + return base::UTF8ToWide( + GetServiceProcessScopedVersionedName(kTerminateEventSuffix)); +} + +std::string GetServiceProcessAutoRunKey() { + return GetServiceProcessScopedName("_service_run"); +} + +// Returns the name of the autotun reg value that we used to use for older +// versions of Chrome. +std::string GetObsoleteServiceProcessAutoRunKey() { + base::FilePath user_data_dir; + base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + std::string scoped_name = base::WideToUTF8(user_data_dir.value()); + std::replace(scoped_name.begin(), scoped_name.end(), '\\', '!'); + std::replace(scoped_name.begin(), scoped_name.end(), '/', '!'); + scoped_name.append("_service_run"); + return scoped_name; +} + +class ServiceProcessTerminateMonitor + : public base::win::ObjectWatcher::Delegate { + public: + explicit ServiceProcessTerminateMonitor(const base::Closure& terminate_task) + : terminate_task_(terminate_task) { + } + void Start() { + base::string16 event_name = GetServiceProcessTerminateEventName(); + DCHECK(event_name.length() <= MAX_PATH); + terminate_event_.Set(CreateEvent(NULL, TRUE, FALSE, event_name.c_str())); + watcher_.StartWatchingOnce(terminate_event_.Get(), this); + } + + // base::ObjectWatcher::Delegate implementation. + void OnObjectSignaled(HANDLE object) override { + if (!terminate_task_.is_null()) { + terminate_task_.Run(); + terminate_task_.Reset(); + } + } + + private: + base::win::ScopedHandle terminate_event_; + base::win::ObjectWatcher watcher_; + base::Closure terminate_task_; +}; + +} // namespace + +// Gets the name of the service process IPC channel. +mojo::NamedPlatformChannel::ServerName GetServiceProcessServerName() { + return mojo::NamedPlatformChannel::ServerNameFromUTF8( + GetServiceProcessScopedVersionedName("_service_ipc")); +} + +bool ForceServiceProcessShutdown(const std::string& version, + base::ProcessId process_id) { + base::win::ScopedHandle terminate_event; + std::string versioned_name = version; + versioned_name.append(kTerminateEventSuffix); + base::string16 event_name = + base::UTF8ToWide(GetServiceProcessScopedName(versioned_name)); + terminate_event.Set(OpenEvent(EVENT_MODIFY_STATE, FALSE, event_name.c_str())); + if (!terminate_event.IsValid()) + return false; + SetEvent(terminate_event.Get()); + return true; +} + +// static +base::WritableSharedMemoryRegion +ServiceProcessState::CreateServiceProcessDataRegion(size_t size) { + // Check maximum accounting for overflow. + if (size > static_cast<size_t>(std::numeric_limits<int>::max())) + return {}; + + base::string16 name = base::ASCIIToUTF16(GetServiceProcessSharedMemName()); + + SECURITY_ATTRIBUTES sa = {sizeof(sa), nullptr, FALSE}; + HANDLE raw_handle = + CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, + static_cast<DWORD>(size), base::as_wcstr(name)); + if (!raw_handle) { + auto error = GetLastError(); + DLOG(ERROR) << "Cannot create named mapping " << name << ": " << error; + return {}; + } + base::win::ScopedHandle handle(raw_handle); + + base::WritableSharedMemoryRegion writable_region = + base::WritableSharedMemoryRegion::Deserialize( + base::subtle::PlatformSharedMemoryRegion::Take( + std::move(handle), + base::subtle::PlatformSharedMemoryRegion::Mode::kWritable, size, + base::UnguessableToken::Create())); + if (!writable_region.IsValid()) { + DLOG(ERROR) << "Cannot deserialize file mapping"; + return {}; + } + return writable_region; +} + +// static +base::ReadOnlySharedMemoryMapping +ServiceProcessState::OpenServiceProcessDataMapping(size_t size) { + DWORD access = FILE_MAP_READ | SECTION_QUERY; + base::string16 name = base::ASCIIToUTF16(GetServiceProcessSharedMemName()); + HANDLE raw_handle = OpenFileMapping(access, false, base::as_wcstr(name)); + if (!raw_handle) { + auto err = GetLastError(); + DLOG(ERROR) << "OpenFileMapping failed for " << name << " / " + << GetServiceProcessSharedMemName() << " / " << err; + return {}; + } + + // The region is writable for this user, so the handle is converted to a + // WritableSharedMemoryMapping which is then downgraded to read-only for the + // mapping. + base::WritableSharedMemoryRegion writable_region = + base::WritableSharedMemoryRegion::Deserialize( + base::subtle::PlatformSharedMemoryRegion::Take( + base::win::ScopedHandle(raw_handle), + base::subtle::PlatformSharedMemoryRegion::Mode::kWritable, size, + base::UnguessableToken::Create())); + if (!writable_region.IsValid()) { + DLOG(ERROR) << "Unable to deserialize raw file mapping handle to " + << "WritableSharedMemoryRegion"; + return {}; + } + base::ReadOnlySharedMemoryRegion readonly_region = + base::WritableSharedMemoryRegion::ConvertToReadOnly( + std::move(writable_region)); + if (!readonly_region.IsValid()) { + DLOG(ERROR) << "Unable to convert to read-only region"; + return {}; + } + base::ReadOnlySharedMemoryMapping mapping = readonly_region.Map(); + if (!mapping.IsValid()) { + DLOG(ERROR) << "Unable to map region"; + return {}; + } + // The region will be closed on return, leaving on the mapping. + return mapping; +} + +// static +bool ServiceProcessState::DeleteServiceProcessDataRegion() { + // intentionally empty -- there is nothing for us to do on Windows. + return true; +} + +bool CheckServiceProcessReady() { + base::string16 event_name = GetServiceProcessReadyEventName(); + base::win::ScopedHandle event( + OpenEvent(SYNCHRONIZE | READ_CONTROL, false, event_name.c_str())); + if (!event.IsValid()) + return false; + // Check if the event is signaled. + return WaitForSingleObject(event.Get(), 0) == WAIT_OBJECT_0; +} + +struct ServiceProcessState::StateData { + // An event that is signaled when a service process is ready. + base::win::ScopedHandle ready_event; + std::unique_ptr<ServiceProcessTerminateMonitor> terminate_monitor; +}; + +void ServiceProcessState::CreateState() { + DCHECK(!state_); + state_ = new StateData; +} + +bool ServiceProcessState::TakeSingletonLock() { + DCHECK(state_); + base::string16 event_name = GetServiceProcessReadyEventName(); + DCHECK(event_name.length() <= MAX_PATH); + base::win::ScopedHandle service_process_ready_event; + service_process_ready_event.Set( + CreateEvent(NULL, TRUE, FALSE, event_name.c_str())); + DWORD error = GetLastError(); + if ((error == ERROR_ALREADY_EXISTS) || (error == ERROR_ACCESS_DENIED)) + return false; + DCHECK(service_process_ready_event.IsValid()); + state_->ready_event.Set(service_process_ready_event.Take()); + return true; +} + +bool ServiceProcessState::SignalReady( + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + const base::Closure& terminate_task) { + DCHECK(state_); + DCHECK(state_->ready_event.IsValid()); + if (!SetEvent(state_->ready_event.Get())) { + return false; + } + if (!terminate_task.is_null()) { + state_->terminate_monitor.reset( + new ServiceProcessTerminateMonitor(terminate_task)); + state_->terminate_monitor->Start(); + } + return true; +} + +bool ServiceProcessState::AddToAutoRun() { + DCHECK(autorun_command_line_.get()); + // Remove the old autorun value first because we changed the naming scheme + // for the autorun value name. + base::win::RemoveCommandFromAutoRun( + HKEY_CURRENT_USER, + base::UTF8ToWide(GetObsoleteServiceProcessAutoRunKey())); + return base::win::AddCommandToAutoRun( + HKEY_CURRENT_USER, + base::UTF8ToWide(GetServiceProcessAutoRunKey()), + autorun_command_line_->GetCommandLineString()); +} + +bool ServiceProcessState::RemoveFromAutoRun() { + // Remove the old autorun value first because we changed the naming scheme + // for the autorun value name. + base::win::RemoveCommandFromAutoRun( + HKEY_CURRENT_USER, + base::UTF8ToWide(GetObsoleteServiceProcessAutoRunKey())); + return base::win::RemoveCommandFromAutoRun( + HKEY_CURRENT_USER, base::UTF8ToWide(GetServiceProcessAutoRunKey())); +} + +void ServiceProcessState::TearDownState() { + delete state_; + state_ = NULL; +} |