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 #include "omaha/common/scheduled_task_utils.h" | |
16 #include "omaha/common/scheduled_task_utils_internal.h" | |
17 #include <corerror.h> | |
18 #include <lmcons.h> | |
19 #include <lmsname.h> | |
20 #include <mstask.h> | |
21 #include <atlsecurity.h> | |
22 #include "omaha/base/debug.h" | |
23 #include "omaha/base/error.h" | |
24 #include "omaha/base/logging.h" | |
25 #include "omaha/base/scoped_ptr_cotask.h" | |
26 #include "omaha/base/service_utils.h" | |
27 #include "omaha/base/string.h" | |
28 #include "omaha/base/system_info.h" | |
29 #include "omaha/base/time.h" | |
30 #include "omaha/base/timer.h" | |
31 #include "omaha/base/user_info.h" | |
32 #include "omaha/base/utils.h" | |
33 #include "omaha/client/resource.h" | |
34 #include "omaha/common/command_line_builder.h" | |
35 #include "omaha/common/config_manager.h" | |
36 #include "omaha/common/const_cmd_line.h" | |
37 #include "omaha/common/const_goopdate.h" | |
38 #include "omaha/common/goopdate_utils.h" | |
39 | |
40 namespace omaha { | |
41 | |
42 namespace scheduled_task_utils { | |
43 | |
44 namespace internal { | |
45 | |
46 CString GetCurrentTaskNameCore(bool is_machine) { | |
47 UTIL_LOG(L3, (_T("[GetCurrentTaskNameCore][%d]"), is_machine)); | |
48 | |
49 CString default_name(GetDefaultGoopdateTaskName(is_machine, | |
50 COMMANDLINE_MODE_CORE)); | |
51 return goopdate_utils::GetCurrentVersionedName(is_machine, | |
52 kRegValueTaskNameC, | |
53 default_name); | |
54 } | |
55 | |
56 HRESULT CreateAndSetVersionedTaskNameCoreInRegistry( | |
57 bool is_machine) { | |
58 UTIL_LOG(L3, (_T("[CreateAndSetVersionedTaskNameCoreInRegistry][%d]"), | |
59 is_machine)); | |
60 | |
61 CString default_name(GetDefaultGoopdateTaskName(is_machine, | |
62 COMMANDLINE_MODE_CORE)); | |
63 return goopdate_utils::CreateAndSetVersionedNameInRegistry( | |
64 is_machine, | |
65 default_name, | |
66 kRegValueTaskNameC); | |
67 } | |
68 | |
69 CString GetCurrentTaskNameUA(bool is_machine) { | |
70 UTIL_LOG(L3, (_T("[GetCurrentTaskNameUA][%d]"), is_machine)); | |
71 | |
72 CString default_name(GetDefaultGoopdateTaskName(is_machine, | |
73 COMMANDLINE_MODE_UA)); | |
74 return goopdate_utils::GetCurrentVersionedName(is_machine, | |
75 kRegValueTaskNameUA, | |
76 default_name); | |
77 } | |
78 | |
79 HRESULT CreateAndSetVersionedTaskNameUAInRegistry(bool machine) { | |
80 UTIL_LOG(L3, (_T("[CreateAndSetVersionedTaskNameUAInRegistry][%d]"), | |
81 machine)); | |
82 | |
83 CString default_name(GetDefaultGoopdateTaskName(machine, | |
84 COMMANDLINE_MODE_UA)); | |
85 return goopdate_utils::CreateAndSetVersionedNameInRegistry( | |
86 machine, | |
87 default_name, | |
88 kRegValueTaskNameUA); | |
89 } | |
90 | |
91 bool IsInstalledScheduledTask(const TCHAR* task_name) { | |
92 ASSERT1(task_name && *task_name); | |
93 | |
94 CComPtr<ITaskScheduler> scheduler; | |
95 HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler, | |
96 NULL, | |
97 CLSCTX_INPROC_SERVER); | |
98 if (FAILED(hr)) { | |
99 UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr)); | |
100 return false; | |
101 } | |
102 | |
103 CComPtr<ITask> task; | |
104 hr = scheduler->Activate(task_name, | |
105 __uuidof(ITask), | |
106 reinterpret_cast<IUnknown**>(&task)); | |
107 | |
108 UTIL_LOG(L3, (_T("[IsInstalledScheduledTask returned][0x%x]"), hr)); | |
109 return COR_E_FILENOTFOUND == hr ? false : true; | |
110 } | |
111 | |
112 DWORD GetScheduledTaskPriority(const TCHAR* task_name) { | |
113 ASSERT1(task_name && *task_name); | |
114 | |
115 CComPtr<ITaskScheduler> scheduler; | |
116 HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler, | |
117 NULL, | |
118 CLSCTX_INPROC_SERVER); | |
119 if (FAILED(hr)) { | |
120 UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr)); | |
121 return 0; | |
122 } | |
123 | |
124 CComPtr<ITask> task; | |
125 hr = scheduler->Activate(task_name, | |
126 __uuidof(ITask), | |
127 reinterpret_cast<IUnknown**>(&task)); | |
128 | |
129 if (FAILED(hr)) { | |
130 UTIL_LOG(LE, (_T("[GetScheduledTaskPriority][Activate failed][0x%x]"), hr)); | |
131 return 0; | |
132 } | |
133 | |
134 DWORD priority = 0; | |
135 hr = task->GetPriority(&priority); | |
136 if (FAILED(hr)) { | |
137 UTIL_LOG(LE, (_T("[ITask.GetMostRecentRunTime failed][0x%x]"), hr)); | |
138 return 0; | |
139 } | |
140 | |
141 ASSERT1(priority); | |
142 return priority; | |
143 } | |
144 | |
145 bool HasScheduledTaskEverRun(const TCHAR* task_name) { | |
146 ASSERT1(task_name && *task_name); | |
147 | |
148 CComPtr<ITaskScheduler> scheduler; | |
149 HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler, | |
150 NULL, | |
151 CLSCTX_INPROC_SERVER); | |
152 if (FAILED(hr)) { | |
153 UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr)); | |
154 return false; | |
155 } | |
156 | |
157 CComPtr<ITask> task; | |
158 hr = scheduler->Activate(task_name, | |
159 __uuidof(ITask), | |
160 reinterpret_cast<IUnknown**>(&task)); | |
161 | |
162 if (FAILED(hr)) { | |
163 UTIL_LOG(LE, (_T("[HasScheduledTaskEverRun][Activate failed][0x%x]"), hr)); | |
164 return false; | |
165 } | |
166 | |
167 SYSTEMTIME recent_run_time = {0}; | |
168 hr = task->GetMostRecentRunTime(&recent_run_time); | |
169 if (FAILED(hr)) { | |
170 UTIL_LOG(LE, (_T("[ITask.GetMostRecentRunTime failed][0x%x]"), hr)); | |
171 return false; | |
172 } | |
173 | |
174 // hr == SCHED_S_TASK_HAS_NOT_RUN if the task has never run. | |
175 return hr == S_OK; | |
176 } | |
177 | |
178 HRESULT GetScheduledTaskStatus(const TCHAR* task_name) { | |
179 ASSERT1(task_name && *task_name); | |
180 | |
181 CComPtr<ITaskScheduler> scheduler; | |
182 HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler, | |
183 NULL, | |
184 CLSCTX_INPROC_SERVER); | |
185 if (FAILED(hr)) { | |
186 UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr)); | |
187 return hr; | |
188 } | |
189 | |
190 CComPtr<ITask> task; | |
191 hr = scheduler->Activate(task_name, | |
192 __uuidof(ITask), | |
193 reinterpret_cast<IUnknown**>(&task)); | |
194 | |
195 if (FAILED(hr)) { | |
196 UTIL_LOG(LE, (_T("[GetScheduledTaskStatus: Activate failed][0x%x]"), hr)); | |
197 return hr; | |
198 } | |
199 | |
200 HRESULT task_status(S_OK); | |
201 hr = task->GetStatus(&task_status); | |
202 if (FAILED(hr)) { | |
203 UTIL_LOG(LE, (_T("[ITask.GetStatus failed][0x%x]"), hr)); | |
204 return hr; | |
205 } | |
206 | |
207 return task_status; | |
208 } | |
209 | |
210 HRESULT GetScheduledTaskExitCode(const TCHAR* task_name) { | |
211 ASSERT1(task_name && *task_name); | |
212 | |
213 CComPtr<ITaskScheduler> scheduler; | |
214 HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler, | |
215 NULL, | |
216 CLSCTX_INPROC_SERVER); | |
217 if (FAILED(hr)) { | |
218 UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr)); | |
219 return hr; | |
220 } | |
221 | |
222 CComPtr<ITask> task; | |
223 hr = scheduler->Activate(task_name, | |
224 __uuidof(ITask), | |
225 reinterpret_cast<IUnknown**>(&task)); | |
226 | |
227 if (FAILED(hr)) { | |
228 UTIL_LOG(LE, (_T("[ITask.Activate failed][0x%x]"), hr)); | |
229 return hr; | |
230 } | |
231 | |
232 DWORD exit_code(0); | |
233 hr = task->GetExitCode(&exit_code); | |
234 if (FAILED(hr)) { | |
235 UTIL_LOG(LE, (_T("[ITask.GetExitCode failed][0x%x]"), hr)); | |
236 return hr; | |
237 } | |
238 | |
239 return hr == SCHED_S_TASK_HAS_NOT_RUN ? hr : exit_code; | |
240 } | |
241 | |
242 HRESULT StartScheduledTask(const TCHAR* task_name) { | |
243 ASSERT1(task_name && *task_name); | |
244 | |
245 if (v2::IsTaskScheduler2APIAvailable()) { | |
246 return v2::StartScheduledTask(task_name); | |
247 } | |
248 | |
249 if (GetScheduledTaskStatus(task_name) == SCHED_S_TASK_RUNNING) { | |
250 return S_OK; | |
251 } | |
252 | |
253 CComPtr<ITaskScheduler> scheduler; | |
254 HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler, | |
255 NULL, | |
256 CLSCTX_INPROC_SERVER); | |
257 if (FAILED(hr)) { | |
258 UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr)); | |
259 return hr; | |
260 } | |
261 | |
262 CComPtr<ITask> task; | |
263 hr = scheduler->Activate(task_name, | |
264 __uuidof(ITask), | |
265 reinterpret_cast<IUnknown**>(&task)); | |
266 | |
267 if (FAILED(hr)) { | |
268 UTIL_LOG(LE, (_T("[ITaskScheduler.Activate failed][0x%x]"), hr)); | |
269 return hr; | |
270 } | |
271 | |
272 hr = task->Run(); | |
273 if (FAILED(hr)) { | |
274 UTIL_LOG(LE, (_T("[ITask.Run failed][0x%x]"), hr)); | |
275 return hr; | |
276 } | |
277 | |
278 return hr; | |
279 } | |
280 | |
281 HRESULT StopScheduledTask(const TCHAR* task_name) { | |
282 ASSERT1(task_name && *task_name); | |
283 | |
284 if (v2::IsTaskScheduler2APIAvailable()) { | |
285 return v2::StopScheduledTask(task_name); | |
286 } | |
287 | |
288 if (GetScheduledTaskStatus(task_name) != SCHED_S_TASK_RUNNING) { | |
289 return S_OK; | |
290 } | |
291 | |
292 CComPtr<ITaskScheduler> scheduler; | |
293 HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler, | |
294 NULL, | |
295 CLSCTX_INPROC_SERVER); | |
296 if (FAILED(hr)) { | |
297 UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr)); | |
298 return hr; | |
299 } | |
300 | |
301 CComPtr<ITask> task; | |
302 hr = scheduler->Activate(task_name, | |
303 __uuidof(ITask), | |
304 reinterpret_cast<IUnknown**>(&task)); | |
305 | |
306 if (FAILED(hr)) { | |
307 UTIL_LOG(LE, (_T("[ITaskScheduler.Activate failed][0x%x]"), hr)); | |
308 return hr; | |
309 } | |
310 | |
311 hr = task->Terminate(); | |
312 if (FAILED(hr)) { | |
313 UTIL_LOG(LE, (_T("[ITask.Run failed][0x%x]"), hr)); | |
314 return hr; | |
315 } | |
316 | |
317 return hr; | |
318 } | |
319 | |
320 HRESULT CreateLogonTrigger(ITask* task) { | |
321 ASSERT1(task); | |
322 | |
323 CComPtr<ITaskTrigger> trigger; | |
324 WORD index = 0; | |
325 | |
326 // Create a trigger to run on every user logon. | |
327 HRESULT hr = task->CreateTrigger(&index, &trigger); | |
328 if (FAILED(hr)) { | |
329 UTIL_LOG(LE, (_T("[ITask.CreateTrigger failed][0x%x]"), hr)); | |
330 return hr; | |
331 } | |
332 | |
333 TASK_TRIGGER trigger_config = {0}; | |
334 trigger_config.cbTriggerSize = sizeof(trigger_config); | |
335 // These are required parameters. A past start date is good. | |
336 trigger_config.wBeginDay = 1; | |
337 trigger_config.wBeginMonth = 1; | |
338 trigger_config.wBeginYear = 1999; | |
339 | |
340 // Run on every user logon. | |
341 trigger_config.TriggerType = TASK_EVENT_TRIGGER_AT_LOGON; | |
342 | |
343 hr = trigger->SetTrigger(&trigger_config); | |
344 if (FAILED(hr)) { | |
345 UTIL_LOG(LE, (_T("[ITaskTrigger.SetTrigger failed][0x%x]"), hr)); | |
346 return hr; | |
347 } | |
348 | |
349 return S_OK; | |
350 } | |
351 | |
352 HRESULT CreatePeriodicTrigger(ITask* task, bool create_hourly_trigger) { | |
353 ASSERT1(task); | |
354 | |
355 CComPtr<ITaskTrigger> trigger; | |
356 WORD index = 0; | |
357 | |
358 // Create a trigger to run every day. | |
359 HRESULT hr = task->CreateTrigger(&index, &trigger); | |
360 if (FAILED(hr)) { | |
361 UTIL_LOG(LE, (_T("[ITask.CreateTrigger failed][0x%x]"), hr)); | |
362 return hr; | |
363 } | |
364 | |
365 // Start time set to 5 minutes from the current time. | |
366 time64 start_time = GetCurrent100NSTime() + (5 * kMinsTo100ns); | |
367 SYSTEMTIME sys_time = Time64ToSystemTime(start_time); | |
368 SYSTEMTIME locale_time = {0}; | |
369 hr = SystemTimeToTzSpecificLocalTime(NULL, &sys_time, &locale_time); | |
370 if (FAILED(hr)) { | |
371 UTIL_LOG(LE, (_T("[SystemTimeToTzSpecificLocalTime failed][0x%x]"), hr)); | |
372 return hr; | |
373 } | |
374 | |
375 TASK_TRIGGER trigger_config = {0}; | |
376 trigger_config.cbTriggerSize = sizeof(trigger_config); | |
377 trigger_config.wBeginYear = locale_time.wYear; | |
378 trigger_config.wBeginMonth = locale_time.wMonth; | |
379 trigger_config.wBeginDay = locale_time.wDay; | |
380 trigger_config.wStartHour = locale_time.wHour; | |
381 trigger_config.wStartMinute = locale_time.wMinute; | |
382 | |
383 trigger_config.TriggerType = TASK_TIME_TRIGGER_DAILY; | |
384 trigger_config.Type.Daily.DaysInterval = 1; // every 1 day | |
385 | |
386 if (create_hourly_trigger) { | |
387 // The task will be run daily at 24 hour intervals. And the task will be | |
388 // repeated every au_timer_interval_minutes within a single 24 hour | |
389 // interval. | |
390 const DWORD kTaskTrigger24HoursDuration = 24 * 60; | |
391 int au_timer_interval_minutes = | |
392 ConfigManager::Instance()->GetAutoUpdateTimerIntervalMs() / (60 * 1000); | |
393 ASSERT1(au_timer_interval_minutes > 0 && | |
394 au_timer_interval_minutes < kTaskTrigger24HoursDuration); | |
395 | |
396 trigger_config.MinutesDuration = kTaskTrigger24HoursDuration; | |
397 trigger_config.MinutesInterval = au_timer_interval_minutes; | |
398 } | |
399 | |
400 hr = trigger->SetTrigger(&trigger_config); | |
401 if (FAILED(hr)) { | |
402 UTIL_LOG(LE, (_T("[ITaskTrigger.SetTrigger failed][0x%x]"), hr)); | |
403 return hr; | |
404 } | |
405 | |
406 return S_OK; | |
407 } | |
408 | |
409 HRESULT CreateScheduledTask(ITask* task, | |
410 const TCHAR* task_path, | |
411 const TCHAR* task_parameters, | |
412 const TCHAR* task_comment, | |
413 bool is_machine, | |
414 bool create_logon_trigger, | |
415 bool create_daily_trigger, | |
416 bool create_hourly_trigger) { | |
417 ASSERT1(task); | |
418 ASSERT1(task_path && *task_path); | |
419 ASSERT1(task_parameters); | |
420 ASSERT1(task_comment && *task_comment); | |
421 ASSERT1(create_logon_trigger || create_daily_trigger); | |
422 ASSERT1(!create_logon_trigger || (create_logon_trigger && is_machine)); | |
423 ASSERT1(!create_hourly_trigger || | |
424 (create_hourly_trigger && create_daily_trigger)); | |
425 | |
426 UTIL_LOG(L3, (_T("[CreateScheduledTask][%s][%s][%d]"), | |
427 task_path, task_parameters, is_machine)); | |
428 | |
429 HRESULT hr = task->SetApplicationName(task_path); | |
430 if (FAILED(hr)) { | |
431 UTIL_LOG(LE, (_T("[ITask.SetApplicationName failed][0x%x]"), hr)); | |
432 return hr; | |
433 } | |
434 | |
435 hr = task->SetParameters(task_parameters); | |
436 if (FAILED(hr)) { | |
437 UTIL_LOG(LE, (_T("[ITask.SetParameters failed][0x%x]"), hr)); | |
438 return hr; | |
439 } | |
440 | |
441 hr = task->SetComment(task_comment); | |
442 if (FAILED(hr)) { | |
443 UTIL_LOG(LE, (_T("[ITask.SetComment failed][0x%x]"), hr)); | |
444 return hr; | |
445 } | |
446 | |
447 if (is_machine) { | |
448 // Run using SYSTEM credentials, by passing in an empty username string. | |
449 hr = task->SetAccountInformation(_T(""), NULL); | |
450 } else { | |
451 // Run as current user. | |
452 // For the user task, we set TASK_FLAG_RUN_ONLY_IF_LOGGED_ON, so that we do | |
453 // not need the user password for task creation. | |
454 hr = task->SetFlags(TASK_FLAG_RUN_ONLY_IF_LOGGED_ON); | |
455 if (FAILED(hr)) { | |
456 UTIL_LOG(LE, (_T("[ITask.SetFlags failed][0x%x]"), hr)); | |
457 return hr; | |
458 } | |
459 | |
460 CString user_name; | |
461 DWORD buffer_size = UNLEN + 1; | |
462 if (!::GetUserName(CStrBuf(user_name, buffer_size), &buffer_size)) { | |
463 hr = HRESULTFromLastError(); | |
464 UTIL_LOG(LE, (_T("[::GetUserName failed][0x%x]"), hr)); | |
465 return hr; | |
466 } | |
467 hr = task->SetAccountInformation(user_name, NULL); | |
468 } | |
469 | |
470 if (FAILED(hr)) { | |
471 UTIL_LOG(LE, (_T("[ITask.SetAccountInformation failed][0x%x]"), hr)); | |
472 return hr; | |
473 } | |
474 | |
475 // The default is to run for a finite number of days. We want to run | |
476 // indefinitely. | |
477 // Due to a bug introduced in Vista, and propogated to Windows 7, setting the | |
478 // MaxRunTime to INFINITE results in the task only running for 72 hours. For | |
479 // these operating systems, setting the RunTime to "INFINITE - 1" gets the | |
480 // desired behavior of allowing an "infinite" run of the task. | |
481 DWORD max_time = INFINITE - (SystemInfo::IsRunningOnVistaOrLater() ? 1 : 0); | |
482 hr = task->SetMaxRunTime(max_time); | |
483 if (FAILED(hr)) { | |
484 UTIL_LOG(LE, (_T("[ITask.SetMaxRunTime failed][0x%x]"), hr)); | |
485 return hr; | |
486 } | |
487 | |
488 CComPtr<ITaskTrigger> trigger; | |
489 WORD index = 0; | |
490 | |
491 if (create_logon_trigger && is_machine) { | |
492 // Create a trigger to run on every user logon. Non-admin users are not able | |
493 // to create logon triggers, so we create only for machine. | |
494 hr = CreateLogonTrigger(task); | |
495 if (FAILED(hr)) { | |
496 return hr; | |
497 } | |
498 } | |
499 | |
500 if (create_daily_trigger) { | |
501 hr = CreatePeriodicTrigger(task, create_hourly_trigger); | |
502 if (FAILED(hr)) { | |
503 return hr; | |
504 } | |
505 } | |
506 | |
507 // Save task. | |
508 CComQIPtr<IPersistFile> persist(task); | |
509 if (!persist) { | |
510 hr = E_NOINTERFACE; | |
511 UTIL_LOG(LE, (_T("[ITask.QueryInterface IPersistFile failed][0x%x]"), hr)); | |
512 return hr; | |
513 } | |
514 | |
515 hr = persist->Save(NULL, TRUE); | |
516 if (FAILED(hr)) { | |
517 UTIL_LOG(LE, (_T("[IPersistFile.Save failed][0x%x]"), hr)); | |
518 return hr; | |
519 } | |
520 | |
521 if (is_machine) { | |
522 return S_OK; | |
523 } | |
524 | |
525 // Adjust privileges to explicitly allow the current user to be able to | |
526 // manipulate this task. User applications, and consequently, Omaha, can be | |
527 // installed in an elevated mode. This can happen, for instance, if the user | |
528 // installs on XP, then upgrades to Vista. Or chooses "Run as Administrator" | |
529 // when running the meta-installer on Vista. Subsequently, Omaha running at | |
530 // medium integrity needs to be able to manipulate the installed task. | |
531 scoped_ptr_cotask<OLECHAR> job_file; | |
532 hr = persist->GetCurFile(address(job_file)); | |
533 if (FAILED(hr)) { | |
534 UTIL_LOG(LE, (_T("[IPersistFile.GetCurFile failed][0x%x]"), hr)); | |
535 return hr; | |
536 } | |
537 | |
538 persist.Release(); | |
539 | |
540 CAccessToken token; | |
541 CSid current_sid; | |
542 if (!token.GetEffectiveToken(TOKEN_QUERY) || !token.GetUser(¤t_sid)) { | |
543 hr = HRESULTFromLastError(); | |
544 UTIL_LOG(LE, (_T("[Failed to get current user sid][0x%x]"), hr)); | |
545 return hr; | |
546 } | |
547 | |
548 hr = AddAllowedAce(job_file.get(), | |
549 SE_FILE_OBJECT, | |
550 current_sid, | |
551 FILE_ALL_ACCESS, | |
552 0); | |
553 if (FAILED(hr)) { | |
554 UTIL_LOG(LE, (_T("[Could not adjust DACL][%s][0x%x]"), job_file.get(), hr)); | |
555 return hr; | |
556 } | |
557 | |
558 return S_OK; | |
559 } | |
560 | |
561 HRESULT UpgradeScheduledTask(const TCHAR* task_name, | |
562 const TCHAR* task_path, | |
563 const TCHAR* task_parameters, | |
564 const TCHAR* task_comment, | |
565 bool is_machine, | |
566 bool create_logon_trigger, | |
567 bool create_daily_trigger, | |
568 bool create_hourly_trigger) { | |
569 ASSERT1(task_name && *task_name); | |
570 ASSERT1(IsInstalledScheduledTask(task_name)); | |
571 | |
572 UTIL_LOG(L3, (_T("[UpgradeScheduledTask][%s][%s][%s][%d]"), | |
573 task_name, task_path, task_parameters, is_machine)); | |
574 | |
575 // TODO(Omaha): Perhaps pass the ITaskScheduler around where possible. | |
576 CComPtr<ITaskScheduler> scheduler; | |
577 HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler, | |
578 NULL, | |
579 CLSCTX_INPROC_SERVER); | |
580 if (FAILED(hr)) { | |
581 UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr)); | |
582 return hr; | |
583 } | |
584 | |
585 CComPtr<ITask> task; | |
586 hr = scheduler->Activate(task_name, | |
587 __uuidof(ITask), | |
588 reinterpret_cast<IUnknown**>(&task)); | |
589 | |
590 if (FAILED(hr)) { | |
591 UTIL_LOG(LE, (_T("[UpgradeScheduledTask][Activate failed][0x%x]"), hr)); | |
592 return hr; | |
593 } | |
594 | |
595 // Delete existing triggers. CreateScheduledTask() will recreate them anew. | |
596 WORD trigger_count(0); | |
597 hr = task->GetTriggerCount(&trigger_count); | |
598 if (FAILED(hr)) { | |
599 UTIL_LOG(LE, (_T("[ITaskScheduler.GetTriggerCount failed][0x%x]"), hr)); | |
600 return hr; | |
601 } | |
602 | |
603 for (int i = 0; i < trigger_count; ++i) { | |
604 hr = task->DeleteTrigger(0); | |
605 if (FAILED(hr)) { | |
606 UTIL_LOG(LE, (_T("[ITaskScheduler.DeleteTrigger failed][0x%x]"), hr)); | |
607 return hr; | |
608 } | |
609 } | |
610 | |
611 return CreateScheduledTask(task, | |
612 task_path, | |
613 task_parameters, | |
614 task_comment, | |
615 is_machine, | |
616 create_logon_trigger, | |
617 create_daily_trigger, | |
618 create_hourly_trigger); | |
619 } | |
620 | |
621 // TODO(Omaha): Change the apis to avoid specifying hourly and daily triggers. | |
622 HRESULT InstallScheduledTask(const TCHAR* task_name, | |
623 const TCHAR* task_path, | |
624 const TCHAR* task_parameters, | |
625 const TCHAR* task_comment, | |
626 bool is_machine, | |
627 bool create_logon_trigger, | |
628 bool create_daily_trigger, | |
629 bool create_hourly_trigger) { | |
630 if (IsInstalledScheduledTask(task_name)) { | |
631 return UpgradeScheduledTask(task_name, | |
632 task_path, | |
633 task_parameters, | |
634 task_comment, | |
635 is_machine, | |
636 create_logon_trigger, | |
637 create_daily_trigger, | |
638 create_hourly_trigger); | |
639 } | |
640 | |
641 CComPtr<ITaskScheduler> scheduler; | |
642 HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler, | |
643 NULL, | |
644 CLSCTX_INPROC_SERVER); | |
645 if (FAILED(hr)) { | |
646 UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr)); | |
647 return hr; | |
648 } | |
649 | |
650 CComPtr<ITask> task; | |
651 hr = scheduler->NewWorkItem(task_name, | |
652 CLSID_CTask, | |
653 __uuidof(ITask), | |
654 reinterpret_cast<IUnknown**>(&task)); | |
655 | |
656 if (FAILED(hr)) { | |
657 UTIL_LOG(LE, (_T("[ITaskScheduler.NewWorkItem failed][0x%x]"), hr)); | |
658 return hr; | |
659 } | |
660 | |
661 return CreateScheduledTask(task, | |
662 task_path, | |
663 task_parameters, | |
664 task_comment, | |
665 is_machine, | |
666 create_logon_trigger, | |
667 create_daily_trigger, | |
668 create_hourly_trigger); | |
669 } | |
670 | |
671 HRESULT UninstallScheduledTask(const TCHAR* task_name) { | |
672 ASSERT1(task_name && *task_name); | |
673 | |
674 CComPtr<ITaskScheduler> scheduler; | |
675 HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler, | |
676 NULL, | |
677 CLSCTX_INPROC_SERVER); | |
678 if (FAILED(hr)) { | |
679 UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr)); | |
680 return hr; | |
681 } | |
682 | |
683 // Stop the task before deleting it. Ignore return value. | |
684 VERIFY1(SUCCEEDED(StopScheduledTask(task_name))); | |
685 | |
686 // delete the task. | |
687 hr = scheduler->Delete(task_name); | |
688 if (FAILED(hr) && COR_E_FILENOTFOUND != hr) { | |
689 UTIL_LOG(LE, (_T("[GetScheduledTaskStatus][Delete failed][0x%x]"), hr)); | |
690 return hr; | |
691 } | |
692 | |
693 return S_OK; | |
694 } | |
695 | |
696 HRESULT UninstallScheduledTasks(const TCHAR* task_prefix) { | |
697 ASSERT1(task_prefix && *task_prefix); | |
698 | |
699 CComPtr<ITaskScheduler> scheduler; | |
700 HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler, | |
701 NULL, | |
702 CLSCTX_INPROC_SERVER); | |
703 if (FAILED(hr)) { | |
704 UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr)); | |
705 return hr; | |
706 } | |
707 | |
708 CComPtr<IEnumWorkItems> enum_items; | |
709 hr = scheduler->Enum(&enum_items); | |
710 if (FAILED(hr)) { | |
711 UTIL_LOG(LE, (_T("[ITaskScheduler.Enum failed][0x%x]"), hr)); | |
712 return hr; | |
713 } | |
714 | |
715 TCHAR** task_names = NULL; | |
716 DWORD task_count = 0; | |
717 while (enum_items->Next(1, &task_names, &task_count) == S_OK) { | |
718 ASSERT1(task_count == 1); | |
719 scoped_co_task_ptr task_names_guard(task_names); | |
720 scoped_co_task_ptr task_name_guard(task_names[0]); | |
721 | |
722 if (String_StartsWith(task_names[0], task_prefix, true)) { | |
723 UninstallScheduledTask(task_names[0]); | |
724 } | |
725 } | |
726 | |
727 return S_OK; | |
728 } | |
729 | |
730 // Returns the task name Omaha used to install in Omaha 1.2.x. | |
731 CString GetOmaha1LegacyTaskName(bool is_machine) { | |
732 const TCHAR* const kLegacyOmaha1TaskNameMachine = _T("GoogleUpdateTask"); | |
733 const TCHAR* const kLegacyOmaha1TaskNameUser = _T("GoogleUpdateTaskUser"); | |
734 return is_machine ? kLegacyOmaha1TaskNameMachine : kLegacyOmaha1TaskNameUser; | |
735 } | |
736 | |
737 // Returns the task name Omaha used to install in Omaha 2 before the | |
738 // "GoogleUpdate.exe does not run all the time" refactoring. | |
739 CString GetOmaha2LegacyTaskName(bool is_machine) { | |
740 const TCHAR* kLegacyOmaha2TaskNameUserPrefix = _T("GoogleUpdateTaskUser"); | |
741 const TCHAR* kLegacyOmaha2TaskNameMachine = _T("GoogleUpdateTaskMachine"); | |
742 if (is_machine) { | |
743 return kLegacyOmaha2TaskNameMachine; | |
744 } | |
745 | |
746 CString task_name_user = kLegacyOmaha2TaskNameUserPrefix; | |
747 CString user_sid; | |
748 VERIFY1(SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid))); | |
749 task_name_user += user_sid; | |
750 return task_name_user; | |
751 } | |
752 | |
753 HRESULT WaitForTaskStatus(const TCHAR* task_name, HRESULT status, int time_ms) { | |
754 int kSleepBetweenRetriesMs = 100; | |
755 Timer timer(true); | |
756 while (timer.GetMilliseconds() < time_ms) { | |
757 if (GetScheduledTaskStatus(task_name) == status) { | |
758 return status; | |
759 } | |
760 ::Sleep(kSleepBetweenRetriesMs); | |
761 } | |
762 return GetScheduledTaskStatus(task_name); | |
763 } | |
764 | |
765 namespace v2 { | |
766 | |
767 bool IsTaskScheduler2APIAvailable() { | |
768 CComPtr<ITaskService> task_service; | |
769 return SUCCEEDED(task_service.CoCreateInstance(CLSID_TaskScheduler, | |
770 NULL, | |
771 CLSCTX_INPROC_SERVER)); | |
772 } | |
773 | |
774 HRESULT GetRegisteredTask(const TCHAR* task_name, IRegisteredTask** task) { | |
775 ASSERT1(IsTaskScheduler2APIAvailable()); | |
776 ASSERT1(task_name && *task_name); | |
777 ASSERT1(task); | |
778 | |
779 CComPtr<ITaskService> task_service; | |
780 HRESULT hr = task_service.CoCreateInstance(CLSID_TaskScheduler, | |
781 NULL, | |
782 CLSCTX_INPROC_SERVER); | |
783 if (FAILED(hr)) { | |
784 UTIL_LOG(LE, (_T("[ITaskService.CoCreateInstance failed][0x%x]"), hr)); | |
785 return hr; | |
786 } | |
787 | |
788 hr = task_service->Connect(CComVariant(), CComVariant(), | |
789 CComVariant(), CComVariant()); | |
790 if (FAILED(hr)) { | |
791 UTIL_LOG(LE, (_T("[ITaskService::Connect failed][0x%x]"), hr)); | |
792 return hr; | |
793 } | |
794 | |
795 CComPtr<ITaskFolder> task_folder; | |
796 hr = task_service->GetFolder(CComBSTR(_T("\\")) , &task_folder); | |
797 if (FAILED(hr)) { | |
798 UTIL_LOG(LE, (_T("[Cannot get Root Folder pointer][0x%x]"), hr)); | |
799 return hr; | |
800 } | |
801 | |
802 CComPtr<IRegisteredTask> registered_task; | |
803 hr = task_folder->GetTask(CComBSTR(task_name), ®istered_task); | |
804 if (FAILED(hr)) { | |
805 UTIL_LOG(LE, (_T("[Cannot get the registered task][0x%x]"), hr)); | |
806 return hr; | |
807 } | |
808 | |
809 *task = registered_task.Detach(); | |
810 return S_OK; | |
811 } | |
812 | |
813 bool IsScheduledTaskRunning(const TCHAR* task_name) { | |
814 ASSERT1(IsTaskScheduler2APIAvailable()); | |
815 ASSERT1(task_name && *task_name); | |
816 | |
817 CComPtr<IRegisteredTask> registered_task; | |
818 HRESULT hr = GetRegisteredTask(task_name, ®istered_task); | |
819 if (FAILED(hr)) { | |
820 return false; | |
821 } | |
822 | |
823 CComPtr<IRunningTaskCollection> running_task_collection; | |
824 hr = registered_task->GetInstances(0, &running_task_collection); | |
825 if (FAILED(hr)) { | |
826 UTIL_LOG(LE, (_T("[IRegisteredTask.GetInstances failed][0x%x]"), hr)); | |
827 return false; | |
828 } | |
829 | |
830 long count = 0; | |
831 hr = running_task_collection->get_Count(&count); | |
832 if (FAILED(hr)) { | |
833 UTIL_LOG(LE, (_T("[IRunningTaskCollection.get_Count failed][0x%x]"), hr)); | |
834 return false; | |
835 } | |
836 | |
837 return count > 0; | |
838 } | |
839 | |
840 HRESULT StartScheduledTask(const TCHAR* task_name) { | |
841 ASSERT1(IsTaskScheduler2APIAvailable()); | |
842 ASSERT1(task_name && *task_name); | |
843 | |
844 if (IsScheduledTaskRunning(task_name)) { | |
845 return S_OK; | |
846 } | |
847 | |
848 CComPtr<IRegisteredTask> registered_task; | |
849 HRESULT hr = GetRegisteredTask(task_name, ®istered_task); | |
850 if (FAILED(hr)) { | |
851 return hr; | |
852 } | |
853 | |
854 hr = registered_task->Run(CComVariant(), NULL); | |
855 if (FAILED(hr)) { | |
856 UTIL_LOG(LE, (_T("[IRegisteredTask.Run failed][0x%x]"), hr)); | |
857 return hr; | |
858 } | |
859 | |
860 return hr; | |
861 } | |
862 | |
863 HRESULT StopScheduledTask(const TCHAR* task_name) { | |
864 ASSERT1(IsTaskScheduler2APIAvailable()); | |
865 ASSERT1(task_name && *task_name); | |
866 | |
867 if (!IsScheduledTaskRunning(task_name)) { | |
868 return S_OK; | |
869 } | |
870 | |
871 CComPtr<IRegisteredTask> registered_task; | |
872 HRESULT hr = GetRegisteredTask(task_name, ®istered_task); | |
873 if (FAILED(hr)) { | |
874 return hr; | |
875 } | |
876 | |
877 CComPtr<IRunningTaskCollection> running_task_collection; | |
878 hr = registered_task->GetInstances(0, &running_task_collection); | |
879 if (FAILED(hr)) { | |
880 UTIL_LOG(LE, (_T("[IRegisteredTask.GetInstances failed][0x%x]"), hr)); | |
881 return hr; | |
882 } | |
883 | |
884 long count = 0; | |
885 hr = running_task_collection->get_Count(&count); | |
886 if (FAILED(hr)) { | |
887 UTIL_LOG(LE, (_T("[IRunningTaskCollection.get_Count failed][0x%x]"), hr)); | |
888 return hr; | |
889 } | |
890 | |
891 if (count <= 0) { | |
892 return S_OK; | |
893 } | |
894 | |
895 for (long i = 0; i < count; ++i) { | |
896 CComPtr<IRunningTask> running_task; | |
897 hr = running_task_collection->get_Item(CComVariant(i+1), &running_task); | |
898 if (FAILED(hr)) { | |
899 UTIL_LOG(LE, (_T("[IRunningTaskCollection.get_Item][%d][0x%x]"), i, hr)); | |
900 return hr; | |
901 } | |
902 | |
903 hr = running_task->Stop(); | |
904 if (FAILED(hr)) { | |
905 UTIL_LOG(LE, (_T("[IRunningTask.Stop failed][%d][0x%x]"), i, hr)); | |
906 return hr; | |
907 } | |
908 } | |
909 | |
910 return S_OK; | |
911 } | |
912 | |
913 } // namespace v2 | |
914 | |
915 } // namespace internal | |
916 | |
917 CString GetDefaultGoopdateTaskName(bool is_machine, CommandLineMode mode) { | |
918 ASSERT1(mode == COMMANDLINE_MODE_CORE || mode == COMMANDLINE_MODE_UA); | |
919 | |
920 CString task_name; | |
921 if (is_machine) { | |
922 task_name = kScheduledTaskNameMachinePrefix; | |
923 } else { | |
924 task_name = kScheduledTaskNameUserPrefix; | |
925 CString user_sid; | |
926 VERIFY1(SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid))); | |
927 task_name += user_sid; | |
928 } | |
929 | |
930 task_name += (mode == COMMANDLINE_MODE_CORE) ? kScheduledTaskNameCoreSuffix : | |
931 kScheduledTaskNameUASuffix; | |
932 return task_name; | |
933 } | |
934 | |
935 HRESULT InstallGoopdateTaskForMode(const TCHAR* task_path, | |
936 bool is_machine, | |
937 CommandLineMode mode) { | |
938 ASSERT1(mode == COMMANDLINE_MODE_CORE || mode == COMMANDLINE_MODE_UA); | |
939 | |
940 CommandLineBuilder builder(mode); | |
941 if (mode == COMMANDLINE_MODE_UA) { | |
942 builder.set_install_source(kCmdLineInstallSource_Scheduler); | |
943 } | |
944 const CString task_parameters = builder.GetCommandLineArgs(); | |
945 | |
946 CString company_name; | |
947 VERIFY1(company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME)); | |
948 CString task_description; | |
949 task_description.FormatMessage(IDS_SCHEDULED_TASK_DESCRIPTION, company_name); | |
950 | |
951 CString task_name(mode == COMMANDLINE_MODE_CORE ? | |
952 internal::GetCurrentTaskNameCore(is_machine) : | |
953 internal::GetCurrentTaskNameUA(is_machine)); | |
954 if (internal::IsInstalledScheduledTask(task_name)) { | |
955 HRESULT hr = internal::InstallScheduledTask(task_name, | |
956 task_path, | |
957 task_parameters, | |
958 task_description, | |
959 is_machine, | |
960 mode == COMMANDLINE_MODE_CORE && | |
961 is_machine, | |
962 true, | |
963 mode == COMMANDLINE_MODE_UA); | |
964 | |
965 if (SUCCEEDED(hr)) { | |
966 return hr; | |
967 } | |
968 | |
969 // Try to uninstall the task that we failed to upgrade. Then create a new | |
970 // task name, and fall through to install that. | |
971 internal::UninstallScheduledTask(task_name); | |
972 if (mode == COMMANDLINE_MODE_CORE) { | |
973 VERIFY1(SUCCEEDED( | |
974 internal::CreateAndSetVersionedTaskNameCoreInRegistry(is_machine))); | |
975 task_name = internal::GetCurrentTaskNameCore(is_machine); | |
976 } else { | |
977 VERIFY1(SUCCEEDED( | |
978 internal::CreateAndSetVersionedTaskNameUAInRegistry(is_machine))); | |
979 task_name = internal::GetCurrentTaskNameUA(is_machine); | |
980 } | |
981 ASSERT1(!internal::IsInstalledScheduledTask(task_name)); | |
982 } | |
983 | |
984 return internal::InstallScheduledTask(task_name, | |
985 task_path, | |
986 task_parameters, | |
987 task_description, | |
988 is_machine, | |
989 mode == COMMANDLINE_MODE_CORE && | |
990 is_machine, | |
991 true, | |
992 mode == COMMANDLINE_MODE_UA); | |
993 } | |
994 | |
995 HRESULT InstallGoopdateTasks(const TCHAR* task_path, bool is_machine) { | |
996 HRESULT hr = InstallGoopdateTaskForMode(task_path, | |
997 is_machine, | |
998 COMMANDLINE_MODE_CORE); | |
999 if (FAILED(hr)) { | |
1000 return hr; | |
1001 } | |
1002 | |
1003 return InstallGoopdateTaskForMode(task_path, is_machine, COMMANDLINE_MODE_UA); | |
1004 } | |
1005 | |
1006 HRESULT UninstallGoopdateTasks(bool is_machine) { | |
1007 VERIFY1(SUCCEEDED(internal::UninstallScheduledTask( | |
1008 internal::GetCurrentTaskNameCore(is_machine)))); | |
1009 VERIFY1(SUCCEEDED(internal::UninstallScheduledTask( | |
1010 internal::GetCurrentTaskNameUA(is_machine)))); | |
1011 | |
1012 // Try to uninstall any tasks that we failed to update during a previous | |
1013 // overinstall. It is possible that we fail to uninstall these again here. | |
1014 VERIFY1(SUCCEEDED(internal::UninstallScheduledTasks( | |
1015 scheduled_task_utils::GetDefaultGoopdateTaskName(is_machine, | |
1016 COMMANDLINE_MODE_CORE)))); | |
1017 VERIFY1(SUCCEEDED(internal::UninstallScheduledTasks( | |
1018 scheduled_task_utils::GetDefaultGoopdateTaskName(is_machine, | |
1019 COMMANDLINE_MODE_UA)))); | |
1020 return S_OK; | |
1021 } | |
1022 | |
1023 HRESULT UninstallLegacyGoopdateTasks(bool is_machine) { | |
1024 const CString& legacy_omaha1_task = | |
1025 internal::GetOmaha1LegacyTaskName(is_machine); | |
1026 VERIFY1(SUCCEEDED(internal::UninstallScheduledTask(legacy_omaha1_task))); | |
1027 | |
1028 const CString& legacy_omaha2_task = | |
1029 internal::GetOmaha2LegacyTaskName(is_machine); | |
1030 VERIFY1(SUCCEEDED(internal::UninstallScheduledTask(legacy_omaha2_task))); | |
1031 | |
1032 return S_OK; | |
1033 } | |
1034 | |
1035 HRESULT StartGoopdateTaskCore(bool is_machine) { | |
1036 return internal::StartScheduledTask( | |
1037 internal::GetCurrentTaskNameCore(is_machine)); | |
1038 } | |
1039 | |
1040 bool IsInstalledGoopdateTaskUA(bool is_machine) { | |
1041 return internal::IsInstalledScheduledTask( | |
1042 internal::GetCurrentTaskNameUA(is_machine)); | |
1043 } | |
1044 | |
1045 bool IsDisabledGoopdateTaskUA(bool is_machine) { | |
1046 const CString& task_name(internal::GetCurrentTaskNameUA(is_machine)); | |
1047 return internal::GetScheduledTaskStatus(task_name) == SCHED_S_TASK_DISABLED; | |
1048 } | |
1049 | |
1050 HRESULT GetExitCodeGoopdateTaskUA(bool is_machine) { | |
1051 const CString& task_name(internal::GetCurrentTaskNameUA(is_machine)); | |
1052 return internal::GetScheduledTaskExitCode(task_name); | |
1053 } | |
1054 | |
1055 bool IsUATaskHealthy(bool is_machine) { | |
1056 if (!ServiceUtils::IsServiceRunning(SERVICE_SCHEDULE)) { | |
1057 UTIL_LOG(LW, (_T("[Task Scheduler Service is not running]"))); | |
1058 return false; | |
1059 } | |
1060 | |
1061 if (!IsInstalledGoopdateTaskUA(is_machine)) { | |
1062 UTIL_LOG(LW, (_T("[UA Task not installed]"))); | |
1063 return false; | |
1064 } | |
1065 | |
1066 if (IsDisabledGoopdateTaskUA(is_machine)) { | |
1067 UTIL_LOG(LW, (_T("[UA Task disabled]"))); | |
1068 return false; | |
1069 } | |
1070 | |
1071 return true; | |
1072 } | |
1073 | |
1074 } // namespace scheduled_task_utils | |
1075 | |
1076 } // namespace omaha | |
OLD | NEW |