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