Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / chrome / common / service_process_util.cc
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 <algorithm>
6
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/memory/singleton.h"
10 #include "base/path_service.h"
11 #include "base/sha1.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/version.h"
17 #include "chrome/common/chrome_constants.h"
18 #include "chrome/common/chrome_paths.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/chrome_version_info.h"
21 #include "chrome/common/service_process_util.h"
22 #include "content/public/common/content_paths.h"
23
24 #if !defined(OS_MACOSX)
25
26 namespace {
27
28 // This should be more than enough to hold a version string assuming each part
29 // of the version string is an int64.
30 const uint32 kMaxVersionStringLength = 256;
31
32 // The structure that gets written to shared memory.
33 struct ServiceProcessSharedData {
34   char service_process_version[kMaxVersionStringLength];
35   base::ProcessId service_process_pid;
36 };
37
38 // Gets the name of the shared memory used by the service process to write its
39 // version. The name is not versioned.
40 std::string GetServiceProcessSharedMemName() {
41   return GetServiceProcessScopedName("_service_shmem");
42 }
43
44 enum ServiceProcessRunningState {
45   SERVICE_NOT_RUNNING,
46   SERVICE_OLDER_VERSION_RUNNING,
47   SERVICE_SAME_VERSION_RUNNING,
48   SERVICE_NEWER_VERSION_RUNNING,
49 };
50
51 ServiceProcessRunningState GetServiceProcessRunningState(
52     std::string* service_version_out, base::ProcessId* pid_out) {
53   std::string version;
54   if (!GetServiceProcessData(&version, pid_out))
55     return SERVICE_NOT_RUNNING;
56
57 #if defined(OS_POSIX)
58   // We only need to check for service running on POSIX because Windows cleans
59   // up shared memory files when an app crashes, so there isn't a chance of
60   // us reading bogus data from shared memory for an app that has died.
61   if (!CheckServiceProcessReady()) {
62     return SERVICE_NOT_RUNNING;
63   }
64 #endif  // defined(OS_POSIX)
65
66   // At this time we have a version string. Set the out param if it exists.
67   if (service_version_out)
68     *service_version_out = version;
69
70   Version service_version(version);
71   // If the version string is invalid, treat it like an older version.
72   if (!service_version.IsValid())
73     return SERVICE_OLDER_VERSION_RUNNING;
74
75   // Get the version of the currently *running* instance of Chrome.
76   chrome::VersionInfo version_info;
77   if (!version_info.is_valid()) {
78     NOTREACHED() << "Failed to get current file version";
79     // Our own version is invalid. This is an error case. Pretend that we
80     // are out of date.
81     return SERVICE_NEWER_VERSION_RUNNING;
82   }
83   Version running_version(version_info.Version());
84   if (!running_version.IsValid()) {
85     NOTREACHED() << "Failed to parse version info";
86     // Our own version is invalid. This is an error case. Pretend that we
87     // are out of date.
88     return SERVICE_NEWER_VERSION_RUNNING;
89   }
90
91   if (running_version.CompareTo(service_version) > 0) {
92     return SERVICE_OLDER_VERSION_RUNNING;
93   } else if (service_version.CompareTo(running_version) > 0) {
94     return SERVICE_NEWER_VERSION_RUNNING;
95   }
96   return SERVICE_SAME_VERSION_RUNNING;
97 }
98
99 }  // namespace
100
101 // Return a name that is scoped to this instance of the service process. We
102 // use the hash of the user-data-dir as a scoping prefix. We can't use
103 // the user-data-dir itself as we have limits on the size of the lock names.
104 std::string GetServiceProcessScopedName(const std::string& append_str) {
105   base::FilePath user_data_dir;
106   PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
107 #if defined(OS_WIN)
108   std::string user_data_dir_path = base::WideToUTF8(user_data_dir.value());
109 #elif defined(OS_POSIX)
110   std::string user_data_dir_path = user_data_dir.value();
111 #endif  // defined(OS_WIN)
112   std::string hash = base::SHA1HashString(user_data_dir_path);
113   std::string hex_hash = base::HexEncode(hash.c_str(), hash.length());
114   return hex_hash + "." + append_str;
115 }
116
117 // Return a name that is scoped to this instance of the service process. We
118 // use the user-data-dir and the version as a scoping prefix.
119 std::string GetServiceProcessScopedVersionedName(
120     const std::string& append_str) {
121   std::string versioned_str;
122   chrome::VersionInfo version_info;
123   DCHECK(version_info.is_valid());
124   versioned_str.append(version_info.Version());
125   versioned_str.append(append_str);
126   return GetServiceProcessScopedName(versioned_str);
127 }
128
129 // Reads the named shared memory to get the shared data. Returns false if no
130 // matching shared memory was found.
131 bool GetServiceProcessData(std::string* version, base::ProcessId* pid) {
132   scoped_ptr<base::SharedMemory> shared_mem_service_data;
133   shared_mem_service_data.reset(new base::SharedMemory());
134   ServiceProcessSharedData* service_data = NULL;
135   if (shared_mem_service_data.get() &&
136       shared_mem_service_data->Open(GetServiceProcessSharedMemName(), true) &&
137       shared_mem_service_data->Map(sizeof(ServiceProcessSharedData))) {
138     service_data = reinterpret_cast<ServiceProcessSharedData*>(
139         shared_mem_service_data->memory());
140     // Make sure the version in shared memory is null-terminated. If it is not,
141     // treat it as invalid.
142     if (version && memchr(service_data->service_process_version, '\0',
143                           sizeof(service_data->service_process_version)))
144       *version = service_data->service_process_version;
145     if (pid)
146       *pid = service_data->service_process_pid;
147     return true;
148   }
149   return false;
150 }
151
152 #endif  // !OS_MACOSX
153
154 ServiceProcessState::ServiceProcessState() : state_(NULL) {
155   CreateAutoRunCommandLine();
156   CreateState();
157 }
158
159 ServiceProcessState::~ServiceProcessState() {
160 #if !defined(OS_MACOSX)
161   if (shared_mem_service_data_.get()) {
162     shared_mem_service_data_->Delete(GetServiceProcessSharedMemName());
163   }
164 #endif  // !OS_MACOSX
165   TearDownState();
166 }
167
168 void ServiceProcessState::SignalStopped() {
169   TearDownState();
170   shared_mem_service_data_.reset();
171 }
172
173 #if !defined(OS_MACOSX)
174 bool ServiceProcessState::Initialize() {
175   if (!TakeSingletonLock()) {
176     return false;
177   }
178   // Now that we have the singleton, take care of killing an older version, if
179   // it exists.
180   if (!HandleOtherVersion())
181     return false;
182
183   // Write the version we are using to shared memory. This can be used by a
184   // newer service to signal us to exit.
185   return CreateSharedData();
186 }
187
188 bool ServiceProcessState::HandleOtherVersion() {
189   std::string running_version;
190   base::ProcessId process_id = 0;
191   ServiceProcessRunningState state =
192       GetServiceProcessRunningState(&running_version, &process_id);
193   switch (state) {
194     case SERVICE_SAME_VERSION_RUNNING:
195     case SERVICE_NEWER_VERSION_RUNNING:
196       return false;
197     case SERVICE_OLDER_VERSION_RUNNING:
198       // If an older version is running, kill it.
199       ForceServiceProcessShutdown(running_version, process_id);
200       break;
201     case SERVICE_NOT_RUNNING:
202       break;
203   }
204   return true;
205 }
206
207 bool ServiceProcessState::CreateSharedData() {
208   chrome::VersionInfo version_info;
209   if (!version_info.is_valid()) {
210     NOTREACHED() << "Failed to get current file version";
211     return false;
212   }
213   if (version_info.Version().length() >= kMaxVersionStringLength) {
214     NOTREACHED() << "Version string length is << " <<
215         version_info.Version().length() << "which is longer than" <<
216         kMaxVersionStringLength;
217     return false;
218   }
219
220   scoped_ptr<base::SharedMemory> shared_mem_service_data(
221       new base::SharedMemory());
222   if (!shared_mem_service_data.get())
223     return false;
224
225   uint32 alloc_size = sizeof(ServiceProcessSharedData);
226   if (!shared_mem_service_data->CreateNamed(GetServiceProcessSharedMemName(),
227                                             true, alloc_size))
228     return false;
229
230   if (!shared_mem_service_data->Map(alloc_size))
231     return false;
232
233   memset(shared_mem_service_data->memory(), 0, alloc_size);
234   ServiceProcessSharedData* shared_data =
235       reinterpret_cast<ServiceProcessSharedData*>(
236           shared_mem_service_data->memory());
237   memcpy(shared_data->service_process_version, version_info.Version().c_str(),
238          version_info.Version().length());
239   shared_data->service_process_pid = base::GetCurrentProcId();
240   shared_mem_service_data_.reset(shared_mem_service_data.release());
241   return true;
242 }
243
244 IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() {
245   return ::GetServiceProcessChannel();
246 }
247
248 #endif  // !OS_MACOSX
249
250 void ServiceProcessState::CreateAutoRunCommandLine() {
251   base::FilePath exe_path;
252   PathService::Get(content::CHILD_PROCESS_EXE, &exe_path);
253   DCHECK(!exe_path.empty()) << "Unable to get service process binary name.";
254   autorun_command_line_.reset(new CommandLine(exe_path));
255   autorun_command_line_->AppendSwitchASCII(switches::kProcessType,
256                                            switches::kServiceProcess);
257
258   // The user data directory is the only other flag we currently want to
259   // possibly store.
260   const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
261   base::FilePath user_data_dir =
262     browser_command_line.GetSwitchValuePath(switches::kUserDataDir);
263   if (!user_data_dir.empty())
264     autorun_command_line_->AppendSwitchPath(switches::kUserDataDir,
265                                             user_data_dir);
266 }