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 <stdint.h>
8
9 #include <algorithm>
10 #include <memory>
11 #include <utility>
12
13 #include "base/base_switches.h"
14 #include "base/check.h"
15 #include "base/command_line.h"
16 #include "base/hash/sha1.h"
17 #include "base/memory/shared_memory_mapping.h"
18 #include "base/memory/singleton.h"
19 #include "base/notreached.h"
20 #include "base/path_service.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string16.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/version.h"
27 #include "build/build_config.h"
28 #include "chrome/common/chrome_constants.h"
29 #include "chrome/common/chrome_paths.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "components/cloud_devices/common/cloud_devices_switches.h"
32 #include "components/crash/core/app/crash_switches.h"
33 #include "components/crash/core/app/crashpad.h"
34 #include "components/network_session_configurator/common/network_switches.h"
35 #include "components/version_info/version_info.h"
36 #include "content/public/common/content_paths.h"
37 #include "content/public/common/content_switches.h"
38 #include "google_apis/gaia/gaia_switches.h"
39 #include "services/network/public/cpp/network_switches.h"
40 #include "ui/base/ui_base_switches.h"
41
42 #if !defined(OS_MAC)
43
44 namespace {
45
46 // This should be more than enough to hold a version string assuming each part
47 // of the version string is an int64_t.
48 const uint32_t kMaxVersionStringLength = 256;
49
50 // The structure that gets written to shared memory.
51 struct ServiceProcessSharedData {
52 char service_process_version[kMaxVersionStringLength];
53 base::ProcessId service_process_pid;
54 };
55
56 } // namespace
57
58 // Return a name that is scoped to this instance of the service process. We
59 // use the user-data-dir and the version as a scoping prefix.
GetServiceProcessScopedVersionedName(const std::string & append_str)60 std::string GetServiceProcessScopedVersionedName(
61 const std::string& append_str) {
62 std::string versioned_str = version_info::GetVersionNumber();
63 versioned_str.append(append_str);
64 return GetServiceProcessScopedName(versioned_str);
65 }
66
67 // Reads the named shared memory to get the shared data. Returns false if no
68 // matching shared memory was found.
69 // static
GetServiceProcessData(std::string * version,base::ProcessId * pid)70 bool ServiceProcessState::GetServiceProcessData(std::string* version,
71 base::ProcessId* pid) {
72 base::ReadOnlySharedMemoryMapping service_process_data_mapping =
73 OpenServiceProcessDataMapping(sizeof(ServiceProcessSharedData));
74 if (!service_process_data_mapping.IsValid())
75 return false;
76
77 const ServiceProcessSharedData* service_data =
78 service_process_data_mapping.GetMemoryAs<ServiceProcessSharedData>();
79 // Make sure the version in shared memory is null-terminated. If it is not,
80 // treat it as invalid.
81 if (version && memchr(service_data->service_process_version, '\0',
82 sizeof(service_data->service_process_version)))
83 *version = service_data->service_process_version;
84 if (pid)
85 *pid = service_data->service_process_pid;
86 return true;
87 }
88 #endif // !OS_MAC
89
90 // Return a name that is scoped to this instance of the service process. We
91 // use the hash of the user-data-dir as a scoping prefix. We can't use
92 // the user-data-dir itself as we have limits on the size of the lock names.
GetServiceProcessScopedName(const std::string & append_str)93 std::string GetServiceProcessScopedName(const std::string& append_str) {
94 base::FilePath user_data_dir;
95 base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
96 #if defined(OS_WIN)
97 std::string user_data_dir_path = base::WideToUTF8(user_data_dir.value());
98 #elif defined(OS_POSIX)
99 std::string user_data_dir_path = user_data_dir.value();
100 #endif // defined(OS_WIN)
101 std::string hash = base::SHA1HashString(user_data_dir_path);
102 std::string hex_hash = base::HexEncode(hash.c_str(), hash.length());
103 return hex_hash + "." + append_str;
104 }
105
CreateServiceProcessCommandLine()106 std::unique_ptr<base::CommandLine> CreateServiceProcessCommandLine() {
107 base::FilePath exe_path;
108 base::PathService::Get(content::CHILD_PROCESS_EXE, &exe_path);
109 DCHECK(!exe_path.empty()) << "Unable to get service process binary name.";
110 std::unique_ptr<base::CommandLine> command_line(
111 new base::CommandLine(exe_path));
112 command_line->AppendSwitchASCII(switches::kProcessType,
113 switches::kCloudPrintServiceProcess);
114
115 #if defined(OS_WIN)
116 command_line->AppendArg(switches::kPrefetchArgumentOther);
117 #endif // defined(OS_WIN)
118
119 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
120 if (crash_reporter::IsCrashpadEnabled()) {
121 command_line->AppendSwitch(crash_reporter::kEnableCrashpad);
122
123 pid_t pid;
124 if (crash_reporter::GetHandlerSocket(nullptr, &pid)) {
125 command_line->AppendSwitchASCII(
126 crash_reporter::switches::kCrashpadHandlerPid,
127 base::NumberToString(pid));
128 }
129 }
130 #endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
131
132 static const char* const kSwitchesToCopy[] = {
133 network::switches::kIgnoreUrlFetcherCertRequests,
134 switches::kCloudPrintSetupProxy,
135 switches::kCloudPrintURL,
136 switches::kCloudPrintXmppEndpoint,
137 switches::kEnableLogging,
138 switches::kLang,
139 switches::kLoggingLevel,
140 switches::kLsoUrl,
141 switches::kNoServiceAutorun,
142 switches::kUserDataDir,
143 switches::kV,
144 switches::kVModule,
145 switches::kWaitForDebugger,
146 };
147
148 command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
149 kSwitchesToCopy, base::size(kSwitchesToCopy));
150 return command_line;
151 }
152
ServiceProcessState()153 ServiceProcessState::ServiceProcessState() : state_(nullptr) {
154 autorun_command_line_ = CreateServiceProcessCommandLine();
155 CreateState();
156 }
157
~ServiceProcessState()158 ServiceProcessState::~ServiceProcessState() {
159 #if !defined(OS_MAC)
160 if (service_process_data_region_.IsValid()) {
161 service_process_data_region_ = {};
162 DeleteServiceProcessDataRegion();
163 }
164 #endif // !OS_MAC
165 TearDownState();
166 }
167
SignalStopped()168 void ServiceProcessState::SignalStopped() {
169 TearDownState();
170 #if !defined(OS_MAC)
171 service_process_data_region_ = {};
172 #endif // !OS_MAC
173 }
174
175 #if !defined(OS_MAC)
Initialize()176 bool ServiceProcessState::Initialize() {
177 if (!TakeSingletonLock()) {
178 return false;
179 }
180 // Now that we have the singleton, take care of killing an older version, if
181 // it exists.
182 if (!HandleOtherVersion())
183 return false;
184
185 // Write the version we are using to shared memory. This can be used by a
186 // newer service to signal us to exit.
187 return CreateSharedData();
188 }
189
190 // static
GetServiceProcessSharedMemName()191 std::string ServiceProcessState::GetServiceProcessSharedMemName() {
192 return GetServiceProcessScopedName("_service_shmem");
193 }
194
HandleOtherVersion()195 bool ServiceProcessState::HandleOtherVersion() {
196 std::string running_version;
197 base::ProcessId process_id = 0;
198 ServiceProcessRunningState state =
199 GetServiceProcessRunningState(&running_version, &process_id);
200 switch (state) {
201 case SERVICE_SAME_VERSION_RUNNING:
202 case SERVICE_NEWER_VERSION_RUNNING:
203 return false;
204 case SERVICE_OLDER_VERSION_RUNNING:
205 // If an older version is running, kill it.
206 ForceServiceProcessShutdown(running_version, process_id);
207 break;
208 case SERVICE_NOT_RUNNING:
209 break;
210 }
211 return true;
212 }
213
CreateSharedData()214 bool ServiceProcessState::CreateSharedData() {
215 if (version_info::GetVersionNumber().length() >= kMaxVersionStringLength) {
216 NOTREACHED() << "Version string length is << "
217 << version_info::GetVersionNumber().length()
218 << " which is longer than" << kMaxVersionStringLength;
219 return false;
220 }
221
222 uint32_t alloc_size = sizeof(ServiceProcessSharedData);
223 service_process_data_region_ = CreateServiceProcessDataRegion(alloc_size);
224 if (!service_process_data_region_.IsValid())
225 return false;
226 base::WritableSharedMemoryMapping mapping =
227 service_process_data_region_.Map();
228 if (!mapping.IsValid())
229 return false;
230 memset(mapping.memory(), 0, alloc_size);
231 ServiceProcessSharedData* shared_data =
232 mapping.GetMemoryAs<ServiceProcessSharedData>();
233 DCHECK(shared_data);
234 memcpy(shared_data->service_process_version,
235 version_info::GetVersionNumber().c_str(),
236 version_info::GetVersionNumber().length());
237 shared_data->service_process_pid = base::GetCurrentProcId();
238 return true;
239 }
240
241 // static
242 ServiceProcessState::ServiceProcessRunningState
GetServiceProcessRunningState(std::string * service_version_out,base::ProcessId * pid_out)243 ServiceProcessState::GetServiceProcessRunningState(
244 std::string* service_version_out,
245 base::ProcessId* pid_out) {
246 std::string version;
247 if (!ServiceProcessState::GetServiceProcessData(&version, pid_out))
248 return SERVICE_NOT_RUNNING;
249
250 #if defined(OS_POSIX)
251 // We only need to check for service running on POSIX because Windows cleans
252 // up shared memory files when an app crashes, so there isn't a chance of
253 // us reading bogus data from shared memory for an app that has died.
254 if (!CheckServiceProcessReady()) {
255 return SERVICE_NOT_RUNNING;
256 }
257 #endif // defined(OS_POSIX)
258
259 // At this time we have a version string. Set the out param if it exists.
260 if (service_version_out)
261 *service_version_out = version;
262
263 base::Version service_version(version);
264 // If the version string is invalid, treat it like an older version.
265 if (!service_version.IsValid())
266 return SERVICE_OLDER_VERSION_RUNNING;
267
268 // Get the version of the currently *running* instance of Chrome.
269 const base::Version& running_version = version_info::GetVersion();
270 if (!running_version.IsValid()) {
271 NOTREACHED() << "Failed to parse version info";
272 // Our own version is invalid. This is an error case. Pretend that we
273 // are out of date.
274 return SERVICE_NEWER_VERSION_RUNNING;
275 }
276
277 int comp = running_version.CompareTo(service_version);
278 if (comp == 0)
279 return SERVICE_SAME_VERSION_RUNNING;
280 return comp > 0 ? SERVICE_OLDER_VERSION_RUNNING
281 : SERVICE_NEWER_VERSION_RUNNING;
282 }
283
284 mojo::NamedPlatformChannel::ServerName
GetServiceProcessServerName()285 ServiceProcessState::GetServiceProcessServerName() {
286 return ::GetServiceProcessServerName();
287 }
288
289 #endif // !OS_MAC
290