OLD | NEW |
---|---|
(Empty) | |
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 "remoting/host/plugin/daemon_installer_win.h" | |
6 | |
7 #include <windows.h> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/message_loop.h" | |
11 #include "base/process_util.h" | |
12 #include "base/string16.h" | |
13 #include "base/stringize_macros.h" | |
14 #include "base/stringprintf.h" | |
15 #include "base/time.h" | |
16 #include "base/timer.h" | |
17 #include "base/win/object_watcher.h" | |
18 #include "base/win/registry.h" | |
19 #include "base/win/scoped_bstr.h" | |
20 #include "base/win/scoped_comptr.h" | |
21 #include "base/win/scoped_handle.h" | |
22 | |
23 namespace omaha { | |
24 #include "google_update/google_update_idl.h" | |
25 } // namespace omaha | |
Sergey Ulanov
2012/04/09 22:32:58
nit: two spaces before the comment
alexeypa (please no reviews)
2012/04/10 00:20:06
Done.
| |
26 | |
27 using base::win::ScopedBstr; | |
28 using base::win::ScopedComPtr; | |
29 | |
30 namespace { | |
31 | |
32 // The COM elevation moniker for Omaha. | |
33 const char16 kOmahaElevationMoniker[] = | |
34 TO_L_STRING("Elevation:Administrator!new:GoogleUpdate.Update3WebMachine"); | |
35 | |
36 // The registry key where the configuration of Omaha is stored. | |
37 const char16 kOmahaUpdateKeyName[] = TO_L_STRING("Software\\Google\\Update"); | |
38 | |
39 // The name of the value where the full path to GoogleUpdate.exe is stored. | |
40 const char16 kOmahaPathValueName[] = TO_L_STRING("path"); | |
41 | |
42 // The command line format string for GoogleUpdate.exe | |
43 const char16 kGoogleUpdateCommandLineFormat[] = | |
44 TO_L_STRING("\"%ls\" /install \"bundlename=Chromoting%%20Host&appguid=%ls&") | |
45 TO_L_STRING("appname=Chromoting%%20Host&needsadmin=True&lang=%ls\""); | |
46 | |
47 // The Omaha Appid of the host. | |
48 const char16 kOmahaAppid[] = | |
49 TO_L_STRING("{b210701e-ffc4-49e3-932b-370728c72662}"); | |
50 | |
51 // TODO(alexeypa): Get the desired laungage from the web app. | |
52 const char16 kOmahaLanguage[] = TO_L_STRING("en"); | |
53 | |
54 // An empty string for optional parameters. | |
55 const char16 kOmahaEmpty[] = TO_L_STRING(""); | |
56 | |
57 // The installation status polling interval. | |
58 const int kOmahaPollIntervalMs = 500; | |
59 | |
60 } // namespace | |
61 | |
62 namespace remoting { | |
63 | |
64 // This class implements on-demand installation of the Chromoting Host via | |
65 // per-machine Omaha instance. | |
66 class DaemonComInstallerWin : public DaemonInstallerWin { | |
67 public: | |
68 DaemonComInstallerWin(const ScopedComPtr<omaha::IGoogleUpdate3Web>& update3, | |
69 const CompletionCallback& done); | |
70 | |
71 // DaemonInstallerWin implementation. | |
72 virtual void Install() OVERRIDE; | |
73 | |
74 private: | |
75 // Polls the installation status performing state-specific actions (such as | |
76 // starting installation once download has finished). | |
77 void PollInstallationStatus(); | |
78 | |
79 // Omaha interfaces. | |
80 ScopedComPtr<omaha::IAppWeb> app_; | |
81 ScopedComPtr<omaha::IAppBundleWeb> bundle_; | |
82 ScopedComPtr<omaha::IGoogleUpdate3Web> update3_; | |
83 | |
84 base::DelayTimer<DaemonComInstallerWin> polling_timer_; | |
Sergey Ulanov
2012/04/09 22:32:58
Why DelayTimer instead of RepeatingTimer?
Or you c
alexeypa (please no reviews)
2012/04/10 00:20:06
RepeatingTimer has to be stopped explicitly, so I'
Sergey Ulanov
2012/04/10 01:17:14
Hm, then maybe use base::Timer? It's not a templat
| |
85 }; | |
86 | |
87 // This class implements on-demand installation of the Chromoting Host by | |
88 // launching a per-user instance of Omaha and requesting elevation. | |
89 class DaemonCommandLineInstallerWin | |
90 : public DaemonInstallerWin, | |
91 public base::win::ObjectWatcher::Delegate { | |
92 public: | |
93 DaemonCommandLineInstallerWin(const CompletionCallback& done); | |
94 ~DaemonCommandLineInstallerWin(); | |
95 | |
96 // DaemonInstallerWin implementation. | |
97 virtual void Install() OVERRIDE; | |
98 | |
99 // base::win::ObjectWatcher::Delegate implementation. | |
100 virtual void OnObjectSignaled(HANDLE object) OVERRIDE; | |
101 | |
102 private: | |
103 // Handle of the launched process. | |
104 base::win::ScopedHandle process_; | |
105 | |
106 // Used to determine when the launched process terminates. | |
107 base::win::ObjectWatcher process_watcher_; | |
108 }; | |
109 | |
110 DaemonComInstallerWin::DaemonComInstallerWin( | |
111 const ScopedComPtr<omaha::IGoogleUpdate3Web>& update3, | |
112 const CompletionCallback& done) | |
113 : DaemonInstallerWin(done), | |
114 update3_(update3), | |
115 ALLOW_THIS_IN_INITIALIZER_LIST( | |
116 polling_timer_( | |
117 FROM_HERE, | |
118 base::TimeDelta::FromMilliseconds(kOmahaPollIntervalMs), | |
119 this, | |
120 &DaemonComInstallerWin::PollInstallationStatus)) { | |
121 } | |
122 | |
123 void DaemonComInstallerWin::Install() { | |
124 // Create an app bundle. | |
125 ScopedComPtr<IDispatch> dispatch; | |
126 HRESULT hr = update3_->createAppBundleWeb(dispatch.Receive()); | |
127 if (FAILED(hr)) { | |
128 Done(hr); | |
129 return; | |
130 } | |
131 | |
132 hr = dispatch.QueryInterface(omaha::IID_IAppBundleWeb, bundle_.ReceiveVoid()); | |
133 if (FAILED(hr)) { | |
134 Done(hr); | |
135 return; | |
136 } | |
137 | |
138 hr = bundle_->initialize(); | |
139 if (FAILED(hr)) { | |
140 Done(hr); | |
141 return; | |
142 } | |
143 | |
144 // Add Chromoting Host to the bundle. | |
145 ScopedBstr appid(kOmahaAppid); | |
146 ScopedBstr empty(kOmahaEmpty); | |
147 ScopedBstr language(kOmahaLanguage); | |
148 hr = bundle_->createApp(appid, empty, language, empty); | |
149 if (FAILED(hr)) { | |
150 Done(hr); | |
151 return; | |
152 } | |
153 | |
154 hr = bundle_->checkForUpdate(); | |
155 if (FAILED(hr)) { | |
156 Done(hr); | |
157 return; | |
158 } | |
159 | |
160 dispatch.Release(); | |
161 hr = bundle_->get_appWeb(0, dispatch.Receive()); | |
162 if (FAILED(hr)) { | |
163 Done(hr); | |
164 return; | |
165 } | |
166 | |
167 hr = dispatch.QueryInterface(omaha::IID_IAppWeb, | |
168 app_.ReceiveVoid()); | |
169 if (FAILED(hr)) { | |
170 Done(hr); | |
171 return; | |
172 } | |
173 | |
174 // Now poll for the installation status. | |
175 PollInstallationStatus(); | |
176 } | |
177 | |
178 void DaemonComInstallerWin::PollInstallationStatus() { | |
179 // Get the current application installation state. | |
180 // N.B. The object underlying the ICurrentState interface has static data that | |
181 // does not get updated as the server state changes. To get the most "current" | |
182 // state, the currentState property needs to be queried again. | |
183 ScopedComPtr<IDispatch> dispatch; | |
184 HRESULT hr = app_->get_currentState(dispatch.Receive()); | |
185 if (FAILED(hr)) { | |
186 Done(hr); | |
187 return; | |
188 } | |
189 | |
190 ScopedComPtr<omaha::ICurrentState> current_state; | |
191 hr = dispatch.QueryInterface(omaha::IID_ICurrentState, | |
192 current_state.ReceiveVoid()); | |
193 if (FAILED(hr)) { | |
194 Done(hr); | |
195 return; | |
196 } | |
197 | |
198 LONG state; | |
199 hr = current_state->get_stateValue(&state); | |
200 if (FAILED(hr)) { | |
201 Done(hr); | |
202 return; | |
203 } | |
204 | |
205 // Perform state-specific actions. | |
206 switch (state) { | |
207 case omaha::STATE_INIT: | |
208 case omaha::STATE_WAITING_TO_CHECK_FOR_UPDATE: | |
209 case omaha::STATE_CHECKING_FOR_UPDATE: | |
210 case omaha::STATE_WAITING_TO_DOWNLOAD: | |
211 case omaha::STATE_RETRYING_DOWNLOAD: | |
212 case omaha::STATE_DOWNLOADING: | |
213 case omaha::STATE_WAITING_TO_INSTALL: | |
214 case omaha::STATE_INSTALLING: | |
215 case omaha::STATE_PAUSED: | |
216 break; | |
217 | |
218 case omaha::STATE_UPDATE_AVAILABLE: | |
219 hr = bundle_->download(); | |
220 if (FAILED(hr)) { | |
221 Done(hr); | |
222 return; | |
223 } | |
224 break; | |
225 | |
226 case omaha::STATE_DOWNLOAD_COMPLETE: | |
227 case omaha::STATE_EXTRACTING: | |
228 case omaha::STATE_APPLYING_DIFFERENTIAL_PATCH: | |
229 case omaha::STATE_READY_TO_INSTALL: | |
230 hr = bundle_->install(); | |
231 if (FAILED(hr)) { | |
232 Done(hr); | |
233 return; | |
234 } | |
235 break; | |
236 | |
237 case omaha::STATE_INSTALL_COMPLETE: | |
238 case omaha::STATE_NO_UPDATE: | |
239 // Installation complete or not required. Report success. | |
240 Done(S_OK); | |
241 return; | |
242 | |
243 case omaha::STATE_ERROR: { | |
244 HRESULT error_code; | |
245 hr = current_state->get_errorCode(&error_code); | |
246 if (FAILED(hr)) { | |
247 error_code = hr; | |
248 } | |
249 Done(error_code); | |
250 return; | |
251 } | |
252 | |
253 default: | |
254 LOG(ERROR) << "Unknown bundle state: " << state << "."; | |
255 Done(E_FAIL); | |
256 return; | |
257 } | |
258 | |
259 // Keep polling. | |
260 polling_timer_.Reset(); | |
261 } | |
262 | |
263 DaemonCommandLineInstallerWin::DaemonCommandLineInstallerWin( | |
264 const CompletionCallback& done) : DaemonInstallerWin(done) { | |
265 } | |
266 | |
267 DaemonCommandLineInstallerWin::~DaemonCommandLineInstallerWin() { | |
268 process_watcher_.StopWatching(); | |
269 } | |
270 | |
271 void DaemonCommandLineInstallerWin::Install() { | |
272 // Get the full path to GoogleUpdate.exe from the registry. | |
273 base::win::RegKey update_key; | |
274 LONG result = update_key.Open(HKEY_CURRENT_USER, | |
275 kOmahaUpdateKeyName, | |
276 KEY_READ); | |
277 if (result != ERROR_SUCCESS) { | |
278 Done(HRESULT_FROM_WIN32(result)); | |
279 return; | |
280 } | |
281 | |
282 string16 google_update; | |
283 result = update_key.ReadValue(kOmahaPathValueName, | |
284 &google_update); | |
285 if (result != ERROR_SUCCESS) { | |
286 Done(HRESULT_FROM_WIN32(result)); | |
287 return; | |
288 } | |
289 | |
290 // Launch the updater process and wait for its termination. | |
291 string16 command_line = | |
292 StringPrintf(kGoogleUpdateCommandLineFormat, | |
293 google_update.c_str(), | |
294 kOmahaAppid, | |
295 kOmahaLanguage); | |
296 | |
297 base::LaunchOptions options; | |
298 if (!base::LaunchProcess(command_line, options, process_.Receive())) { | |
299 result = GetLastError(); | |
300 Done(HRESULT_FROM_WIN32(result)); | |
301 return; | |
302 } | |
303 | |
304 if (!process_watcher_.StartWatching(process_.Get(), this)) { | |
305 result = GetLastError(); | |
306 Done(HRESULT_FROM_WIN32(result)); | |
307 return; | |
308 } | |
309 } | |
310 | |
311 void DaemonCommandLineInstallerWin::OnObjectSignaled(HANDLE object) { | |
312 // Check if the updater process returned success. | |
313 DWORD exit_code; | |
314 if (GetExitCodeProcess(process_.Get(), &exit_code) && exit_code == 0) { | |
315 Done(S_OK); | |
316 } else { | |
317 Done(E_FAIL); | |
318 } | |
319 } | |
320 | |
321 DaemonInstallerWin::DaemonInstallerWin(const CompletionCallback& done) | |
322 : done_(done) { | |
323 } | |
324 | |
325 DaemonInstallerWin::~DaemonInstallerWin() { | |
326 } | |
327 | |
328 void DaemonInstallerWin::Done(HRESULT result) { | |
329 done_.Run(result); | |
330 } | |
331 | |
332 // static | |
333 scoped_ptr<DaemonInstallerWin> DaemonInstallerWin::Create( | |
334 CompletionCallback done) { | |
335 // Check if the machine instance of Omaha is available. | |
336 BIND_OPTS3 bind_options; | |
337 memset(&bind_options, 0, sizeof(bind_options)); | |
338 bind_options.cbStruct = sizeof(bind_options); | |
339 bind_options.hwnd = NULL; | |
340 bind_options.dwClassContext = CLSCTX_LOCAL_SERVER; | |
341 | |
342 ScopedComPtr<omaha::IGoogleUpdate3Web> update3; | |
343 HRESULT result = ::CoGetObject( | |
344 kOmahaElevationMoniker, | |
345 &bind_options, | |
346 omaha::IID_IGoogleUpdate3Web, | |
347 update3.ReceiveVoid()); | |
348 if (SUCCEEDED(result)) { | |
349 // The machine instance of Omaha is available and we successfully passed | |
350 // the UAC prompt. | |
351 return scoped_ptr<DaemonInstallerWin>( | |
352 new DaemonComInstallerWin(update3, done)); | |
353 } else if (result == CO_E_CLASSSTRING) { | |
354 // The machine instance of Omaha is not available so we will have to run | |
355 // GoogleUpdate.exe manually passing "needsadmin=True". This will cause | |
356 // Omaha to install the machine instance first and then install Chromoting | |
357 // Host. | |
358 return scoped_ptr<DaemonInstallerWin>( | |
359 new DaemonCommandLineInstallerWin(done)); | |
360 } else { | |
361 // The user declined the UAC prompt or some other error occured. | |
362 done.Run(result); | |
363 return scoped_ptr<DaemonInstallerWin>(); | |
364 } | |
365 } | |
366 | |
367 } // namespace remoting | |
OLD | NEW |