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