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 "chrome/common/service_process_util.h" 6 7 #include <algorithm> 8 9 #include "base/command_line.h" 10 #include "base/logging.h" 11 #include "base/memory/singleton.h" 12 #include "base/path_service.h" 13 #include "base/sha1.h" 14 #include "base/strings/string16.h" 15 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/string_util.h" 17 #include "base/strings/utf_string_conversions.h" 18 #include "base/version.h" 19 #include "chrome/common/chrome_constants.h" 20 #include "chrome/common/chrome_paths.h" 21 #include "chrome/common/chrome_switches.h" 22 #include "chrome/common/chrome_version_info.h" 23 #include "components/cloud_devices/common/cloud_devices_switches.h" 24 #include "content/public/common/content_paths.h" 25 #include "google_apis/gaia/gaia_switches.h" 26 #include "ui/base/ui_base_switches.h" 27 28 #if !defined(OS_MACOSX) 29 30 namespace { 31 32 // This should be more than enough to hold a version string assuming each part 33 // of the version string is an int64. 34 const uint32 kMaxVersionStringLength = 256; 35 36 // The structure that gets written to shared memory. 37 struct ServiceProcessSharedData { 38 char service_process_version[kMaxVersionStringLength]; 39 base::ProcessId service_process_pid; 40 }; 41 42 // Gets the name of the shared memory used by the service process to write its 43 // version. The name is not versioned. 44 std::string GetServiceProcessSharedMemName() { 45 return GetServiceProcessScopedName("_service_shmem"); 46 } 47 48 enum ServiceProcessRunningState { 49 SERVICE_NOT_RUNNING, 50 SERVICE_OLDER_VERSION_RUNNING, 51 SERVICE_SAME_VERSION_RUNNING, 52 SERVICE_NEWER_VERSION_RUNNING, 53 }; 54 55 ServiceProcessRunningState GetServiceProcessRunningState( 56 std::string* service_version_out, base::ProcessId* pid_out) { 57 std::string version; 58 if (!GetServiceProcessData(&version, pid_out)) 59 return SERVICE_NOT_RUNNING; 60 61 #if defined(OS_POSIX) 62 // We only need to check for service running on POSIX because Windows cleans 63 // up shared memory files when an app crashes, so there isn't a chance of 64 // us reading bogus data from shared memory for an app that has died. 65 if (!CheckServiceProcessReady()) { 66 return SERVICE_NOT_RUNNING; 67 } 68 #endif // defined(OS_POSIX) 69 70 // At this time we have a version string. Set the out param if it exists. 71 if (service_version_out) 72 *service_version_out = version; 73 74 Version service_version(version); 75 // If the version string is invalid, treat it like an older version. 76 if (!service_version.IsValid()) 77 return SERVICE_OLDER_VERSION_RUNNING; 78 79 // Get the version of the currently *running* instance of Chrome. 80 chrome::VersionInfo version_info; 81 if (!version_info.is_valid()) { 82 NOTREACHED() << "Failed to get current file version"; 83 // Our own version is invalid. This is an error case. Pretend that we 84 // are out of date. 85 return SERVICE_NEWER_VERSION_RUNNING; 86 } 87 Version running_version(version_info.Version()); 88 if (!running_version.IsValid()) { 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. 108 std::string GetServiceProcessScopedName(const std::string& append_str) { 109 base::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 = base::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. 123 std::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. 135 bool 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 #endif // !OS_MACOSX 157 158 scoped_ptr<base::CommandLine> CreateServiceProcessCommandLine() { 159 base::FilePath exe_path; 160 PathService::Get(content::CHILD_PROCESS_EXE, &exe_path); 161 DCHECK(!exe_path.empty()) << "Unable to get service process binary name."; 162 scoped_ptr<base::CommandLine> command_line(new base::CommandLine(exe_path)); 163 command_line->AppendSwitchASCII(switches::kProcessType, 164 switches::kServiceProcess); 165 static const char* const kSwitchesToCopy[] = { 166 switches::kCloudPrintSetupProxy, 167 switches::kCloudPrintURL, 168 switches::kCloudPrintXmppEndpoint, 169 #if defined(OS_WIN) 170 switches::kEnableCloudPrintXps, 171 #endif 172 switches::kEnableLogging, 173 switches::kIgnoreUrlFetcherCertRequests, 174 switches::kLang, 175 switches::kLoggingLevel, 176 switches::kLsoUrl, 177 switches::kNoServiceAutorun, 178 switches::kUserDataDir, 179 switches::kV, 180 switches::kVModule, 181 switches::kWaitForDebugger, 182 }; 183 184 command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), 185 kSwitchesToCopy, 186 arraysize(kSwitchesToCopy)); 187 return command_line.Pass(); 188 } 189 190 ServiceProcessState::ServiceProcessState() : state_(NULL) { 191 autorun_command_line_ = CreateServiceProcessCommandLine(); 192 CreateState(); 193 } 194 195 ServiceProcessState::~ServiceProcessState() { 196 #if !defined(OS_MACOSX) 197 if (shared_mem_service_data_.get()) { 198 shared_mem_service_data_->Delete(GetServiceProcessSharedMemName()); 199 } 200 #endif // !OS_MACOSX 201 TearDownState(); 202 } 203 204 void ServiceProcessState::SignalStopped() { 205 TearDownState(); 206 shared_mem_service_data_.reset(); 207 } 208 209 #if !defined(OS_MACOSX) 210 bool ServiceProcessState::Initialize() { 211 if (!TakeSingletonLock()) { 212 return false; 213 } 214 // Now that we have the singleton, take care of killing an older version, if 215 // it exists. 216 if (!HandleOtherVersion()) 217 return false; 218 219 // Write the version we are using to shared memory. This can be used by a 220 // newer service to signal us to exit. 221 return CreateSharedData(); 222 } 223 224 bool ServiceProcessState::HandleOtherVersion() { 225 std::string running_version; 226 base::ProcessId process_id = 0; 227 ServiceProcessRunningState state = 228 GetServiceProcessRunningState(&running_version, &process_id); 229 switch (state) { 230 case SERVICE_SAME_VERSION_RUNNING: 231 case SERVICE_NEWER_VERSION_RUNNING: 232 return false; 233 case SERVICE_OLDER_VERSION_RUNNING: 234 // If an older version is running, kill it. 235 ForceServiceProcessShutdown(running_version, process_id); 236 break; 237 case SERVICE_NOT_RUNNING: 238 break; 239 } 240 return true; 241 } 242 243 bool ServiceProcessState::CreateSharedData() { 244 chrome::VersionInfo version_info; 245 if (!version_info.is_valid()) { 246 NOTREACHED() << "Failed to get current file version"; 247 return false; 248 } 249 if (version_info.Version().length() >= kMaxVersionStringLength) { 250 NOTREACHED() << "Version string length is << " << 251 version_info.Version().length() << "which is longer than" << 252 kMaxVersionStringLength; 253 return false; 254 } 255 256 scoped_ptr<base::SharedMemory> shared_mem_service_data( 257 new base::SharedMemory()); 258 if (!shared_mem_service_data.get()) 259 return false; 260 261 uint32 alloc_size = sizeof(ServiceProcessSharedData); 262 // TODO(viettrungluu): Named shared memory is deprecated (crbug.com/345734). 263 if (!shared_mem_service_data->CreateNamedDeprecated 264 (GetServiceProcessSharedMemName(), true, alloc_size)) 265 return false; 266 267 if (!shared_mem_service_data->Map(alloc_size)) 268 return false; 269 270 memset(shared_mem_service_data->memory(), 0, alloc_size); 271 ServiceProcessSharedData* shared_data = 272 reinterpret_cast<ServiceProcessSharedData*>( 273 shared_mem_service_data->memory()); 274 memcpy(shared_data->service_process_version, version_info.Version().c_str(), 275 version_info.Version().length()); 276 shared_data->service_process_pid = base::GetCurrentProcId(); 277 shared_mem_service_data_.reset(shared_mem_service_data.release()); 278 return true; 279 } 280 281 IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() { 282 return ::GetServiceProcessChannel(); 283 } 284 285 #endif // !OS_MACOSX 286