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