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
30namespace {
31
32// This should be more than enough to hold a version string assuming each part
33// of the version string is an int64.
34const uint32 kMaxVersionStringLength = 256;
35
36// The structure that gets written to shared memory.
37struct 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.
44std::string GetServiceProcessSharedMemName() {
45  return GetServiceProcessScopedName("_service_shmem");
46}
47
48enum ServiceProcessRunningState {
49  SERVICE_NOT_RUNNING,
50  SERVICE_OLDER_VERSION_RUNNING,
51  SERVICE_SAME_VERSION_RUNNING,
52  SERVICE_NEWER_VERSION_RUNNING,
53};
54
55ServiceProcessRunningState 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.
108std::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.
123std::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.
135bool 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
158scoped_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
190ServiceProcessState::ServiceProcessState() : state_(NULL) {
191  autorun_command_line_ = CreateServiceProcessCommandLine();
192  CreateState();
193}
194
195ServiceProcessState::~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
204void ServiceProcessState::SignalStopped() {
205  TearDownState();
206  shared_mem_service_data_.reset();
207}
208
209#if !defined(OS_MACOSX)
210bool 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
224bool 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
243bool 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
281IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() {
282  return ::GetServiceProcessChannel();
283}
284
285#endif  // !OS_MACOSX
286