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.
5 #include "chrome/common/service_process_util.h"
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"
28 #if !defined(OS_MACOSX)
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;
36 // The structure that gets written to shared memory.
37 struct ServiceProcessSharedData
{
38 char service_process_version
[kMaxVersionStringLength
];
39 base::ProcessId service_process_pid
;
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");
48 enum ServiceProcessRunningState
{
50 SERVICE_OLDER_VERSION_RUNNING
,
51 SERVICE_SAME_VERSION_RUNNING
,
52 SERVICE_NEWER_VERSION_RUNNING
,
55 ServiceProcessRunningState
GetServiceProcessRunningState(
56 std::string
* service_version_out
, base::ProcessId
* pid_out
) {
58 if (!GetServiceProcessData(&version
, pid_out
))
59 return SERVICE_NOT_RUNNING
;
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
;
68 #endif // defined(OS_POSIX)
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
;
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
;
79 // Get the version of the currently *running* instance of Chrome.
80 chrome::VersionInfo version_info
;
81 Version
running_version(version_info
.Version());
82 if (!running_version
.IsValid()) {
83 NOTREACHED() << "Failed to parse version info";
84 // Our own version is invalid. This is an error case. Pretend that we
86 return SERVICE_NEWER_VERSION_RUNNING
;
89 if (running_version
.CompareTo(service_version
) > 0) {
90 return SERVICE_OLDER_VERSION_RUNNING
;
91 } else if (service_version
.CompareTo(running_version
) > 0) {
92 return SERVICE_NEWER_VERSION_RUNNING
;
94 return SERVICE_SAME_VERSION_RUNNING
;
99 // Return a name that is scoped to this instance of the service process. We
100 // use the hash of the user-data-dir as a scoping prefix. We can't use
101 // the user-data-dir itself as we have limits on the size of the lock names.
102 std::string
GetServiceProcessScopedName(const std::string
& append_str
) {
103 base::FilePath user_data_dir
;
104 PathService::Get(chrome::DIR_USER_DATA
, &user_data_dir
);
106 std::string user_data_dir_path
= base::WideToUTF8(user_data_dir
.value());
107 #elif defined(OS_POSIX)
108 std::string user_data_dir_path
= user_data_dir
.value();
109 #endif // defined(OS_WIN)
110 std::string hash
= base::SHA1HashString(user_data_dir_path
);
111 std::string hex_hash
= base::HexEncode(hash
.c_str(), hash
.length());
112 return hex_hash
+ "." + append_str
;
115 // Return a name that is scoped to this instance of the service process. We
116 // use the user-data-dir and the version as a scoping prefix.
117 std::string
GetServiceProcessScopedVersionedName(
118 const std::string
& append_str
) {
119 std::string versioned_str
;
120 chrome::VersionInfo version_info
;
121 versioned_str
.append(version_info
.Version());
122 versioned_str
.append(append_str
);
123 return GetServiceProcessScopedName(versioned_str
);
126 // Reads the named shared memory to get the shared data. Returns false if no
127 // matching shared memory was found.
128 bool GetServiceProcessData(std::string
* version
, base::ProcessId
* pid
) {
129 scoped_ptr
<base::SharedMemory
> shared_mem_service_data
;
130 shared_mem_service_data
.reset(new base::SharedMemory());
131 ServiceProcessSharedData
* service_data
= NULL
;
132 if (shared_mem_service_data
.get() &&
133 shared_mem_service_data
->Open(GetServiceProcessSharedMemName(), true) &&
134 shared_mem_service_data
->Map(sizeof(ServiceProcessSharedData
))) {
135 service_data
= reinterpret_cast<ServiceProcessSharedData
*>(
136 shared_mem_service_data
->memory());
137 // Make sure the version in shared memory is null-terminated. If it is not,
138 // treat it as invalid.
139 if (version
&& memchr(service_data
->service_process_version
, '\0',
140 sizeof(service_data
->service_process_version
)))
141 *version
= service_data
->service_process_version
;
143 *pid
= service_data
->service_process_pid
;
151 scoped_ptr
<base::CommandLine
> CreateServiceProcessCommandLine() {
152 base::FilePath exe_path
;
153 PathService::Get(content::CHILD_PROCESS_EXE
, &exe_path
);
154 DCHECK(!exe_path
.empty()) << "Unable to get service process binary name.";
155 scoped_ptr
<base::CommandLine
> command_line(new base::CommandLine(exe_path
));
156 command_line
->AppendSwitchASCII(switches::kProcessType
,
157 switches::kServiceProcess
);
158 static const char* const kSwitchesToCopy
[] = {
159 switches::kCloudPrintSetupProxy
,
160 switches::kCloudPrintURL
,
161 switches::kCloudPrintXmppEndpoint
,
163 switches::kEnableCloudPrintXps
,
165 switches::kEnableLogging
,
166 switches::kIgnoreUrlFetcherCertRequests
,
168 switches::kLoggingLevel
,
170 switches::kNoServiceAutorun
,
171 switches::kUserDataDir
,
174 switches::kWaitForDebugger
,
177 command_line
->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
179 arraysize(kSwitchesToCopy
));
180 return command_line
.Pass();
183 ServiceProcessState::ServiceProcessState() : state_(NULL
) {
184 autorun_command_line_
= CreateServiceProcessCommandLine();
188 ServiceProcessState::~ServiceProcessState() {
189 #if !defined(OS_MACOSX)
190 if (shared_mem_service_data_
.get()) {
191 shared_mem_service_data_
->Delete(GetServiceProcessSharedMemName());
197 void ServiceProcessState::SignalStopped() {
199 shared_mem_service_data_
.reset();
202 #if !defined(OS_MACOSX)
203 bool ServiceProcessState::Initialize() {
204 if (!TakeSingletonLock()) {
207 // Now that we have the singleton, take care of killing an older version, if
209 if (!HandleOtherVersion())
212 // Write the version we are using to shared memory. This can be used by a
213 // newer service to signal us to exit.
214 return CreateSharedData();
217 bool ServiceProcessState::HandleOtherVersion() {
218 std::string running_version
;
219 base::ProcessId process_id
= 0;
220 ServiceProcessRunningState state
=
221 GetServiceProcessRunningState(&running_version
, &process_id
);
223 case SERVICE_SAME_VERSION_RUNNING
:
224 case SERVICE_NEWER_VERSION_RUNNING
:
226 case SERVICE_OLDER_VERSION_RUNNING
:
227 // If an older version is running, kill it.
228 ForceServiceProcessShutdown(running_version
, process_id
);
230 case SERVICE_NOT_RUNNING
:
236 bool ServiceProcessState::CreateSharedData() {
237 chrome::VersionInfo version_info
;
238 if (version_info
.Version().length() >= kMaxVersionStringLength
) {
239 NOTREACHED() << "Version string length is << " <<
240 version_info
.Version().length() << "which is longer than" <<
241 kMaxVersionStringLength
;
245 scoped_ptr
<base::SharedMemory
> shared_mem_service_data(
246 new base::SharedMemory());
247 if (!shared_mem_service_data
.get())
250 uint32 alloc_size
= sizeof(ServiceProcessSharedData
);
251 // TODO(viettrungluu): Named shared memory is deprecated (crbug.com/345734).
252 if (!shared_mem_service_data
->CreateNamedDeprecated
253 (GetServiceProcessSharedMemName(), true, alloc_size
))
256 if (!shared_mem_service_data
->Map(alloc_size
))
259 memset(shared_mem_service_data
->memory(), 0, alloc_size
);
260 ServiceProcessSharedData
* shared_data
=
261 reinterpret_cast<ServiceProcessSharedData
*>(
262 shared_mem_service_data
->memory());
263 memcpy(shared_data
->service_process_version
, version_info
.Version().c_str(),
264 version_info
.Version().length());
265 shared_data
->service_process_pid
= base::GetCurrentProcId();
266 shared_mem_service_data_
.reset(shared_mem_service_data
.release());
270 IPC::ChannelHandle
ServiceProcessState::GetServiceProcessChannel() {
271 return ::GetServiceProcessChannel();