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

Side by Side Diff: goopdate/installer_wrapper.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 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
« no previous file with comments | « goopdate/installer_wrapper.h ('k') | goopdate/installer_wrapper_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2007-2010 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/goopdate/installer_wrapper.h"
17 #include "omaha/base/const_object_names.h"
18 #include "omaha/base/const_utils.h"
19 #include "omaha/base/debug.h"
20 #include "omaha/base/error.h"
21 #include "omaha/base/logging.h"
22 #include "omaha/base/path.h"
23 #include "omaha/base/process.h"
24 #include "omaha/base/safe_format.h"
25 #include "omaha/base/scope_guard.h"
26 #include "omaha/base/scoped_ptr_address.h"
27 #include "omaha/base/string.h"
28 #include "omaha/base/synchronized.h"
29 #include "omaha/base/system_info.h"
30 #include "omaha/base/utils.h"
31 #include "omaha/common/const_goopdate.h"
32 #include "omaha/common/goopdate_utils.h"
33 #include "omaha/goopdate/app_manager.h"
34 #include "omaha/goopdate/server_resource.h"
35 #include "omaha/goopdate/string_formatter.h"
36 #include "omaha/goopdate/worker_metrics.h"
37
38 namespace omaha {
39
40 namespace {
41
42 CString BuildMsiCommandLine(const CString& arguments,
43 const CString& msi_file_path,
44 const CString& enclosed_installer_data_file_path) {
45 CORE_LOG(L3, (_T("[CreateMsiCommandLine]")));
46
47 CString command_line;
48 // Suppressing reboots can lead to an inconsistent state until the user
49 // reboots, but automatically rebooting is unacceptable. The user will be
50 // informed by the string for ERROR_SUCCESS_REBOOT_REQUIRED that a reboot is
51 // necessary. See http://b/1184091 for details.
52
53 if (!enclosed_installer_data_file_path.IsEmpty()) {
54 SafeCStringFormat(&command_line, _T("INSTALLERDATA=%s "),
55 enclosed_installer_data_file_path);
56 }
57
58 SafeCStringAppendFormat(&command_line, _T("%s %s /qn /i \"%s\""),
59 arguments,
60 kMsiSuppressAllRebootsCmdLine,
61 msi_file_path);
62
63 // The msiexec version in XP SP2 (V 3.01) and higher supports the /log switch.
64 if (SystemInfo::OSWinXPSP2OrLater()) {
65 CString logfile(msi_file_path);
66 logfile.Append(_T(".log"));
67
68 SafeCStringAppendFormat(&command_line, _T(" /log \"%s\""), logfile);
69 }
70
71 CORE_LOG(L2, (_T("[msiexec command line][%s]"), command_line));
72 return command_line;
73 }
74
75 // Gets the installer exit code.
76 HRESULT GetInstallerExitCode(const Process& p, uint32* exit_code) {
77 ASSERT1(exit_code);
78
79 if (p.Running()) {
80 ASSERT(false,
81 (_T("GetInstallerExitCode called while the process is running.")));
82 return GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR;
83 }
84
85 if (!p.GetExitCode(exit_code)) {
86 ASSERT(false,
87 (_T("[Failed to get the installer exit code for some reason.]")));
88 return GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR;
89 }
90
91 CORE_LOG(L2, (_T("[Installer exit code][%u]"), *exit_code));
92
93 return S_OK;
94 }
95
96 // Gets the errors string for the specified system error.
97 // Assumes error_code represents a system error.
98 void GetSystemErrorString(uint32 error_code,
99 const CString& language,
100 CString* error_string) {
101 ASSERT1(error_string);
102 ASSERT1(ERROR_SUCCESS != error_code);
103
104 const CString error_code_string = FormatErrorCode(error_code);
105
106 StringFormatter formatter(language);
107
108 const CString error_message(GetMessageForSystemErrorCode(error_code));
109 if (!error_message.IsEmpty()) {
110 VERIFY1(SUCCEEDED(formatter.FormatMessage(error_string,
111 IDS_INSTALLER_FAILED_WITH_MESSAGE,
112 error_code_string,
113 error_message)));
114 } else {
115 VERIFY1(SUCCEEDED(formatter.FormatMessage(error_string,
116 IDS_INSTALLER_FAILED_NO_MESSAGE,
117 error_code_string)));
118 }
119
120 OPT_LOG(LEVEL_ERROR, (_T("[installer system error][%u][%s]"),
121 error_code, *error_string));
122 ASSERT1(!error_string->IsEmpty());
123 }
124
125 } // namespace
126
127 InstallerWrapper::InstallerWrapper(bool is_machine)
128 : is_machine_(is_machine),
129 num_tries_when_msi_busy_(1) {
130 CORE_LOG(L3, (_T("[InstallerWrapper::InstallerWrapper]")));
131 }
132
133 InstallerWrapper::~InstallerWrapper() {
134 CORE_LOG(L3, (_T("[InstallerWrapper::~InstallerWrapper]")));
135 }
136
137 HRESULT InstallerWrapper::Initialize() {
138 NamedObjectAttributes lock_attr;
139 // TODO(omaha3): We might want to move this lock to the InstallManager.
140 GetNamedObjectAttributes(kInstallManagerSerializer, is_machine_, &lock_attr);
141 if (!installer_lock_.InitializeWithSecAttr(lock_attr.name, &lock_attr.sa)) {
142 OPT_LOG(LEVEL_ERROR, (_T("[Could not init Install Manager lock]")));
143 return GOOPDATEINSTALL_E_FAILED_INIT_INSTALLER_LOCK;
144 }
145
146 return S_OK;
147 }
148
149 // result_* will be populated if the installer ran and exited, regardless of the
150 // return value.
151 // Assumes the call is protected by some mechanism providing exclusive access
152 // to the app's registry keys in Clients and ClientState.
153 HRESULT InstallerWrapper::InstallApp(HANDLE user_token,
154 const GUID& app_guid,
155 const CString& installer_path,
156 const CString& arguments,
157 const CString& installer_data,
158 const CString& language,
159 InstallerResultInfo* result_info) {
160 ASSERT1(result_info);
161
162 HRESULT hr = DoInstallApp(user_token,
163 app_guid,
164 installer_path,
165 arguments,
166 installer_data,
167 language,
168 result_info);
169
170 ASSERT1((SUCCEEDED(hr) && result_info->type == INSTALLER_RESULT_SUCCESS) ||
171 (GOOPDATEINSTALL_E_INSTALLER_FAILED == hr &&
172 (result_info->type == INSTALLER_RESULT_ERROR_MSI ||
173 result_info->type == INSTALLER_RESULT_ERROR_SYSTEM ||
174 result_info->type == INSTALLER_RESULT_ERROR_OTHER)) ||
175 (GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING == hr &&
176 (result_info->type == INSTALLER_RESULT_ERROR_MSI ||
177 result_info->type == INSTALLER_RESULT_ERROR_SYSTEM)) ||
178 (FAILED(hr) && result_info->type == INSTALLER_RESULT_UNKNOWN));
179 ASSERT1(!result_info->text.IsEmpty() ==
180 (GOOPDATEINSTALL_E_INSTALLER_FAILED == hr ||
181 GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING == hr) ||
182 SUCCEEDED(hr)); // Successes may or may not have messages.
183
184 CORE_LOG(L3, (_T("[InstallApp result][0x%x][%s][type: %d][code: %d][%s][%s]"),
185 hr, GuidToString(app_guid), result_info->type,
186 result_info->code, result_info->text,
187 result_info->post_install_launch_command_line));
188 return hr;
189 }
190
191 CString InstallerWrapper::GetMessageForError(HRESULT error_code,
192 const CString& installer_filename,
193 const CString& language) {
194 CString message;
195 StringFormatter formatter(language);
196
197 switch (error_code) {
198 case GOOPDATEINSTALL_E_FILENAME_INVALID:
199 VERIFY1(SUCCEEDED(formatter.FormatMessage(&message,
200 IDS_INVALID_INSTALLER_FILENAME,
201 installer_filename)));
202 break;
203 case GOOPDATEINSTALL_E_INSTALLER_FAILED_START:
204 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALLER_FAILED_TO_START,
205 &message)));
206 break;
207 case GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT:
208 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALLER_TIMED_OUT,
209 &message)));
210 break;
211 case GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY:
212 case GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION:
213 case GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH:
214 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
215 break;
216 case GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING:
217 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_MSI_INSTALL_ALREADY_RUNNING,
218 &message)));
219 break;
220 case GOOPDATEINSTALL_E_INSTALLER_FAILED:
221 ASSERT(false,
222 (_T("[GetOmahaErrorTextToReport]")
223 _T("GOOPDATEINSTALL_E_INSTALLER_FAILED should never be reported ")
224 _T("directly. The installer error string should be reported.")));
225 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
226 break;
227 case GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR:
228 default:
229 ASSERT(false, (_T("[GetOmahaErrorTextToReport]")
230 _T("[An Omaha error occurred that this method does not ")
231 _T("know how to report.][0x%08x]"), error_code));
232 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
233 break;
234 }
235
236 ASSERT1(!message.IsEmpty());
237 return message;
238 }
239
240 void InstallerWrapper::set_num_tries_when_msi_busy(
241 int num_tries_when_msi_busy) {
242 ASSERT1(num_tries_when_msi_busy >= 1);
243 num_tries_when_msi_busy_ = num_tries_when_msi_busy;
244 }
245
246 HRESULT InstallerWrapper::BuildCommandLineFromFilename(
247 const CString& file_path,
248 const CString& arguments,
249 const CString& installer_data,
250 CString* executable_path,
251 CString* command_line,
252 InstallerType* installer_type) {
253 CORE_LOG(L3, (_T("[BuildCommandLineFromFilename]")));
254
255 ASSERT1(executable_path);
256 ASSERT1(command_line);
257 ASSERT1(installer_type);
258
259 *executable_path = _T("");
260 *command_line = _T("");
261 *installer_type = UNKNOWN_INSTALLER;
262
263 // The app's installer owns the lifetime of installer data file if it has been
264 // created, so Omaha does not delete it.
265 CString enclosed_installer_data_file_path;
266
267 VERIFY1(SUCCEEDED(goopdate_utils::WriteInstallerDataToTempFile(
268 installer_data,
269 &enclosed_installer_data_file_path)));
270 if (!enclosed_installer_data_file_path.IsEmpty()) {
271 EnclosePath(&enclosed_installer_data_file_path);
272 }
273
274 // PathFindExtension returns the address of the trailing NUL character if an
275 // extension is not found. It does not return NULL.
276 const TCHAR* ext = ::PathFindExtension(file_path);
277 ASSERT1(ext);
278 if (*ext != _T('\0')) {
279 ext++; // Skip the period.
280 if (0 == lstrcmpi(ext, _T("exe"))) {
281 *executable_path = file_path;
282 if (enclosed_installer_data_file_path.IsEmpty()) {
283 *command_line = arguments;
284 } else {
285 SafeCStringFormat(command_line, _T("%s /installerdata=%s"),
286 arguments,
287 enclosed_installer_data_file_path);
288 }
289 *installer_type = CUSTOM_INSTALLER;
290
291 CORE_LOG(L2, (_T("[BuildCommandLineFromFilename][exe][%s][%s]"),
292 *executable_path, *command_line));
293 } else if (0 == lstrcmpi(ext, _T("msi"))) {
294 *executable_path = _T("msiexec");
295 *command_line = BuildMsiCommandLine(arguments,
296 file_path,
297 enclosed_installer_data_file_path);
298 *installer_type = MSI_INSTALLER;
299
300 CORE_LOG(L2, (_T("[BuildCommandLineFromFilename][msi][%s]"),
301 *command_line));
302 } else {
303 *executable_path = _T("");
304 *command_line = _T("");
305 *installer_type = UNKNOWN_INSTALLER;
306
307 OPT_LOG(LE, (_T("[Unsupported extension '%s' in %s]"), ext, file_path));
308 return GOOPDATEINSTALL_E_FILENAME_INVALID;
309 }
310 } else {
311 OPT_LOG(LE, (_T("[No extension found in %s]"), file_path));
312 return GOOPDATEINSTALL_E_FILENAME_INVALID;
313 }
314
315 return S_OK;
316 }
317
318 // Calls DoExecuteAndWaitForInstaller to do the work. If an MSI installer
319 // returns, ERROR_INSTALL_ALREADY_RUNNING waits and retries several times or
320 // until the installation succeeds.
321 HRESULT InstallerWrapper::ExecuteAndWaitForInstaller(
322 HANDLE user_token,
323 const GUID& app_guid,
324 const CString& executable_path,
325 const CString& command_line,
326 InstallerType installer_type,
327 const CString& language,
328 InstallerResultInfo* result_info) {
329 CORE_LOG(L3, (_T("[InstallerWrapper::ExecuteAndWaitForInstaller]")));
330 ASSERT1(result_info);
331 ASSERT1(num_tries_when_msi_busy_ >= 1);
332
333 ++metric_worker_install_execute_total;
334 if (MSI_INSTALLER == installer_type) {
335 ++metric_worker_install_execute_msi_total;
336 }
337
338 // Run the installer, retrying if necessary.
339 int retry_delay = kMsiAlreadyRunningRetryDelayBaseMs;
340 int num_tries(0);
341 HRESULT hr = GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING;
342 for (num_tries = 0;
343 hr == GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING &&
344 num_tries < num_tries_when_msi_busy_;
345 ++num_tries) {
346 // Reset the result info - it contains the previous error when retrying.
347 *result_info = InstallerResultInfo();
348
349 if (0 < num_tries) {
350 // Retrying - wait between attempts.
351 CORE_LOG(L1, (_T("[Retrying][%d]"), num_tries));
352 ::Sleep(retry_delay);
353 retry_delay *= 2; // Double the retry delay next time.
354 }
355
356 hr = DoExecuteAndWaitForInstaller(user_token,
357 app_guid,
358 executable_path,
359 command_line,
360 installer_type,
361 language,
362 result_info);
363 if (FAILED(hr)) {
364 CORE_LOG(LE, (_T("[DoExecuteAndWaitForInstaller failed][0x%08x]"), hr));
365 return hr;
366 }
367 CORE_LOG(L1, (_T("[Installer result][%d][%d][%s]"),
368 result_info->type, result_info->code, result_info->text));
369 ASSERT1(result_info->type != INSTALLER_RESULT_UNKNOWN);
370
371 if ((INSTALLER_RESULT_ERROR_MSI == result_info->type ||
372 INSTALLER_RESULT_ERROR_SYSTEM == result_info->type) &&
373 ERROR_INSTALL_ALREADY_RUNNING == result_info->code) {
374 hr = GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING;
375 }
376 }
377
378 if (1 < num_tries) {
379 // Record metrics about the ERROR_INSTALL_ALREADY_RUNNING retries.
380 // TODO(omaha3): If we're willing to have a single metric for installs and
381 // updates, we can avoid knowing is_update.
382 #if 0
383 if (!app_version_->is_update()) {
384 #endif
385 ++metric_worker_install_msi_in_progress_detected_install;
386 if (result_info->type == INSTALLER_RESULT_SUCCESS) {
387 ++metric_worker_install_msi_in_progress_retry_succeeded_install;
388 metric_worker_install_msi_in_progress_retry_succeeded_tries_install
389 = num_tries;
390 }
391 #if 0
392 } else {
393 ++metric_worker_install_msi_in_progress_detected_update;
394 if (result_info->type == INSTALLER_RESULT_SUCCESS) {
395 ++metric_worker_install_msi_in_progress_retry_succeeded_update;
396 metric_worker_install_msi_in_progress_retry_succeeded_tries_update
397 = num_tries;
398 }
399 }
400 #endif
401 }
402
403 return hr;
404 }
405
406 HRESULT InstallerWrapper::DoExecuteAndWaitForInstaller(
407 HANDLE user_token,
408 const GUID& app_guid,
409 const CString& executable_path,
410 const CString& command_line,
411 InstallerType installer_type,
412 const CString& language,
413 InstallerResultInfo* result_info) {
414 OPT_LOG(L1, (_T("[Running installer][%s][%s][%s]"),
415 executable_path, command_line, GuidToString(app_guid)));
416 ASSERT1(result_info);
417
418 AppManager::Instance()->ClearInstallerResultApiValues(app_guid);
419
420 Process p(executable_path, NULL);
421 HRESULT hr = p.Start(command_line, user_token);
422 if (FAILED(hr)) {
423 OPT_LOG(LE, (_T("[p.Start fail][hr][%s][%s]"),
424 hr, executable_path, command_line));
425 set_error_extra_code1(static_cast<int>(hr));
426 return GOOPDATEINSTALL_E_INSTALLER_FAILED_START;
427 }
428
429 // TODO(omaha): InstallerWrapper should not special case Omaha. It is better
430 // to have an abstraction such as waiting or not for the installer to
431 // exit and let the App state machine special case Omaha. It's too low level
432 // to make a decision like this in the InstallerWrapper. Same for all
433 // kinds of tests on the call stack above this call that the app_guid is
434 // Omaha's guid.
435 if (::IsEqualGUID(app_guid, kGoopdateGuid)) {
436 // Do not wait for the installer when installing Omaha.
437 result_info->type = INSTALLER_RESULT_SUCCESS;
438 return S_OK;
439 }
440
441 if (!p.WaitUntilDead(kInstallerCompleteIntervalMs)) {
442 OPT_LOG(LEVEL_WARNING, (_T("[Installer has timed out]")
443 _T("[%s][%s]"), executable_path, command_line));
444 return GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT;
445 }
446
447 hr = GetInstallerResult(app_guid,
448 installer_type,
449 p,
450 language,
451 result_info);
452 if (FAILED(hr)) {
453 CORE_LOG(LEVEL_ERROR, (_T("[GetInstallerResult failed][0x%08x]"), hr));
454 return hr;
455 }
456
457 if (result_info->type != INSTALLER_RESULT_SUCCESS) {
458 OPT_LOG(LE, (_T("[Installer failed][%s][%s][%u]"),
459 executable_path, command_line, result_info->code));
460 }
461
462 return S_OK;
463 }
464
465 HRESULT InstallerWrapper::GetInstallerResult(const GUID& app_guid,
466 InstallerType installer_type,
467 const Process& p,
468 const CString& language,
469 InstallerResultInfo* result_info) {
470 CORE_LOG(L3, (_T("[InstallerWrapper::GetInstallerResult]")));
471 ASSERT1(result_info);
472
473 uint32 exit_code = 0;
474 HRESULT hr = GetInstallerExitCode(p, &exit_code);
475 if (FAILED(hr)) {
476 CORE_LOG(LEVEL_ERROR, (_T("[GetInstallerExitCode failed][0x%08x]"), hr));
477 return hr;
478 }
479
480 GetInstallerResultHelper(app_guid,
481 installer_type,
482 exit_code,
483 language,
484 result_info);
485 return S_OK;
486 }
487
488 // The default InstallerResult behavior can be overridden in the registry.
489 // By default, error_code is the exit code. For some InstallerResults, it
490 // can be overridden by InstallerError in the registry.
491 // The success string cannot be overridden.
492 void InstallerWrapper::GetInstallerResultHelper(
493 const GUID& app_guid,
494 InstallerType installer_type,
495 uint32 exit_code,
496 const CString& language,
497 InstallerResultInfo* result_info) {
498 ASSERT1(result_info);
499
500 AppManager::InstallerResult installer_result =
501 AppManager::INSTALLER_RESULT_DEFAULT;
502 InstallerResultInfo result;
503
504 result.code = exit_code;
505
506 AppManager& app_manager = *AppManager::Instance();
507 app_manager.ReadInstallerResultApiValues(
508 app_guid,
509 &installer_result,
510 &result.code,
511 &result.extra_code1,
512 &result.text,
513 &result.post_install_launch_command_line);
514 OPT_LOG(L1, (_T("[InstallerResult][%s][%u]"),
515 GuidToString(app_guid), installer_result));
516
517 switch (installer_result) {
518 case AppManager::INSTALLER_RESULT_SUCCESS:
519 result.type = INSTALLER_RESULT_SUCCESS;
520 // TODO(omaha3): Support custom success messages.
521 break;
522 case AppManager::INSTALLER_RESULT_FAILED_CUSTOM_ERROR:
523 result.type = INSTALLER_RESULT_ERROR_OTHER;
524 break;
525 case AppManager::INSTALLER_RESULT_FAILED_MSI_ERROR:
526 result.type = INSTALLER_RESULT_ERROR_MSI;
527 break;
528 case AppManager::INSTALLER_RESULT_FAILED_SYSTEM_ERROR:
529 result.type = INSTALLER_RESULT_ERROR_SYSTEM;
530 break;
531 case AppManager::INSTALLER_RESULT_EXIT_CODE:
532 ASSERT(result.code == exit_code, (_T("InstallerError overridden")));
533 if (0 == exit_code) {
534 result.type = INSTALLER_RESULT_SUCCESS;
535 result.code = 0;
536 } else {
537 switch (installer_type) {
538 case MSI_INSTALLER:
539 result.type = INSTALLER_RESULT_ERROR_MSI;
540 break;
541 case UNKNOWN_INSTALLER:
542 case CUSTOM_INSTALLER:
543 case MAX_INSTALLER:
544 default:
545 result.type = INSTALLER_RESULT_ERROR_OTHER;
546 break;
547 }
548 }
549 break;
550 case AppManager::INSTALLER_RESULT_MAX:
551 default:
552 ASSERT1(false);
553 break;
554 }
555
556 // Handle the reboot required case.
557 if ((INSTALLER_RESULT_ERROR_MSI == result.type ||
558 INSTALLER_RESULT_ERROR_SYSTEM == result.type) &&
559 (ERROR_SUCCESS_REBOOT_REQUIRED == result.code)) {
560 // Reboot takes precedence over other actions.
561 result.type = INSTALLER_RESULT_SUCCESS;
562 result.code = 0;
563 result.post_install_action = POST_INSTALL_ACTION_REBOOT;
564 } else if (!result.post_install_launch_command_line.IsEmpty()) {
565 result.post_install_action = POST_INSTALL_ACTION_LAUNCH_COMMAND;
566 }
567
568 // InstallerResultInfo status has been finalized. Make sure all errors
569 // have error strings.
570 switch (result.type) {
571 case INSTALLER_RESULT_SUCCESS:
572 break;
573
574 case INSTALLER_RESULT_ERROR_MSI:
575 case INSTALLER_RESULT_ERROR_SYSTEM:
576 GetSystemErrorString(result.code, language, &result.text);
577 break;
578
579 case INSTALLER_RESULT_ERROR_OTHER:
580 if (result.text.IsEmpty()) {
581 result.text.FormatMessage(
582 IDS_INSTALLER_FAILED_NO_MESSAGE,
583 FormatErrorCode(result.code));
584 }
585 break;
586
587 case INSTALLER_RESULT_UNKNOWN:
588 default:
589 ASSERT1(false);
590 }
591
592 // TODO(omaha3): Serialize InstallerResultInfo.
593 // OPT_LOG(L1, (_T("[%s]"), result_info->ToString()));
594 *result_info = result;
595 }
596
597 // TODO(omaha3): Consider moving this method out of this class, maybe into
598 // InstallManager.
599 HRESULT InstallerWrapper::CheckApplicationRegistration(
600 const GUID& app_guid,
601 const CString& registered_version,
602 const CString& expected_version,
603 const CString& previous_version,
604 bool is_update) const {
605 const CString app_guid_string = GuidToString(app_guid);
606 CORE_LOG(L2, (_T("[InstallerWrapper::CheckApplicationRegistration][%s]"),
607 app_guid_string));
608 ASSERT(!::IsEqualGUID(kGoopdateGuid, app_guid),
609 (_T("Probably do not want to call this method for Omaha")));
610
611 if (registered_version.IsEmpty()) {
612 return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY;
613 }
614
615 CORE_LOG(L2, (_T("[CheckApplicationRegistration]")
616 _T("[guid=%s][registered=%s][expected=%s][previous=%s]"),
617 app_guid_string, registered_version, expected_version,
618 previous_version));
619
620 if (!expected_version.IsEmpty() && registered_version != expected_version) {
621 OPT_LOG(LE, (_T("[Registered version does not match expected][%s][%s][%s]"),
622 app_guid_string, registered_version, expected_version));
623 // This is expected if a newer version is already installed. Do not fail
624 // here in that case. This only works for four-element version strings.
625 // If the version format is not recognized, VersionFromString() returns 0.
626
627 ULONGLONG registered_version_number = VersionFromString(registered_version);
628 ULONGLONG expected_version_number = VersionFromString(expected_version);
629
630 // Check that the version did not change, the registered version is newer,
631 // and neither VersionFromString() call failed.
632 if (is_update && registered_version != previous_version ||
633 registered_version_number < expected_version_number ||
634 !registered_version_number ||
635 !expected_version_number) {
636 return GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH;
637 }
638
639 CORE_LOG(L1, (_T("[Newer version already registered]")));
640 }
641
642 if (is_update && previous_version == registered_version) {
643 ASSERT1(!previous_version.IsEmpty());
644 ASSERT(expected_version.IsEmpty() ||
645 VersionFromString(expected_version) >
646 VersionFromString(previous_version) ||
647 VersionFromString(expected_version) == 0 ||
648 VersionFromString(previous_version) == 0,
649 (_T("expected_version should be > previous_version when ")
650 _T("is_update - possibly a bad update rule.")));
651
652 OPT_LOG(LE, (_T("[Installer did not change version][%s][%s]"),
653 app_guid_string, previous_version));
654 return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION;
655 }
656
657 return S_OK;
658 }
659
660 // Assumes installer_lock_ has been initialized.
661 HRESULT InstallerWrapper::DoInstallApp(HANDLE user_token,
662 const GUID& app_guid,
663 const CString& installer_path,
664 const CString& arguments,
665 const CString& installer_data,
666 const CString& language,
667 InstallerResultInfo* result_info) {
668 CORE_LOG(L1, (_T("[InstallerWrapper::DoInstallApp][%s][%s][%s]"),
669 GuidToString(app_guid), installer_path, arguments));
670 ASSERT1(result_info);
671
672 CString executable_path;
673 CString command_line;
674 InstallerType installer_type = UNKNOWN_INSTALLER;
675
676 // TODO(omaha): Remove when http://b/1443404 is addressed.
677 const TCHAR* const kChromeGuid = _T("{8A69D345-D564-463C-AFF1-A69D9E530F96}");
678 const TCHAR* const kChromePerMachineArg = _T("--system-level");
679 CString modified_arguments = arguments;
680 if (kChromeGuid == GuidToString(app_guid) && is_machine_) {
681 modified_arguments.AppendFormat(_T(" %s"), kChromePerMachineArg);
682 }
683
684 HRESULT hr = BuildCommandLineFromFilename(installer_path,
685 modified_arguments,
686 installer_data,
687 &executable_path,
688 &command_line,
689 &installer_type);
690
691 if (FAILED(hr)) {
692 CORE_LOG(LW, (_T("[BuildCommandLineFromFilename failed][0x%08x]"), hr));
693 ASSERT1(GOOPDATEINSTALL_E_FILENAME_INVALID == hr);
694 return hr;
695 }
696
697 // Acquire the global lock here. This will ensure that we are the only
698 // installer running of the multiple goopdates.
699 __mutexBlock(installer_lock_) {
700 hr = ExecuteAndWaitForInstaller(user_token,
701 app_guid,
702 executable_path,
703 command_line,
704 installer_type,
705 language,
706 result_info);
707 }
708
709 if (FAILED(hr)) {
710 CORE_LOG(LE, (_T("[ExecuteAndWaitForInstaller failed][0x%08x][%s]"),
711 hr, GuidToString(app_guid)));
712 return hr;
713 }
714
715 if (result_info->type != INSTALLER_RESULT_SUCCESS) {
716 CORE_LOG(LE, (_T("[Installer failed][%d]"), result_info->type));
717 return GOOPDATEINSTALL_E_INSTALLER_FAILED;
718 }
719
720 return S_OK;
721 }
722
723 } // namespace omaha
OLDNEW
« no previous file with comments | « goopdate/installer_wrapper.h ('k') | goopdate/installer_wrapper_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698