Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(153)

Side by Side Diff: remoting/host/plugin/daemon_installer_win.cc

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

Powered by Google App Engine
This is Rietveld 408576698