OLD | NEW |
| (Empty) |
1 // Copyright 2008-2009 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 #include "omaha/tools/omahacompatibility/compatibility_test.h" | |
17 #include <Windows.h> | |
18 #include <tchar.h> | |
19 #include "base/basictypes.h" | |
20 #include "base/scoped_ptr.h" | |
21 #include "omaha/common/apply_tag.h" | |
22 #include "omaha/common/constants.h" | |
23 #include "omaha/common/debug.h" | |
24 #include "omaha/common/error.h" | |
25 #include "omaha/common/file.h" | |
26 #include "omaha/common/logging.h" | |
27 #include "omaha/common/path.h" | |
28 #include "omaha/common/reg_key.h" | |
29 #include "omaha/common/scope_guard.h" | |
30 #include "omaha/common/system.h" | |
31 #include "omaha/common/utils.h" | |
32 #include "omaha/goopdate/config_manager.h" | |
33 #include "omaha/goopdate/const_goopdate.h" | |
34 #include "omaha/tools/omahacompatibility/common/ping_observer.h" | |
35 #include "omaha/tools/omahacompatibility/httpserver/http_server.h" | |
36 #include "omaha/tools/omahacompatibility/httpserver/update_check_handler.h" | |
37 #include "omaha/tools/omahacompatibility/httpserver/download_handler.h" | |
38 | |
39 namespace omaha { | |
40 | |
41 const TCHAR* const kUpdateCheckUrlPath = _T("/service/update2"); | |
42 const TCHAR* const kDownloadUrlPath = _T("/download"); | |
43 const DWORD kAuCheckPeriodMs = 5 * 60 * 1000; | |
44 const TCHAR* const kHost = _T("localhost"); | |
45 const TCHAR* const kUrl = _T("http://localhost:8001/"); | |
46 const TCHAR* const kDownloadUrl = _T("http://localhost:8001/download"); | |
47 const TCHAR* const kUpdateCheckUrl = | |
48 _T("http://localhost:8001/service/update2"); | |
49 const int kPort = 8001; | |
50 | |
51 int CompatibilityTest::Main(bool test_omaha) { | |
52 CORE_LOG(L1, (_T("[Main]"))); | |
53 | |
54 if (IsOmahaInstalled()) { | |
55 printf("\nOmaha is already installed on this machine.\n"); | |
56 printf("The test may not work correctly.\n"); | |
57 printf("Ensure that the version of omaha being pointed to\n"); | |
58 printf("is atleast >= the registered versions reported above.\n"); | |
59 printf("Are you sure you want to run this test (Y/N)?"); | |
60 | |
61 int response = getchar(); | |
62 if (response == 'n' || response == 'N') { | |
63 return -1; | |
64 } | |
65 } | |
66 | |
67 // Read the config file. | |
68 HRESULT hr = ReadConfigFile(config_file_, | |
69 kDownloadUrl, | |
70 &config_responses_); | |
71 if (FAILED(hr)) { | |
72 return -1; | |
73 } | |
74 | |
75 // Create the observer with the guid. | |
76 console_writer_.reset(new ConsoleWriter( | |
77 GuidToString(config_responses_[0].guid))); | |
78 | |
79 // Setup the registry to make omaha talk to the local server. | |
80 // TODO(omaha): Warn user about changing user settings. | |
81 hr = SetupRegistry(config_responses_[0].needs_admin); | |
82 if (FAILED(hr)) { | |
83 return -1; | |
84 } | |
85 ON_SCOPE_EXIT(&CompatibilityTest::RestoreRegistry); | |
86 | |
87 printf("\nStarting the local http server.\n"); | |
88 hr = StartHttpServer(); | |
89 if (FAILED(hr)) { | |
90 printf("\nLocal Http server start failed.\n"); | |
91 return -1; | |
92 } | |
93 | |
94 if (test_omaha) { | |
95 printf("\nStarting Googleupdate to install application."); | |
96 printf("Please wait .....\n"); | |
97 printf("Omaha UI should show up in a sec."); | |
98 printf("If it does not, please kill this process and restart test.\n"); | |
99 | |
100 hr = StartGoogleUpdate(); | |
101 if (FAILED(hr)) { | |
102 // TODO(omaha): Maybe report a verbose error message indicating | |
103 // what could be wrong. | |
104 printf("\nThe installation failed.\n"); | |
105 printf("Please refer to the omaha integration documentation\n"); | |
106 printf("for more information, it is possible that when you rerun\n"); | |
107 printf("you get a warning about omaha already running,\n"); | |
108 printf("please choose to continue in this case\n"); | |
109 return -1; | |
110 } | |
111 | |
112 // TODO(omaha): Add a wait on the UI process, instead of depending on | |
113 // the user correctly dismissing the UI. | |
114 printf("\nThe installation completed successfully.\n"); | |
115 printf("Please dismiss the installer UI.\n"); | |
116 printf("Continue update test (Y/N)?"); | |
117 fflush(stdin); | |
118 int response = getchar(); | |
119 if (response =='n' || response == 'N') { | |
120 return -1; | |
121 } | |
122 | |
123 hr = StartApplicationUpdate(); | |
124 if (FAILED(hr)) { | |
125 printf("\nThe update failed.\n"); | |
126 printf("Please take a look at the omaha integration documentation\n"); | |
127 printf("and rerun the test. It is possible that when you rerun\n"); | |
128 printf("you might get a warning about omaha already running,\n"); | |
129 printf("please choose to continue in this case\n"); | |
130 return -1; | |
131 } | |
132 | |
133 printf("\nUpdate succeeded. Wee.\n"); | |
134 printf("Congratulations on a successful install and update.\n"); | |
135 printf("Your friendly compatibility assistant will take your leave now.\n"); | |
136 printf("Bye\n"); | |
137 } else { | |
138 while (true) { | |
139 ::SleepEx(10000, false); | |
140 } | |
141 } | |
142 | |
143 return 0; | |
144 } | |
145 | |
146 bool CompatibilityTest::IsOmahaInstalled() { | |
147 bool is_omaha_installed = false; | |
148 CString goopdate_key_name = | |
149 ConfigManager::Instance()->registry_clients_goopdate(true); | |
150 CString machine_version; | |
151 HRESULT hr = RegKey::GetValue(goopdate_key_name, kRegValueProductVersion, | |
152 &machine_version); | |
153 if (SUCCEEDED(hr)) { | |
154 is_omaha_installed = true; | |
155 } | |
156 | |
157 goopdate_key_name = | |
158 ConfigManager::Instance()->registry_clients_goopdate(false); | |
159 CString user_version; | |
160 hr = RegKey::GetValue(goopdate_key_name, | |
161 kRegValueProductVersion, | |
162 &user_version); | |
163 if (SUCCEEDED(hr)) { | |
164 is_omaha_installed = true; | |
165 } | |
166 | |
167 if (is_omaha_installed) { | |
168 printf("\nOmaha Installed on machine!\n"); | |
169 if (!machine_version.IsEmpty()) { | |
170 printf("Machine Omaha version: %s\n", CT2A(machine_version)); | |
171 } | |
172 | |
173 if (!user_version.IsEmpty()) { | |
174 printf("User Omaha version: %s\n", CT2A(user_version)); | |
175 } | |
176 } | |
177 | |
178 return is_omaha_installed; | |
179 } | |
180 | |
181 // Starts the http server on a different thread. | |
182 HRESULT CompatibilityTest::StartHttpServer() { | |
183 CORE_LOG(L1, (_T("[StartHttpServer]"))); | |
184 | |
185 reset(thread_, | |
186 ::CreateThread(NULL, 0, | |
187 &CompatibilityTest::StartStartHttpServerInternal, | |
188 this, 0, &thread_id_)); | |
189 return S_OK; | |
190 } | |
191 | |
192 DWORD WINAPI CompatibilityTest::StartStartHttpServerInternal( | |
193 void* param) { | |
194 CompatibilityTest* omaha_compat = | |
195 static_cast<CompatibilityTest*>(param); | |
196 omaha_compat->RunHttpServerInternal(); | |
197 return 0; | |
198 } | |
199 | |
200 HRESULT CompatibilityTest::RunHttpServerInternal() { | |
201 CORE_LOG(L1, (_T("Starting local http server"))); | |
202 | |
203 scoped_co_init init_com_apt(COINIT_MULTITHREADED); | |
204 HRESULT hr = init_com_apt.hresult(); | |
205 if (FAILED(hr)) { | |
206 CORE_LOG(L1, (_T("[CoInitialize Failed.][0x%08x]"), hr)); | |
207 return hr; | |
208 } | |
209 | |
210 HttpServer server(kHost, kPort); | |
211 hr = server.Initialize(); | |
212 if (FAILED(hr)) { | |
213 CORE_LOG(L1, (_T("[HttpServer Initialize failed.][0x%08x]"), hr)); | |
214 return hr; | |
215 } | |
216 | |
217 scoped_ptr<UpdateCheckHandler> update_handler( | |
218 new UpdateCheckHandler(kUpdateCheckUrlPath, console_writer_.get())); | |
219 hr = server.AddUrlHandler(update_handler.get()); | |
220 if (FAILED(hr)) { | |
221 CORE_LOG(L1, (_T("[Failed to add update handler.][0x%08x]"), hr)); | |
222 return hr; | |
223 } | |
224 | |
225 scoped_ptr<DownloadHandler> download_handler( | |
226 new DownloadHandler(kDownloadUrlPath)); | |
227 hr = server.AddUrlHandler(download_handler.get()); | |
228 if (FAILED(hr)) { | |
229 CORE_LOG(L1, (_T("[Failed to add download handler.][0x%08x]"), hr)); | |
230 return hr; | |
231 } | |
232 | |
233 for (size_t i = 0; i < config_responses_.size(); ++i) { | |
234 update_handler->AddAppVersionResponse(config_responses_[i]); | |
235 download_handler->AddDownloadFile(config_responses_[i]); | |
236 } | |
237 | |
238 update_handler.release(); | |
239 download_handler.release(); | |
240 server.Start(); | |
241 return S_OK; | |
242 } | |
243 | |
244 HRESULT CompatibilityTest::RestoreRegistry(void) { | |
245 CORE_LOG(L1, (_T("[RestoreRegistry]"))); | |
246 HRESULT hr = RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, | |
247 kRegValueMonitorLastChecked); | |
248 if (FAILED(hr)) { | |
249 return hr; | |
250 } | |
251 | |
252 hr = RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, | |
253 kRegValueNamePingUrl); | |
254 if (FAILED(hr)) { | |
255 return hr; | |
256 } | |
257 | |
258 hr = RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, | |
259 kRegValueNameUrl); | |
260 if (FAILED(hr)) { | |
261 return hr; | |
262 } | |
263 | |
264 hr = RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, | |
265 kRegValueAuCheckPeriodMs); | |
266 if (FAILED(hr)) { | |
267 return hr; | |
268 } | |
269 | |
270 hr = RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, | |
271 kRegValueNameOverInstall); | |
272 if (FAILED(hr)) { | |
273 return hr; | |
274 } | |
275 | |
276 return S_OK; | |
277 } | |
278 | |
279 HRESULT CompatibilityTest::SetupRegistry(bool needs_admin) { | |
280 CORE_LOG(L1, (_T("[SetupRegistry]"))); | |
281 | |
282 // Override the url, pingurl, AuCheckPeriod, overinstall. | |
283 // Create the lastchecked value monitor. | |
284 // Also create the last checked key, to allow a /ua launch | |
285 // on deletion. | |
286 HRESULT hr = RegKey::SetValue(MACHINE_REG_UPDATE_DEV, | |
287 kRegValueMonitorLastChecked, | |
288 static_cast<DWORD>(1)); | |
289 if (FAILED(hr)) { | |
290 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
291 return hr; | |
292 } | |
293 | |
294 hr = RegKey::SetValue(MACHINE_REG_UPDATE_DEV, | |
295 kRegValueNamePingUrl, | |
296 kUpdateCheckUrl); | |
297 if (FAILED(hr)) { | |
298 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
299 return hr; | |
300 } | |
301 | |
302 hr = RegKey::SetValue(MACHINE_REG_UPDATE_DEV, | |
303 kRegValueNameUrl, | |
304 kUpdateCheckUrl); | |
305 if (FAILED(hr)) { | |
306 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
307 return hr; | |
308 } | |
309 | |
310 hr = RegKey::SetValue(MACHINE_REG_UPDATE_DEV, | |
311 kRegValueAuCheckPeriodMs, | |
312 kAuCheckPeriodMs); | |
313 if (FAILED(hr)) { | |
314 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
315 return hr; | |
316 } | |
317 | |
318 hr = RegKey::SetValue(MACHINE_REG_UPDATE_DEV, | |
319 kRegValueNameOverInstall, | |
320 static_cast<DWORD>(1)); | |
321 if (FAILED(hr)) { | |
322 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
323 return hr; | |
324 } | |
325 | |
326 CString update_key_name = | |
327 ConfigManager::Instance()->registry_update(needs_admin); | |
328 hr = RegKey::SetValue(update_key_name, | |
329 kRegValueLastChecked, | |
330 static_cast<DWORD>(0)); | |
331 if (FAILED(hr)) { | |
332 CORE_LOG(L1, (_T("[SetValue failed.][0x%08x]"), hr)); | |
333 return hr; | |
334 } | |
335 | |
336 return S_OK; | |
337 } | |
338 | |
339 // Builds the googleupdate file name to be run. | |
340 HRESULT CompatibilityTest::BuildTaggedGoogleUpdatePath() { | |
341 CORE_LOG(L1, (_T("[BuildTaggedGoogleUpdatePath]"))); | |
342 | |
343 GUID guid(GUID_NULL); | |
344 HRESULT hr = ::CoCreateGuid(&guid); | |
345 if (FAILED(hr)) { | |
346 CORE_LOG(L1, (_T("[CoCreateGuid failed 0x%08x]"), hr)); | |
347 return hr; | |
348 } | |
349 | |
350 CString unique_exe(GuidToString(guid)); | |
351 unique_exe += _T(".exe"); | |
352 | |
353 TCHAR temp_path[MAX_PATH] = {0}; | |
354 if (!::GetTempPath(MAX_PATH, temp_path)) { | |
355 CORE_LOG(L1, (_T("[GetTempPath failed.]"))); | |
356 return HRESULTFromLastError(); | |
357 } | |
358 | |
359 tagged_google_update_path_ = ConcatenatePath(temp_path, unique_exe); | |
360 return S_OK; | |
361 } | |
362 | |
363 // Sets up the correct ap value to cause the server to send back | |
364 // an update response. | |
365 HRESULT CompatibilityTest::StartApplicationUpdate() { | |
366 CORE_LOG(L1, (_T("[StartApplicationUpdate]"))); | |
367 | |
368 // Set the ap value to "update_app". | |
369 bool needs_admin = config_responses_[0].needs_admin; | |
370 CString reg_key_name = | |
371 ConfigManager::Instance()->registry_client_state(needs_admin); | |
372 CString app_client_state_key_name = AppendRegKeyPath( | |
373 reg_key_name, | |
374 GuidToString(config_responses_[0].guid)); | |
375 HRESULT hr = RegKey::SetValue(app_client_state_key_name, | |
376 kRegValueAdditionalParams, | |
377 _T("update_app")); | |
378 if (FAILED(hr)) { | |
379 return hr; | |
380 } | |
381 | |
382 // Delete the last checked value to force an update check. | |
383 CString update_key_name = | |
384 ConfigManager::Instance()->registry_update(needs_admin); | |
385 hr = RegKey::DeleteValue(update_key_name, kRegValueLastChecked); | |
386 if (FAILED(hr)) { | |
387 return hr; | |
388 } | |
389 | |
390 printf("\nSignaled Googleupdate to start application update."); | |
391 printf("Waiting for events...\n"); | |
392 while (true) { | |
393 ::SleepEx(1000, false); | |
394 if (console_writer_->update_completed()) { | |
395 PingEvent::Results result = console_writer_->update_result(); | |
396 if (result == PingEvent::EVENT_RESULT_SUCCESS) { | |
397 CORE_LOG(L1, (_T("[Successfully completed update]"))); | |
398 return S_OK; | |
399 } else { | |
400 CORE_LOG(L1, (_T("[Update failed %d]"), result)); | |
401 return E_FAIL; | |
402 } | |
403 } | |
404 } | |
405 | |
406 return S_OK; | |
407 } | |
408 | |
409 // Stamps googleupdatesetup with the information from the config and | |
410 // runs it. | |
411 HRESULT CompatibilityTest::StartGoogleUpdate() { | |
412 CORE_LOG(L1, (_T("[StartGoogleUpdate]"))); | |
413 | |
414 // Stamp the googleupdatesetupe.exe with the information from | |
415 // the config. | |
416 HRESULT hr = BuildTaggedGoogleUpdatePath(); | |
417 if (FAILED(hr)) { | |
418 CORE_LOG(L1, (_T("[BuildTaggedGoogleUpdatePath failed 0x%08x]"), hr)); | |
419 return hr; | |
420 } | |
421 | |
422 // For now we only read the information from the first config and use | |
423 // that to stamp the binary. | |
424 CString tag_str; | |
425 tag_str.Format( | |
426 _T("appguid=%s&appname=%s&needsadmin=%s&lang=%s"), | |
427 GuidToString(config_responses_[0].guid), | |
428 config_responses_[0].app_name, | |
429 config_responses_[0].needs_admin ? _T("True") : _T("False"), | |
430 config_responses_[0].language); | |
431 | |
432 ApplyTag tag; | |
433 hr = tag.Init(googleupdate_setup_path_, | |
434 CT2CA(tag_str), | |
435 lstrlenA(CT2CA(tag_str)), | |
436 tagged_google_update_path_, | |
437 false); | |
438 if (FAILED(hr)) { | |
439 CORE_LOG(L1, (_T("[ApplyTag.Init failed 0x%08x]"), hr)); | |
440 return hr; | |
441 } | |
442 | |
443 hr = tag.EmbedTagString(); | |
444 if (FAILED(hr)) { | |
445 CORE_LOG(L1, (_T("[ApplyTag.EmbedTagString failed 0x%08x]"), hr)); | |
446 return hr; | |
447 } | |
448 | |
449 // Start omaha to install the application. | |
450 hr = System::ShellExecuteProcess(tagged_google_update_path_, | |
451 NULL, | |
452 NULL, | |
453 NULL); | |
454 if (FAILED(hr)) { | |
455 CORE_LOG(L1, (_T("[Start installer failed 0x%08x]"), hr)); | |
456 return hr; | |
457 } | |
458 | |
459 // Now wait for the install to complete. We poll the | |
460 // observer for a install complete event. | |
461 while (true) { | |
462 ::SleepEx(1000, false); | |
463 if (console_writer_->install_completed()) { | |
464 PingEvent::Results result = console_writer_->install_result(); | |
465 if (result == PingEvent::EVENT_RESULT_SUCCESS) { | |
466 CORE_LOG(L1, (_T("[Successfully completed install]"))); | |
467 return S_OK; | |
468 } else { | |
469 CORE_LOG(L1, (_T("[Start installer failed %d]"), result)); | |
470 return E_FAIL; | |
471 } | |
472 } | |
473 } | |
474 | |
475 return S_OK; | |
476 } | |
477 | |
478 } // namespace omaha | |
479 | |
OLD | NEW |