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

Side by Side Diff: goopdate/app_command.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/app_command.h ('k') | goopdate/app_command_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 2011 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/app_command.h"
17
18 #include "base/scoped_ptr.h"
19 #include "omaha/base/constants.h"
20 #include "omaha/base/debug.h"
21 #include "omaha/base/error.h"
22 #include "omaha/base/exception_barrier.h"
23 #include "omaha/base/reg_key.h"
24 #include "omaha/base/scoped_ptr_address.h"
25 #include "omaha/base/system.h"
26 #include "omaha/base/utils.h"
27 #include "omaha/common/config_manager.h"
28 #include "omaha/common/const_cmd_line.h"
29 #include "omaha/common/const_goopdate.h"
30 #include "omaha/common/ping.h"
31 #include "omaha/common/ping_event.h"
32
33 namespace omaha {
34
35 namespace {
36
37 // Sends a single ping event to the Omaha server, synchronously.
38 void SendPing(const CString& app_guid,
39 bool is_machine,
40 const CString& session_id,
41 PingEvent::Types type,
42 PingEvent::Results result,
43 int error_code,
44 int extra_code) {
45 PingEventPtr ping_event(new PingEvent(type, result, error_code, extra_code));
46
47 Ping ping(is_machine, session_id, kCmdLineInstallSource_OneClick);
48 std::vector<CString> apps;
49 apps.push_back(app_guid);
50 ping.LoadAppDataFromRegistry(apps);
51 ping.BuildAppsPing(ping_event);
52 ping.Send(true); // true == is_fire_and_forget
53 }
54
55 // Waits on a process to exit, sends a ping based on the outcome.
56 // This is a COM object so that, during its lifetime, the process will not exit.
57 // The instance is AddRef'd in the instantiating thread and Release'd by the
58 // thread procedure when all work is completed.
59 class ATL_NO_VTABLE CompletePingSender
60 : public CComObjectRootEx<CComMultiThreadModel>,
61 public IUnknown {
62 public:
63 // Starts a wait on a process, belonging to the specified app and having the
64 // given reporting ID. Will send a ping when the process exits.
65 static void Start(const CString& app_guid,
66 bool is_machine,
67 const CString& session_id,
68 int reporting_id,
69 HANDLE process);
70
71 BEGIN_COM_MAP(CompletePingSender)
72 END_COM_MAP()
73
74 protected:
75 CompletePingSender();
76 virtual ~CompletePingSender();
77
78 private:
79 static HRESULT Create(const CString& app_guid,
80 bool is_machine,
81 const CString& session_id,
82 int reporting_id,
83 HANDLE process,
84 CompletePingSender** sender);
85
86 // Sends an EVENT_APP_COMMAND_COMPLETE ping with data from member
87 // variables and parameters.
88 void SendCompletePing(PingEvent::Results result, int error_code);
89
90 // Waits for the process to exit, returning S_OK and the exit_code or the
91 // underlying error code if the wait fails.
92 HRESULT WaitForProcessExit(DWORD* exit_code);
93
94 // Waits until the process exits or timeout occurs, then sends a ping with
95 // the result. parameter is the CompletePingSender instance.
96 static DWORD WINAPI WaitFunction(void* parameter);
97
98 CString app_guid_;
99 bool is_machine_;
100 CString session_id_;
101 int reporting_id_;
102 scoped_process process_;
103
104 DISALLOW_COPY_AND_ASSIGN(CompletePingSender);
105 }; // class CompletePingSender
106
107 CompletePingSender::CompletePingSender() {
108 }
109
110 HRESULT CompletePingSender::Create(const CString& app_guid,
111 bool is_machine,
112 const CString& session_id,
113 int reporting_id,
114 HANDLE process,
115 CompletePingSender** sender) {
116 ASSERT1(process && sender);
117
118 scoped_process process_handle(process);
119 process = NULL;
120
121 typedef CComObject<CompletePingSender> ComObjectCompletePingSender;
122
123 scoped_ptr<ComObjectCompletePingSender> new_object;
124 HRESULT hr = ComObjectCompletePingSender::CreateInstance(address(new_object));
125 if (FAILED(hr)) {
126 return hr;
127 }
128
129 new_object->app_guid_ = app_guid;
130 new_object->is_machine_ = is_machine;
131 new_object->session_id_ = session_id;
132 new_object->reporting_id_ = reporting_id;
133 reset(new_object->process_, release(process_handle));
134
135 new_object->AddRef();
136 *sender = new_object.release();
137 return S_OK;
138 }
139
140 CompletePingSender::~CompletePingSender() {
141 }
142
143 void CompletePingSender::Start(const CString& app_guid,
144 bool is_machine,
145 const CString& session_id,
146 int reporting_id,
147 HANDLE process) {
148 ASSERT1(process);
149
150 scoped_process process_handle(process);
151 process = NULL;
152
153 CComPtr<CompletePingSender> sender;
154 HRESULT hr = CompletePingSender::Create(app_guid,
155 is_machine,
156 session_id,
157 reporting_id,
158 release(process_handle),
159 &sender);
160 if (FAILED(hr)) {
161 CORE_LOG(LE, (_T("[failed to create CompletePingSender]"),
162 _T("[0x%08x]"), hr));
163 return;
164 }
165
166 void* context =
167 reinterpret_cast<void *>(static_cast<CompletePingSender*>(sender));
168
169 scoped_handle thread(::CreateThread(NULL, 0, WaitFunction, context, 0, NULL));
170
171 if (thread) {
172 // In case of success, the thread is responsible for calling Release.
173 sender.Detach();
174 } else {
175 hr = HRESULTFromLastError();
176 CORE_LOG(LE, (_T("[failed to start wait thread for app command ")
177 _T("process exit]") _T("[0x%08x]"), hr));
178 sender->SendCompletePing(PingEvent::EVENT_RESULT_ERROR, hr);
179 }
180 }
181
182 void CompletePingSender::SendCompletePing(PingEvent::Results result,
183 int error_code) {
184 SendPing(app_guid_,
185 is_machine_,
186 session_id_,
187 PingEvent::EVENT_APP_COMMAND_COMPLETE,
188 result,
189 error_code,
190 reporting_id_);
191 }
192
193 HRESULT CompletePingSender::WaitForProcessExit(DWORD* exit_code) {
194 ASSERT1(exit_code);
195 if (!exit_code) {
196 return E_INVALIDARG;
197 }
198
199 DWORD wait_result = ::WaitForSingleObject(get(process_), INFINITE);
200
201 if (wait_result == WAIT_TIMEOUT) {
202 return GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT;
203 } else if (wait_result == WAIT_FAILED) {
204 return HRESULTFromLastError();
205 }
206
207 ASSERT1(wait_result == WAIT_OBJECT_0);
208
209 if (wait_result != WAIT_OBJECT_0) {
210 return E_UNEXPECTED;
211 }
212
213 if (!::GetExitCodeProcess(get(process_), exit_code)) {
214 return HRESULTFromLastError();
215 }
216
217 return S_OK;
218 }
219
220 DWORD WINAPI CompletePingSender::WaitFunction(void* parameter) {
221 scoped_co_init init_com_apt(COINIT_MULTITHREADED);
222 HRESULT hr = init_com_apt.hresult();
223 if (FAILED(hr)) {
224 CORE_LOG(LE, (_T("[init_com_apt failed][0x%x]"), hr));
225 return 0;
226 }
227
228 CComPtr<CompletePingSender> instance(
229 reinterpret_cast<CompletePingSender*>(parameter));
230 DWORD exit_code = 0;
231 hr = instance->WaitForProcessExit(&exit_code);
232
233 PingEvent::Results result = PingEvent::EVENT_RESULT_SUCCESS;
234 int error_code = 0;
235
236 if (FAILED(hr)) {
237 result = PingEvent::EVENT_RESULT_ERROR;
238 error_code = hr;
239 } else {
240 switch (exit_code) {
241 case ERROR_SUCCESS_REBOOT_REQUIRED:
242 result = PingEvent::EVENT_RESULT_SUCCESS_REBOOT;
243 break;
244 case ERROR_SUCCESS:
245 result = PingEvent::EVENT_RESULT_SUCCESS;
246 break;
247 default:
248 result = PingEvent::EVENT_RESULT_INSTALLER_ERROR_OTHER;
249 error_code = exit_code;
250 break;
251 }
252 }
253
254 instance->SendCompletePing(result, error_code);
255
256 return 0;
257 }
258
259 // Attempts to read the command line from the given registry key and value.
260 // Logs a message in case of failure.
261 HRESULT ReadCommandLine(const CString& key_name,
262 const CString& value_name,
263 CString* command_line) {
264 HRESULT hr = RegKey::GetValue(key_name, value_name, command_line);
265 if (FAILED(hr)) {
266 CORE_LOG(LE, (_T("[failed to read command line]")
267 _T("[key %s][value %s][0x%08x]"), key_name, value_name, hr));
268 }
269
270 return hr;
271 }
272
273 // Checks if the specified value exists in the registry under the specified key.
274 // If so, attempts to read the value's DWORD contents into 'paramter'. Succeeds
275 // iff the value is absent or a DWORD value is successfully read.
276 HRESULT ReadCommandParameter(const CString& key_name,
277 const CString& value_name,
278 DWORD* parameter) {
279 if (!RegKey::HasValue(key_name, value_name)) {
280 return S_OK;
281 }
282
283 HRESULT hr = RegKey::GetValue(key_name, value_name, parameter);
284 if (FAILED(hr)) {
285 CORE_LOG(LE, (_T("[failed to read command parameter]")
286 _T("[key %s][value %s][0x%08x]"), key_name, value_name, hr));
287 }
288
289 return hr;
290 }
291
292 } // namespace
293
294 AppCommand::AppCommand(const CString& app_guid,
295 bool is_machine,
296 const CString& cmd_id,
297 const CString& cmd_line,
298 bool sends_pings,
299 const CString& session_id,
300 bool is_web_accessible,
301 DWORD reporting_id)
302 : app_guid_(app_guid),
303 is_machine_(is_machine),
304 cmd_id_(cmd_id),
305 session_id_(session_id),
306 cmd_line_(cmd_line),
307 sends_pings_(sends_pings),
308 reporting_id_(reporting_id),
309 is_web_accessible_(is_web_accessible) {
310 }
311
312 HRESULT AppCommand::Load(const CString& app_guid,
313 bool is_machine,
314 const CString& cmd_id,
315 const CString& session_id,
316 AppCommand** app_command) {
317 ASSERT1(app_command);
318
319 CString cmd_line;
320 DWORD sends_pings = 0;
321 DWORD is_web_accessible = 0;
322 DWORD reporting_id = 0;
323
324 ConfigManager* config_manager = ConfigManager::Instance();
325 CString clients_key_name = config_manager->registry_clients(is_machine);
326
327 CString app_key_name(AppendRegKeyPath(clients_key_name, app_guid));
328 CString command_key_name(
329 AppendRegKeyPath(app_key_name, kCommandsRegKeyName, cmd_id));
330
331 // Prefer the new layout, otherwise look for the legacy layout. See comments
332 // in app_command.h for description of each.
333 if (!RegKey::HasKey(command_key_name)) {
334 if (!RegKey::HasValue(app_key_name, cmd_id)) {
335 return GOOPDATE_E_CORE_MISSING_CMD;
336 }
337
338 // Legacy command layout.
339 HRESULT hr = ReadCommandLine(app_key_name, cmd_id, &cmd_line);
340 if (FAILED(hr)) {
341 return hr;
342 }
343 } else {
344 // New command layout.
345 HRESULT hr = ReadCommandLine(command_key_name, kRegValueCommandLine,
346 &cmd_line);
347 if (FAILED(hr)) {
348 return hr;
349 }
350
351 hr = ReadCommandParameter(command_key_name, kRegValueSendsPings,
352 &sends_pings);
353 if (FAILED(hr)) {
354 return hr;
355 }
356
357 hr = ReadCommandParameter(command_key_name, kRegValueWebAccessible,
358 &is_web_accessible);
359 if (FAILED(hr)) {
360 return hr;
361 }
362
363 hr = ReadCommandParameter(command_key_name, kRegValueReportingId,
364 &reporting_id);
365 if (FAILED(hr)) {
366 return hr;
367 }
368 }
369
370 *app_command = new AppCommand(app_guid,
371 is_machine,
372 cmd_id,
373 cmd_line,
374 sends_pings != 0,
375 session_id,
376 is_web_accessible != 0,
377 reporting_id);
378 return S_OK;
379 }
380
381 HRESULT AppCommand::Execute(HANDLE* process) const {
382 ASSERT1(process);
383 if (!process) {
384 return E_INVALIDARG;
385 }
386
387 *process = NULL;
388
389 CString cmd_line(cmd_line_);
390
391 PROCESS_INFORMATION pi = {0};
392 HRESULT hr = System::StartProcess(NULL, cmd_line.GetBuffer(), &pi);
393
394 if (sends_pings_) {
395 PingEvent::Results result = SUCCEEDED(hr) ?
396 PingEvent::EVENT_RESULT_SUCCESS : PingEvent::EVENT_RESULT_ERROR;
397
398 SendPing(app_guid_,
399 is_machine_,
400 session_id_,
401 PingEvent::EVENT_APP_COMMAND_BEGIN,
402 result,
403 hr,
404 reporting_id_);
405 }
406
407 if (FAILED(hr)) {
408 CORE_LOG(LE, (_T("[failed to launch cmd][%s][0x%08x]"), cmd_line_, hr));
409 return hr;
410 }
411
412 ASSERT1(pi.hProcess);
413 VERIFY1(::CloseHandle(pi.hThread));
414
415 *process = pi.hProcess;
416
417 if (sends_pings_) {
418 StartBackgroundThread(pi.hProcess);
419 }
420
421 return S_OK;
422 }
423
424 // Starts a background thread with a duplicate of the process handle.
425 // We need to duplicate the handle because the original handle will be returned
426 // to the client.
427 void AppCommand::StartBackgroundThread(HANDLE command_process) const {
428 HANDLE duplicate_process = NULL;
429
430 // This is a pseudo handle that need not be closed.
431 HANDLE this_process_handle = ::GetCurrentProcess();
432
433 if (::DuplicateHandle(this_process_handle, command_process,
434 this_process_handle, &duplicate_process,
435 NULL, false, DUPLICATE_SAME_ACCESS)) {
436 CompletePingSender::Start(app_guid_,
437 is_machine_,
438 session_id_,
439 reporting_id_,
440 duplicate_process);
441 } else {
442 CORE_LOG(LE, (_T("[failed call to DuplicateHandle][0x%08x]"),
443 HRESULTFromLastError()));
444 SendPing(app_guid_,
445 is_machine_,
446 session_id_,
447 PingEvent::EVENT_APP_COMMAND_COMPLETE,
448 PingEvent::EVENT_RESULT_ERROR,
449 HRESULTFromLastError(),
450 reporting_id_);
451 }
452 }
453
454 } // namespace omaha
OLDNEW
« no previous file with comments | « goopdate/app_command.h ('k') | goopdate/app_command_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698