| // Copyright (c) 2012 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 <stdint.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <memory> | 
 | #include <utility> | 
 |  | 
 | #include "base/base_switches.h" | 
 | #include "base/command_line.h" | 
 | #include "base/logging.h" | 
 | #include "base/macros.h" | 
 | #include "base/memory/singleton.h" | 
 | #include "base/path_service.h" | 
 | #include "base/sha1.h" | 
 | #include "base/strings/string16.h" | 
 | #include "base/strings/string_number_conversions.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "base/version.h" | 
 | #include "build/build_config.h" | 
 | #include "chrome/common/chrome_constants.h" | 
 | #include "chrome/common/chrome_paths.h" | 
 | #include "chrome/common/chrome_switches.h" | 
 | #include "components/cloud_devices/common/cloud_devices_switches.h" | 
 | #include "components/network_session_configurator/common/network_switches.h" | 
 | #include "components/version_info/version_info.h" | 
 | #include "content/public/common/content_paths.h" | 
 | #include "content/public/common/content_switches.h" | 
 | #include "google_apis/gaia/gaia_switches.h" | 
 | #include "ui/base/ui_base_switches.h" | 
 |  | 
 | #if !defined(OS_MACOSX) | 
 |  | 
 | namespace { | 
 |  | 
 | // This should be more than enough to hold a version string assuming each part | 
 | // of the version string is an int64_t. | 
 | const uint32_t kMaxVersionStringLength = 256; | 
 |  | 
 | // The structure that gets written to shared memory. | 
 | struct ServiceProcessSharedData { | 
 |   char service_process_version[kMaxVersionStringLength]; | 
 |   base::ProcessId service_process_pid; | 
 | }; | 
 |  | 
 | // Gets the name of the shared memory used by the service process to write its | 
 | // version. The name is not versioned. | 
 | std::string GetServiceProcessSharedMemName() { | 
 |   return GetServiceProcessScopedName("_service_shmem"); | 
 | } | 
 |  | 
 | enum ServiceProcessRunningState { | 
 |   SERVICE_NOT_RUNNING, | 
 |   SERVICE_OLDER_VERSION_RUNNING, | 
 |   SERVICE_SAME_VERSION_RUNNING, | 
 |   SERVICE_NEWER_VERSION_RUNNING, | 
 | }; | 
 |  | 
 | ServiceProcessRunningState GetServiceProcessRunningState( | 
 |     std::string* service_version_out, base::ProcessId* pid_out) { | 
 |   std::string version; | 
 |   if (!GetServiceProcessData(&version, pid_out)) | 
 |     return SERVICE_NOT_RUNNING; | 
 |  | 
 | #if defined(OS_POSIX) | 
 |   // We only need to check for service running on POSIX because Windows cleans | 
 |   // up shared memory files when an app crashes, so there isn't a chance of | 
 |   // us reading bogus data from shared memory for an app that has died. | 
 |   if (!CheckServiceProcessReady()) { | 
 |     return SERVICE_NOT_RUNNING; | 
 |   } | 
 | #endif  // defined(OS_POSIX) | 
 |  | 
 |   // At this time we have a version string. Set the out param if it exists. | 
 |   if (service_version_out) | 
 |     *service_version_out = version; | 
 |  | 
 |   base::Version service_version(version); | 
 |   // If the version string is invalid, treat it like an older version. | 
 |   if (!service_version.IsValid()) | 
 |     return SERVICE_OLDER_VERSION_RUNNING; | 
 |  | 
 |   // Get the version of the currently *running* instance of Chrome. | 
 |   base::Version running_version(version_info::GetVersionNumber()); | 
 |   if (!running_version.IsValid()) { | 
 |     NOTREACHED() << "Failed to parse version info"; | 
 |     // Our own version is invalid. This is an error case. Pretend that we | 
 |     // are out of date. | 
 |     return SERVICE_NEWER_VERSION_RUNNING; | 
 |   } | 
 |  | 
 |   if (running_version.CompareTo(service_version) > 0) { | 
 |     return SERVICE_OLDER_VERSION_RUNNING; | 
 |   } else if (service_version.CompareTo(running_version) > 0) { | 
 |     return SERVICE_NEWER_VERSION_RUNNING; | 
 |   } | 
 |   return SERVICE_SAME_VERSION_RUNNING; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 |  | 
 | // Return a name that is scoped to this instance of the service process. We | 
 | // use the user-data-dir and the version as a scoping prefix. | 
 | std::string GetServiceProcessScopedVersionedName( | 
 |     const std::string& append_str) { | 
 |   std::string versioned_str; | 
 |   versioned_str.append(version_info::GetVersionNumber()); | 
 |   versioned_str.append(append_str); | 
 |   return GetServiceProcessScopedName(versioned_str); | 
 | } | 
 |  | 
 | // Reads the named shared memory to get the shared data. Returns false if no | 
 | // matching shared memory was found. | 
 | bool GetServiceProcessData(std::string* version, base::ProcessId* pid) { | 
 |   std::unique_ptr<base::SharedMemory> shared_mem_service_data; | 
 |   shared_mem_service_data.reset(new base::SharedMemory()); | 
 |   ServiceProcessSharedData* service_data = NULL; | 
 |   if (shared_mem_service_data.get() && | 
 |       shared_mem_service_data->Open(GetServiceProcessSharedMemName(), true) && | 
 |       shared_mem_service_data->Map(sizeof(ServiceProcessSharedData))) { | 
 |     service_data = reinterpret_cast<ServiceProcessSharedData*>( | 
 |         shared_mem_service_data->memory()); | 
 |     // Make sure the version in shared memory is null-terminated. If it is not, | 
 |     // treat it as invalid. | 
 |     if (version && memchr(service_data->service_process_version, '\0', | 
 |                           sizeof(service_data->service_process_version))) | 
 |       *version = service_data->service_process_version; | 
 |     if (pid) | 
 |       *pid = service_data->service_process_pid; | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | #endif  // !OS_MACOSX | 
 |  | 
 | // Return a name that is scoped to this instance of the service process. We | 
 | // use the hash of the user-data-dir as a scoping prefix. We can't use | 
 | // the user-data-dir itself as we have limits on the size of the lock names. | 
 | std::string GetServiceProcessScopedName(const std::string& append_str) { | 
 |   base::FilePath user_data_dir; | 
 |   PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); | 
 | #if defined(OS_WIN) | 
 |   std::string user_data_dir_path = base::WideToUTF8(user_data_dir.value()); | 
 | #elif defined(OS_POSIX) | 
 |   std::string user_data_dir_path = user_data_dir.value(); | 
 | #endif  // defined(OS_WIN) | 
 |   std::string hash = base::SHA1HashString(user_data_dir_path); | 
 |   std::string hex_hash = base::HexEncode(hash.c_str(), hash.length()); | 
 |   return hex_hash + "." + append_str; | 
 | } | 
 |  | 
 | std::unique_ptr<base::CommandLine> CreateServiceProcessCommandLine() { | 
 |   base::FilePath exe_path; | 
 |   PathService::Get(content::CHILD_PROCESS_EXE, &exe_path); | 
 |   DCHECK(!exe_path.empty()) << "Unable to get service process binary name."; | 
 |   std::unique_ptr<base::CommandLine> command_line( | 
 |       new base::CommandLine(exe_path)); | 
 |   command_line->AppendSwitchASCII(switches::kProcessType, | 
 |                                   switches::kCloudPrintServiceProcess); | 
 |  | 
 | #if defined(OS_WIN) | 
 |   command_line->AppendArg(switches::kPrefetchArgumentOther); | 
 | #endif  // defined(OS_WIN) | 
 |  | 
 |   static const char* const kSwitchesToCopy[] = { | 
 |     switches::kCloudPrintSetupProxy, | 
 |     switches::kCloudPrintURL, | 
 |     switches::kCloudPrintXmppEndpoint, | 
 | #if defined(OS_WIN) | 
 |     switches::kEnableCloudPrintXps, | 
 | #endif | 
 |     switches::kEnableLogging, | 
 |     switches::kIgnoreUrlFetcherCertRequests, | 
 |     switches::kLang, | 
 |     switches::kLoggingLevel, | 
 |     switches::kLsoUrl, | 
 |     switches::kNoServiceAutorun, | 
 |     switches::kUserDataDir, | 
 |     switches::kV, | 
 |     switches::kVModule, | 
 |     switches::kWaitForDebugger, | 
 |   }; | 
 |  | 
 |   command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), | 
 |                                  kSwitchesToCopy, | 
 |                                  arraysize(kSwitchesToCopy)); | 
 |   return command_line; | 
 | } | 
 |  | 
 | ServiceProcessState::ServiceProcessState() : state_(NULL) { | 
 |   autorun_command_line_ = CreateServiceProcessCommandLine(); | 
 |   CreateState(); | 
 | } | 
 |  | 
 | ServiceProcessState::~ServiceProcessState() { | 
 | #if !defined(OS_MACOSX) | 
 |   if (shared_mem_service_data_.get()) { | 
 |     shared_mem_service_data_->Delete(GetServiceProcessSharedMemName()); | 
 |   } | 
 | #endif  // !OS_MACOSX | 
 |   TearDownState(); | 
 | } | 
 |  | 
 | void ServiceProcessState::SignalStopped() { | 
 |   TearDownState(); | 
 |   shared_mem_service_data_.reset(); | 
 | } | 
 |  | 
 | #if !defined(OS_MACOSX) | 
 | bool ServiceProcessState::Initialize() { | 
 |   if (!TakeSingletonLock()) { | 
 |     return false; | 
 |   } | 
 |   // Now that we have the singleton, take care of killing an older version, if | 
 |   // it exists. | 
 |   if (!HandleOtherVersion()) | 
 |     return false; | 
 |  | 
 |   // Write the version we are using to shared memory. This can be used by a | 
 |   // newer service to signal us to exit. | 
 |   return CreateSharedData(); | 
 | } | 
 |  | 
 | bool ServiceProcessState::HandleOtherVersion() { | 
 |   std::string running_version; | 
 |   base::ProcessId process_id = 0; | 
 |   ServiceProcessRunningState state = | 
 |       GetServiceProcessRunningState(&running_version, &process_id); | 
 |   switch (state) { | 
 |     case SERVICE_SAME_VERSION_RUNNING: | 
 |     case SERVICE_NEWER_VERSION_RUNNING: | 
 |       return false; | 
 |     case SERVICE_OLDER_VERSION_RUNNING: | 
 |       // If an older version is running, kill it. | 
 |       ForceServiceProcessShutdown(running_version, process_id); | 
 |       break; | 
 |     case SERVICE_NOT_RUNNING: | 
 |       break; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool ServiceProcessState::CreateSharedData() { | 
 |   if (version_info::GetVersionNumber().length() >= kMaxVersionStringLength) { | 
 |     NOTREACHED() << "Version string length is << " | 
 |                  << version_info::GetVersionNumber().length() | 
 |                  << " which is longer than" << kMaxVersionStringLength; | 
 |     return false; | 
 |   } | 
 |  | 
 |   std::unique_ptr<base::SharedMemory> shared_mem_service_data( | 
 |       new base::SharedMemory()); | 
 |   if (!shared_mem_service_data.get()) | 
 |     return false; | 
 |  | 
 |   uint32_t alloc_size = sizeof(ServiceProcessSharedData); | 
 |   // TODO(viettrungluu): Named shared memory is deprecated (crbug.com/345734). | 
 |   if (!shared_mem_service_data->CreateNamedDeprecated | 
 |           (GetServiceProcessSharedMemName(), true, alloc_size)) | 
 |     return false; | 
 |  | 
 |   if (!shared_mem_service_data->Map(alloc_size)) | 
 |     return false; | 
 |  | 
 |   memset(shared_mem_service_data->memory(), 0, alloc_size); | 
 |   ServiceProcessSharedData* shared_data = | 
 |       reinterpret_cast<ServiceProcessSharedData*>( | 
 |           shared_mem_service_data->memory()); | 
 |   memcpy(shared_data->service_process_version, | 
 |          version_info::GetVersionNumber().c_str(), | 
 |          version_info::GetVersionNumber().length()); | 
 |   shared_data->service_process_pid = base::GetCurrentProcId(); | 
 |   shared_mem_service_data_ = std::move(shared_mem_service_data); | 
 |   return true; | 
 | } | 
 |  | 
 | mojo::edk::NamedPlatformHandle ServiceProcessState::GetServiceProcessChannel() { | 
 |   return ::GetServiceProcessChannel(); | 
 | } | 
 |  | 
 | #endif  // !OS_MACOSX |