1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <algorithm> 6 7#include "base/command_line.h" 8#include "base/file_util.h" 9#include "base/logging.h" 10#include "base/mac/scoped_nsautorelease_pool.h" 11#include "base/memory/singleton.h" 12#include "base/path_service.h" 13#include "base/process_util.h" 14#include "base/sha1.h" 15#include "base/string16.h" 16#include "base/string_number_conversions.h" 17#include "base/string_util.h" 18#include "base/utf_string_conversions.h" 19#include "base/version.h" 20#include "chrome/common/chrome_constants.h" 21#include "chrome/common/chrome_paths.h" 22#include "chrome/common/chrome_switches.h" 23#include "chrome/common/chrome_version_info.h" 24#include "chrome/common/service_process_util.h" 25#include "content/common/child_process_host.h" 26 27#if !defined(OS_MACOSX) 28 29namespace { 30 31// This should be more than enough to hold a version string assuming each part 32// of the version string is an int64. 33const uint32 kMaxVersionStringLength = 256; 34 35// The structure that gets written to shared memory. 36struct ServiceProcessSharedData { 37 char service_process_version[kMaxVersionStringLength]; 38 base::ProcessId service_process_pid; 39}; 40 41// Gets the name of the shared memory used by the service process to write its 42// version. The name is not versioned. 43std::string GetServiceProcessSharedMemName() { 44 return GetServiceProcessScopedName("_service_shmem"); 45} 46 47enum ServiceProcessRunningState { 48 SERVICE_NOT_RUNNING, 49 SERVICE_OLDER_VERSION_RUNNING, 50 SERVICE_SAME_VERSION_RUNNING, 51 SERVICE_NEWER_VERSION_RUNNING, 52}; 53 54ServiceProcessRunningState GetServiceProcessRunningState( 55 std::string* service_version_out, base::ProcessId* pid_out) { 56 std::string version; 57 if (!GetServiceProcessData(&version, pid_out)) 58 return SERVICE_NOT_RUNNING; 59 60#if defined(OS_POSIX) 61 // We only need to check for service running on POSIX because Windows cleans 62 // up shared memory files when an app crashes, so there isn't a chance of 63 // us reading bogus data from shared memory for an app that has died. 64 if (!CheckServiceProcessReady()) { 65 return SERVICE_NOT_RUNNING; 66 } 67#endif // defined(OS_POSIX) 68 69 // At this time we have a version string. Set the out param if it exists. 70 if (service_version_out) 71 *service_version_out = version; 72 73 scoped_ptr<Version> service_version(Version::GetVersionFromString(version)); 74 // If the version string is invalid, treat it like an older version. 75 if (!service_version.get()) 76 return SERVICE_OLDER_VERSION_RUNNING; 77 78 // Get the version of the currently *running* instance of Chrome. 79 chrome::VersionInfo version_info; 80 if (!version_info.is_valid()) { 81 NOTREACHED() << "Failed to get current file version"; 82 // Our own version is invalid. This is an error case. Pretend that we 83 // are out of date. 84 return SERVICE_NEWER_VERSION_RUNNING; 85 } 86 scoped_ptr<Version> running_version(Version::GetVersionFromString( 87 version_info.Version())); 88 if (!running_version.get()) { 89 NOTREACHED() << "Failed to parse version info"; 90 // Our own version is invalid. This is an error case. Pretend that we 91 // are out of date. 92 return SERVICE_NEWER_VERSION_RUNNING; 93 } 94 95 if (running_version->CompareTo(*service_version) > 0) { 96 return SERVICE_OLDER_VERSION_RUNNING; 97 } else if (service_version->CompareTo(*running_version) > 0) { 98 return SERVICE_NEWER_VERSION_RUNNING; 99 } 100 return SERVICE_SAME_VERSION_RUNNING; 101} 102 103} // namespace 104 105// Return a name that is scoped to this instance of the service process. We 106// use the hash of the user-data-dir as a scoping prefix. We can't use 107// the user-data-dir itself as we have limits on the size of the lock names. 108std::string GetServiceProcessScopedName(const std::string& append_str) { 109 FilePath user_data_dir; 110 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); 111#if defined(OS_WIN) 112 std::string user_data_dir_path = WideToUTF8(user_data_dir.value()); 113#elif defined(OS_POSIX) 114 std::string user_data_dir_path = user_data_dir.value(); 115#endif // defined(OS_WIN) 116 std::string hash = base::SHA1HashString(user_data_dir_path); 117 std::string hex_hash = base::HexEncode(hash.c_str(), hash.length()); 118 return hex_hash + "." + append_str; 119} 120 121// Return a name that is scoped to this instance of the service process. We 122// use the user-data-dir and the version as a scoping prefix. 123std::string GetServiceProcessScopedVersionedName( 124 const std::string& append_str) { 125 std::string versioned_str; 126 chrome::VersionInfo version_info; 127 DCHECK(version_info.is_valid()); 128 versioned_str.append(version_info.Version()); 129 versioned_str.append(append_str); 130 return GetServiceProcessScopedName(versioned_str); 131} 132 133// Reads the named shared memory to get the shared data. Returns false if no 134// matching shared memory was found. 135bool GetServiceProcessData(std::string* version, base::ProcessId* pid) { 136 scoped_ptr<base::SharedMemory> shared_mem_service_data; 137 shared_mem_service_data.reset(new base::SharedMemory()); 138 ServiceProcessSharedData* service_data = NULL; 139 if (shared_mem_service_data.get() && 140 shared_mem_service_data->Open(GetServiceProcessSharedMemName(), true) && 141 shared_mem_service_data->Map(sizeof(ServiceProcessSharedData))) { 142 service_data = reinterpret_cast<ServiceProcessSharedData*>( 143 shared_mem_service_data->memory()); 144 // Make sure the version in shared memory is null-terminated. If it is not, 145 // treat it as invalid. 146 if (version && memchr(service_data->service_process_version, '\0', 147 sizeof(service_data->service_process_version))) 148 *version = service_data->service_process_version; 149 if (pid) 150 *pid = service_data->service_process_pid; 151 return true; 152 } 153 return false; 154} 155 156// Gets the name of the service process IPC channel. 157IPC::ChannelHandle GetServiceProcessChannel() { 158 return GetServiceProcessScopedVersionedName("_service_ipc"); 159} 160 161#endif // !OS_MACOSX 162 163ServiceProcessState::ServiceProcessState() : state_(NULL) { 164 CreateAutoRunCommandLine(); 165 CreateState(); 166} 167 168ServiceProcessState::~ServiceProcessState() { 169#if !defined(OS_MACOSX) 170 if (shared_mem_service_data_.get()) { 171 shared_mem_service_data_->Delete(GetServiceProcessSharedMemName()); 172 } 173#endif // !OS_MACOSX 174 TearDownState(); 175} 176 177void ServiceProcessState::SignalStopped() { 178 TearDownState(); 179 shared_mem_service_data_.reset(); 180} 181 182#if !defined(OS_MACOSX) 183bool ServiceProcessState::Initialize() { 184 if (!TakeSingletonLock()) { 185 return false; 186 } 187 // Now that we have the singleton, take care of killing an older version, if 188 // it exists. 189 if (!HandleOtherVersion()) 190 return false; 191 192 // Write the version we are using to shared memory. This can be used by a 193 // newer service to signal us to exit. 194 return CreateSharedData(); 195} 196 197bool ServiceProcessState::HandleOtherVersion() { 198 std::string running_version; 199 base::ProcessId process_id = 0; 200 ServiceProcessRunningState state = 201 GetServiceProcessRunningState(&running_version, &process_id); 202 switch (state) { 203 case SERVICE_SAME_VERSION_RUNNING: 204 case SERVICE_NEWER_VERSION_RUNNING: 205 return false; 206 case SERVICE_OLDER_VERSION_RUNNING: 207 // If an older version is running, kill it. 208 ForceServiceProcessShutdown(running_version, process_id); 209 break; 210 case SERVICE_NOT_RUNNING: 211 break; 212 } 213 return true; 214} 215 216bool ServiceProcessState::CreateSharedData() { 217 chrome::VersionInfo version_info; 218 if (!version_info.is_valid()) { 219 NOTREACHED() << "Failed to get current file version"; 220 return false; 221 } 222 if (version_info.Version().length() >= kMaxVersionStringLength) { 223 NOTREACHED() << "Version string length is << " << 224 version_info.Version().length() << "which is longer than" << 225 kMaxVersionStringLength; 226 return false; 227 } 228 229 scoped_ptr<base::SharedMemory> shared_mem_service_data( 230 new base::SharedMemory()); 231 if (!shared_mem_service_data.get()) 232 return false; 233 234 uint32 alloc_size = sizeof(ServiceProcessSharedData); 235 if (!shared_mem_service_data->CreateNamed(GetServiceProcessSharedMemName(), 236 true, alloc_size)) 237 return false; 238 239 if (!shared_mem_service_data->Map(alloc_size)) 240 return false; 241 242 memset(shared_mem_service_data->memory(), 0, alloc_size); 243 ServiceProcessSharedData* shared_data = 244 reinterpret_cast<ServiceProcessSharedData*>( 245 shared_mem_service_data->memory()); 246 memcpy(shared_data->service_process_version, version_info.Version().c_str(), 247 version_info.Version().length()); 248 shared_data->service_process_pid = base::GetCurrentProcId(); 249 shared_mem_service_data_.reset(shared_mem_service_data.release()); 250 return true; 251} 252 253IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() { 254 return ::GetServiceProcessChannel(); 255} 256 257#endif // !OS_MACOSX 258 259void ServiceProcessState::CreateAutoRunCommandLine() { 260 FilePath exe_path = ChildProcessHost::GetChildPath(false); 261 if (exe_path.empty()) { 262 NOTREACHED() << "Unable to get service process binary name."; 263 } 264 autorun_command_line_.reset(new CommandLine(exe_path)); 265 autorun_command_line_->AppendSwitchASCII(switches::kProcessType, 266 switches::kServiceProcess); 267 268 // The user data directory is the only other flag we currently want to 269 // possibly store. 270 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); 271 FilePath user_data_dir = 272 browser_command_line.GetSwitchValuePath(switches::kUserDataDir); 273 if (!user_data_dir.empty()) 274 autorun_command_line_->AppendSwitchPath(switches::kUserDataDir, 275 user_data_dir); 276} 277