OLD | NEW |
| (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/client/install_self.h" | |
17 #include "omaha/client/install_self_internal.h" | |
18 #include "omaha/base/debug.h" | |
19 #include "omaha/base/error.h" | |
20 #include "omaha/base/logging.h" | |
21 #include "omaha/base/omaha_version.h" | |
22 #include "omaha/base/reg_key.h" | |
23 #include "omaha/base/string.h" | |
24 #include "omaha/base/utils.h" | |
25 #include "omaha/base/vistautil.h" | |
26 #include "omaha/base/xml_utils.h" | |
27 #include "omaha/client/client_utils.h" | |
28 #include "omaha/common/app_registry_utils.h" | |
29 #include "omaha/common/command_line.h" | |
30 #include "omaha/common/const_cmd_line.h" | |
31 #include "omaha/common/config_manager.h" | |
32 #include "omaha/common/const_goopdate.h" | |
33 #include "omaha/common/ping.h" | |
34 #include "omaha/setup/setup.h" | |
35 #include "omaha/setup/setup_metrics.h" | |
36 | |
37 namespace omaha { | |
38 | |
39 namespace install_self { | |
40 | |
41 namespace { | |
42 | |
43 // Returns whether elevation is required. | |
44 bool IsElevationRequired(bool is_machine) { | |
45 return is_machine && !vista_util::IsUserAdmin(); | |
46 } | |
47 | |
48 } // namespace | |
49 | |
50 namespace internal { | |
51 | |
52 HRESULT DoSelfUpdate(bool is_machine, int* extra_code1) { | |
53 ASSERT1(extra_code1); | |
54 | |
55 *extra_code1 = 0; | |
56 | |
57 HRESULT hr = DoInstallSelf(is_machine, true, false, false, extra_code1); | |
58 if (FAILED(hr)) { | |
59 PersistUpdateErrorInfo(is_machine, hr, *extra_code1, GetVersionString()); | |
60 return hr; | |
61 } | |
62 | |
63 return S_OK; | |
64 } | |
65 | |
66 // Does not need to update the UI during Omaha install. This should be quick | |
67 // with a simple throbbing UI. UI will transition when product install begins. | |
68 HRESULT DoInstallSelf(bool is_machine, | |
69 bool is_self_update, | |
70 bool is_eula_required, | |
71 bool set_keepalive, | |
72 int* extra_code1) { | |
73 ASSERT1(extra_code1); | |
74 ASSERT1(!is_self_update || !is_eula_required); | |
75 ASSERT1(!IsElevationRequired(is_machine)); | |
76 | |
77 *extra_code1 = 0; | |
78 | |
79 // TODO(omaha3): This needs to be in some type of lock(s) when | |
80 // !is_eula_required. See comments for SetEulaNotAccepted. | |
81 // TODO(omaha3): Integrate CL 11232530 - fix for bug 1866730 - from mainline. | |
82 // The Omaha 2 implementation of EULA [not] accepted was completely changed in | |
83 // this CL, which has not been integrated yet. | |
84 // TODO(omaha3): Code running in install-related processes before this point | |
85 // need to check for /eularequired before sending pings OR we need to move | |
86 // this before any pings can be sent. Even the latter is insufficient for | |
87 // the non-elevated machine install instance on Vista. | |
88 HRESULT hr = SetEulaRequiredState(is_machine, is_eula_required); | |
89 if (FAILED(hr)) { | |
90 return hr; | |
91 } | |
92 | |
93 // Checking system requirements here keeps requirements checks for other | |
94 // modules out of Setup. | |
95 // It is possible that an older metainstaller would fail the install for | |
96 // system requirements that are not required for the installed version when | |
97 // doing a handoff install. | |
98 hr = CheckSystemRequirements(); | |
99 if (FAILED(hr)) { | |
100 return hr; | |
101 } | |
102 | |
103 Setup setup(is_machine); | |
104 setup.set_is_self_update(is_self_update); | |
105 hr = setup.Install(set_keepalive); | |
106 *extra_code1 = setup.extra_code1(); | |
107 | |
108 if (FAILED(hr)) { | |
109 CORE_LOG(LE, (_T("[Setup::Install failed][0x%08x]"), hr)); | |
110 return hr; | |
111 } | |
112 | |
113 // All Omaha installs are "offline" because there is no update check. | |
114 const CString omaha_client_state_key_path = | |
115 ConfigManager::Instance()->registry_client_state_goopdate(is_machine); | |
116 app_registry_utils::PersistSuccessfulInstall(omaha_client_state_key_path, | |
117 is_self_update, | |
118 !is_self_update); // is_offline | |
119 | |
120 CORE_LOG(L1, (_T("[Setup successfully completed]"))); | |
121 | |
122 return S_OK; | |
123 } | |
124 | |
125 HRESULT CheckSystemRequirements() { | |
126 // Validate that key OS components are installed. | |
127 if (!HasXmlParser()) { | |
128 return GOOPDATE_E_RUNNING_INFERIOR_MSXML; | |
129 } | |
130 | |
131 return S_OK; | |
132 } | |
133 | |
134 bool HasXmlParser() { | |
135 CComPtr<IXMLDOMDocument> my_xmldoc; | |
136 HRESULT hr = CoCreateSafeDOMDocument(&my_xmldoc); | |
137 const bool ret = SUCCEEDED(hr); | |
138 CORE_LOG(L3, (_T("[HasXmlParser returned %d][0x%08x]"), ret, hr)); | |
139 return ret; | |
140 } | |
141 | |
142 // Failing to set the state fails installation because this would prevent | |
143 // updates or allow updates that should not be allowed. | |
144 HRESULT SetEulaRequiredState(bool is_machine, bool is_eula_required) { | |
145 ASSERT1(!IsElevationRequired(is_machine)); | |
146 | |
147 const bool eula_accepted = !is_eula_required; | |
148 HRESULT hr = eula_accepted ? SetEulaAccepted(is_machine) : | |
149 SetEulaNotAccepted(is_machine); | |
150 if (FAILED(hr)) { | |
151 CORE_LOG(LE, (_T("[set EULA accepted state failed][accepted=%d][0x%08x]"), | |
152 eula_accepted, hr)); | |
153 return hr; | |
154 } | |
155 | |
156 return S_OK; | |
157 } | |
158 | |
159 // Does not write the registry if Google Update is already installed as | |
160 // determined by the presence of 2 or more registered apps. In those cases, we | |
161 // assume the existing EULA state is correct and do not want to disable updates | |
162 // for an existing installation. | |
163 // Assumes it is called with appropriate synchronization protection such that it | |
164 // can reliably check the number of registered clients. | |
165 // TODO(omaha3): How do we assure the above assumption? | |
166 HRESULT SetEulaNotAccepted(bool is_machine) { | |
167 CORE_LOG(L4, (_T("[SetEulaNotAccepted][%d]"), is_machine)); | |
168 | |
169 size_t num_clients(0); | |
170 if (SUCCEEDED(app_registry_utils::GetNumClients(is_machine, &num_clients)) && | |
171 num_clients >= 2) { | |
172 CORE_LOG(L4, (_T(" [Apps registered. Not setting eulaaccepted=0.]"))); | |
173 return S_OK; | |
174 } | |
175 | |
176 const ConfigManager* cm = ConfigManager::Instance(); | |
177 return RegKey::SetValue(cm->registry_update(is_machine), | |
178 kRegValueOmahaEulaAccepted, | |
179 static_cast<DWORD>(0)); | |
180 } | |
181 | |
182 HRESULT SetInstallationId(const CString& omaha_client_state_key_path, | |
183 const GUID& iid) { | |
184 if (GUID_NULL != iid) { | |
185 return RegKey::SetValue(omaha_client_state_key_path, | |
186 kRegValueInstallationId, | |
187 GuidToString(iid)); | |
188 } | |
189 | |
190 return S_OK; | |
191 } | |
192 | |
193 // Persist experiment labels that are specific to Google Update itself during | |
194 // an initial install. These are specified in a tag using "omahaexperiments"; | |
195 // once it's on the machine, Google Update's experiment labels will be read | |
196 // and modified like any other app on the system. | |
197 HRESULT SetExperimentLabels(const CString& omaha_client_state_key_path, | |
198 const CString& experiment_labels) { | |
199 if (!experiment_labels.IsEmpty()) { | |
200 return RegKey::SetValue(omaha_client_state_key_path, | |
201 kRegValueExperimentLabels, | |
202 experiment_labels); | |
203 } | |
204 | |
205 return S_OK; | |
206 } | |
207 | |
208 void PersistUpdateErrorInfo(bool is_machine, | |
209 HRESULT error, | |
210 int extra_code1, | |
211 const CString& version) { | |
212 const TCHAR* update_key_name = | |
213 ConfigManager::Instance()->registry_update(is_machine); | |
214 VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name, | |
215 kRegValueSelfUpdateErrorCode, | |
216 static_cast<DWORD>(error)))); | |
217 VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name, | |
218 kRegValueSelfUpdateExtraCode1, | |
219 static_cast<DWORD>(extra_code1)))); | |
220 VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name, | |
221 kRegValueSelfUpdateVersion, | |
222 version))); | |
223 } | |
224 | |
225 } // namespace internal | |
226 | |
227 // Returns false if the values cannot be deleted to avoid skewing the log data | |
228 // with a single user pinging repeatedly with the same data. | |
229 bool ReadAndClearUpdateErrorInfo(bool is_machine, | |
230 DWORD* error_code, | |
231 DWORD* extra_code1, | |
232 CString* version) { | |
233 ASSERT1(error_code); | |
234 ASSERT1(extra_code1); | |
235 ASSERT1(version); | |
236 | |
237 const TCHAR* update_key_name = | |
238 ConfigManager::Instance()->registry_update(is_machine); | |
239 RegKey update_key; | |
240 HRESULT hr = update_key.Open(update_key_name); | |
241 if (FAILED(hr)) { | |
242 ASSERT1(false); | |
243 return false; | |
244 } | |
245 | |
246 if (!update_key.HasValue(kRegValueSelfUpdateErrorCode)) { | |
247 ASSERT1(!update_key.HasValue(kRegValueSelfUpdateExtraCode1)); | |
248 return false; | |
249 } | |
250 | |
251 VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateErrorCode, | |
252 error_code))); | |
253 ASSERT1(FAILED(*error_code)); | |
254 | |
255 VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateExtraCode1, | |
256 extra_code1))); | |
257 | |
258 VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateVersion, version))); | |
259 | |
260 if (FAILED(update_key.DeleteValue(kRegValueSelfUpdateErrorCode)) || | |
261 FAILED(update_key.DeleteValue(kRegValueSelfUpdateExtraCode1)) || | |
262 FAILED(update_key.DeleteValue(kRegValueSelfUpdateVersion))) { | |
263 ASSERT1(false); | |
264 return false; | |
265 } | |
266 | |
267 return true; | |
268 } | |
269 | |
270 HRESULT SetEulaAccepted(bool is_machine) { | |
271 CORE_LOG(L4, (_T("[SetEulaAccepted][%d]"), is_machine)); | |
272 const TCHAR* update_key_name = | |
273 ConfigManager::Instance()->registry_update(is_machine); | |
274 return RegKey::HasKey(update_key_name) ? | |
275 RegKey::DeleteValue(update_key_name, kRegValueOmahaEulaAccepted) : | |
276 S_OK; | |
277 } | |
278 | |
279 HRESULT InstallSelf(bool is_machine, | |
280 bool is_eula_required, | |
281 bool is_oem_install, | |
282 const CString& current_version, | |
283 const CString& install_source, | |
284 const CommandLineExtraArgs& extra_args, | |
285 const CString& session_id, | |
286 int* extra_code1) { | |
287 CORE_LOG(L2, (_T("[InstallSelf]"))); | |
288 | |
289 HRESULT hr = internal::DoInstallSelf(is_machine, | |
290 false, | |
291 is_eula_required, | |
292 extra_args.runtime_only, | |
293 extra_code1); | |
294 if (FAILED(hr)) { | |
295 CORE_LOG(LE, (_T("[DoInstallSelf failed][0x%08x]"), hr)); | |
296 return hr; | |
297 } | |
298 | |
299 // Set Omaha's optional IID, experiment labels, and branding. | |
300 // IID and experiment labels are always written on install; branding will | |
301 // only be written if no branding exists, which should only be true on the | |
302 // first install. | |
303 const CString omaha_client_state_key_path = | |
304 ConfigManager::Instance()->registry_client_state_goopdate(is_machine); | |
305 | |
306 // TODO(omaha): move SetInstallationId to app_registry_utils | |
307 VERIFY1(SUCCEEDED(internal::SetInstallationId(omaha_client_state_key_path, | |
308 extra_args.installation_id))); | |
309 VERIFY1(SUCCEEDED(internal::SetExperimentLabels( | |
310 omaha_client_state_key_path, | |
311 extra_args.experiment_labels))); | |
312 VERIFY1(SUCCEEDED(app_registry_utils::SetGoogleUpdateBranding( | |
313 omaha_client_state_key_path, | |
314 extra_args.brand_code, | |
315 extra_args.client_id))); | |
316 | |
317 if (is_eula_required || is_oem_install) { | |
318 return S_OK; | |
319 } | |
320 | |
321 // Send a successful EVENT_INSTALL_COMPLETE ping and do not wait for the | |
322 // completion of the ping. This reduces the overall latency of Omaha | |
323 // installs. The 'curent_version' parameter represent the version of | |
324 // Omaha before the setup has run. | |
325 Ping install_ping(is_machine, session_id, install_source); | |
326 PingEventPtr setup_install_complete_ping_event( | |
327 new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE, | |
328 PingEvent::EVENT_RESULT_SUCCESS, | |
329 hr, | |
330 *extra_code1)); | |
331 const CString next_version(GetVersionString()); | |
332 install_ping.LoadAppDataFromExtraArgs(extra_args); | |
333 install_ping.BuildOmahaPing(current_version, | |
334 next_version, | |
335 setup_install_complete_ping_event); | |
336 HRESULT send_result = install_ping.Send(true); | |
337 if (FAILED(send_result)) { | |
338 CORE_LOG(LW, (_T("[InstallPing::Send failed][0x%x]"), send_result)); | |
339 } | |
340 | |
341 return S_OK; | |
342 } | |
343 | |
344 HRESULT UpdateSelf(bool is_machine, const CString& session_id) { | |
345 CORE_LOG(L2, (_T("[UpdateSelf]"))); | |
346 | |
347 ++metric_setup_update_self_total; | |
348 | |
349 // 'current_version' corresponds to the value of 'pv' read from the registry. | |
350 CString current_version; | |
351 app_registry_utils::GetAppVersion(is_machine, | |
352 kGoogleUpdateAppId, | |
353 ¤t_version); | |
354 | |
355 int extra_code1 = 0; | |
356 const HRESULT hr = internal::DoSelfUpdate(is_machine, &extra_code1); | |
357 if (SUCCEEDED(hr)) { | |
358 ++metric_setup_update_self_succeeded; | |
359 } else { | |
360 CORE_LOG(LE, (_T("[DoSelfUpdate failed][0x%08x]"), hr)); | |
361 } | |
362 | |
363 // If a self-update failed because an uninstall of that Omaha is in progress, | |
364 // don't bother with an update failure ping; the uninstall ping will suffice. | |
365 if (hr == GOOPDATE_E_FAILED_TO_GET_LOCK_UNINSTALL_PROCESS_RUNNING) { | |
366 return hr; | |
367 } | |
368 | |
369 if (!ConfigManager::Instance()->CanUseNetwork(is_machine)) { | |
370 return hr; | |
371 } | |
372 | |
373 // Send an update complete ping and wait for send to complete. | |
374 PingEvent::Results result = SUCCEEDED(hr) ? | |
375 PingEvent::EVENT_RESULT_SUCCESS : | |
376 PingEvent::EVENT_RESULT_ERROR; | |
377 | |
378 const CString next_version(GetVersionString()); | |
379 | |
380 PingEventPtr update_complete_ping_event( | |
381 new PingEvent(PingEvent::EVENT_UPDATE_COMPLETE, result, hr, extra_code1)); | |
382 | |
383 Ping ping(is_machine, session_id, kCmdLineInstallSource_SelfUpdate); | |
384 ping.LoadOmahaDataFromRegistry(); | |
385 ping.BuildOmahaPing(current_version, | |
386 next_version, | |
387 update_complete_ping_event); | |
388 ping.Send(false); | |
389 | |
390 return hr; | |
391 } | |
392 | |
393 HRESULT Repair(bool is_machine) { | |
394 CORE_LOG(L2, (_T("[Repair]"))); | |
395 int extra_code1 = 0; | |
396 return internal::DoSelfUpdate(is_machine, &extra_code1); | |
397 } | |
398 | |
399 void CheckInstallStateConsistency(bool is_machine) { | |
400 Setup::CheckInstallStateConsistency(is_machine); | |
401 } | |
402 | |
403 HRESULT UninstallSelf(bool is_machine, bool send_uninstall_ping) { | |
404 CORE_LOG(L2, (_T("[UninstallSelf]"))); | |
405 Setup setup(is_machine); | |
406 return setup.Uninstall(send_uninstall_ping); | |
407 } | |
408 | |
409 } // namespace install_self | |
410 | |
411 } // namespace omaha | |
OLD | NEW |