Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1093 lines
32 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Job Scheduler Job Object Handler
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  7. //
  8. // File: jobex.cxx
  9. //
  10. // Contents: ITask interface methods
  11. //
  12. // Classes: CJob
  13. //
  14. // Interfaces: ITask
  15. //
  16. // History: 16-Oct-95 EricB created
  17. // 11-Nov-96 AnirudhS Fixed both GetRunTimes methods to return
  18. // the right success codes.
  19. // 20-Nov-01 ShBrown Switched to use standard NT build versioning
  20. //
  21. //-----------------------------------------------------------------------------
  22. #include "..\pch\headers.hxx"
  23. #pragma hdrstop
  24. #include "job.hxx"
  25. #include "defines.hxx"
  26. #include "misc.hxx"
  27. #include <ntverp.h>
  28. #include <strsafe.h>
  29. //
  30. // Increment the following if the job object file format is changed in an incompatible fashion.
  31. //
  32. #define JOB_OBJ_FORMAT_VERSION 1
  33. //+----------------------------------------------------------------------------
  34. //
  35. // Member: CJob::CJob
  36. //
  37. // Synopsis: constructor
  38. //
  39. //-----------------------------------------------------------------------------
  40. CJob::CJob(void) :
  41. m_wVersion(MAKEWORD(VER_PRODUCTMINORVERSION, VER_PRODUCTMAJORVERSION)),
  42. m_wFileObjVer(JOB_OBJ_FORMAT_VERSION),
  43. m_wTriggerOffset(0),
  44. m_wErrorRetryCount(0),
  45. m_wErrorRetryInterval(0),
  46. m_cRunningInstances(0),
  47. m_wIdleWait(SCH_DEFAULT_IDLE_TIME),
  48. m_wIdleDeadline(SCH_DEFAULT_IDLE_DEADLINE),
  49. m_dwPriority(NORMAL_PRIORITY_CLASS),
  50. m_dwMaxRunTime(MAX_RUN_TIME_DEFAULT),
  51. m_ExitCode(0),
  52. m_hrStatus(SCHED_S_TASK_NOT_SCHEDULED),
  53. m_rgFlags(JOB_I_FLAG_NO_RUN_PROP_CHANGE), // The task is not yet dirty
  54. m_rgTaskFlags(0),
  55. m_pwszApplicationName(NULL),
  56. m_pwszParameters(NULL),
  57. m_pwszWorkingDirectory(NULL),
  58. m_pwszCreator(NULL),
  59. m_pwszComment(NULL),
  60. m_cbTaskData(0),
  61. m_pbTaskData(NULL),
  62. m_cReserved(0),
  63. m_pbReserved(NULL),
  64. m_hrStartError(SCHED_S_TASK_HAS_NOT_RUN),
  65. m_pIJobTypeInfo(NULL),
  66. m_cReferences(1),
  67. m_pAccountInfo(NULL),
  68. m_pbSignature(NULL),
  69. m_ptszFileName(NULL),
  70. m_fFileCreated(FALSE)
  71. {
  72. //TRACE(CJob, CJob);
  73. UUID uuidNull = {0};
  74. m_uuidJob = uuidNull;
  75. SYSTEMTIME stNull = {0};
  76. m_stMostRecentRunTime = stNull;
  77. }
  78. //+----------------------------------------------------------------------------
  79. //
  80. // Member: CJob::~CJob
  81. //
  82. // Synopsis: destructor
  83. //
  84. //-----------------------------------------------------------------------------
  85. CJob::~CJob(void)
  86. {
  87. //TRACE(CJob, ~CJob);
  88. FreeProperties();
  89. if (m_pIJobTypeInfo != NULL)
  90. {
  91. m_pIJobTypeInfo->Release();
  92. }
  93. delete m_ptszFileName;
  94. if (m_pAccountInfo != NULL)
  95. {
  96. ZERO_PASSWORD(m_pAccountInfo->pwszPassword); // NULL is handled.
  97. delete m_pAccountInfo->pwszAccount;
  98. delete m_pAccountInfo->pwszPassword;
  99. delete m_pAccountInfo;
  100. }
  101. }
  102. //+----------------------------------------------------------------------------
  103. //
  104. // Member: CJob::ITask::GetRunTimes
  105. //
  106. // Synopsis: Return a list of run times for this job that fall between the
  107. // bracketing times inclusively.
  108. //
  109. // Arguments: [pstBegin] - the start of the bracketing period
  110. // [pstEnd] - the end of the bracketing period, may be NULL
  111. // [pCount] - On entry, points to the number of run times
  112. // to retrieve. This must be a number between 1
  113. // and TASK_MAX_RUN_TIMES. On exit, points to
  114. // the number of run times actually retrieved.
  115. // [rgstJobTimes] - the returned array of SYSTEMTIME structures
  116. //
  117. // Returns: S_OK: the requested number of run times are returned.
  118. // S_FALSE: fewer than the requested number of run times are
  119. // returned. (More precisely: the task has valid time-based
  120. // triggers, but the number of run times during the specified
  121. // time bracket is less than the number of run times requested.
  122. // (This includes the case of no event triggers and zero run
  123. // times during that time bracket.))
  124. // SCHED_S_EVENT_TRIGGER: no time-based triggers will cause the
  125. // task to run during the specified time bracket, but event
  126. // triggers may (note that event triggers have no set run
  127. // times). (In this case *pCount is set to 0)
  128. // SCHED_S_TASK_NO_VALID_TRIGGERS: the task is enabled but has no
  129. // valid triggers.
  130. // SCHED_S_TASK_DISABLED: the task is disabled.
  131. // E_INVALIDARG: the arguments are not valid.
  132. // E_OUTOFMEMORY: not enough memory is available.
  133. //
  134. // Notes: The job time list is callee allocated and caller freed. The
  135. // caller must use CoTaskMemFree to free this list.
  136. //-----------------------------------------------------------------------------
  137. STDMETHODIMP
  138. CJob::GetRunTimes(const LPSYSTEMTIME pstBegin, const LPSYSTEMTIME pstEnd,
  139. WORD * pCount, LPSYSTEMTIME * rgstJobTimes)
  140. {
  141. TRACE(CJob, GetRunTimes);
  142. HRESULT hr;
  143. WORD cLimit = *pCount;
  144. if (cLimit < 1 || cLimit > TASK_MAX_RUN_TIMES)
  145. {
  146. return E_INVALIDARG;
  147. }
  148. *rgstJobTimes = NULL;
  149. //
  150. // Get the list of run times.
  151. //
  152. CTimeRunList RunList;
  153. *pCount = 0; // Number of elements in RunList
  154. hr = GetRunTimesP(pstBegin, pstEnd, pCount, cLimit, &RunList, NULL);
  155. if (hr != S_OK)
  156. {
  157. *pCount = 0;
  158. return hr;
  159. }
  160. schAssert(*pCount <= cLimit);
  161. //
  162. // Convert the time list to an array of SYSTEMTIMEs.
  163. //
  164. #if DBG
  165. WORD cCountBefore = *pCount;
  166. #endif
  167. hr = RunList.MakeSysTimeArray(rgstJobTimes, pCount);
  168. if (SUCCEEDED(hr))
  169. {
  170. schAssert(*pCount == cCountBefore);
  171. hr = (*pCount < cLimit) ? S_FALSE : S_OK;
  172. }
  173. return hr;
  174. }
  175. //+----------------------------------------------------------------------------
  176. //
  177. // Member: CJob::GetRunTimesP, private
  178. //
  179. // Synopsis: Computes a set of run times for this job that fall between the
  180. // bracketing times inclusively, and adds them to the run list.
  181. //
  182. // Arguments: [pstBegin] - the start of the bracketing period
  183. // [pstEnd] - the end of the bracketing period, may be NULL
  184. // [pCount] - On both entry and exit, points to the number of
  185. // run times in the list.
  186. // [cLimit] - the maximum number of CRun objects that the list
  187. // may contain.
  188. // [pRunList] - the list of run time objects, can be
  189. // NULL if just checking to see if there will be
  190. // *any* runs. (Note: If it's NULL, duplicate run
  191. // times are not detected, so pCount may be over-
  192. // estimated on return.)
  193. // [ptszShortName] - the folder-relative job name.
  194. //
  195. // Returns: S_OK: Some (zero or more) runs have been added to the list.
  196. // SCHED_S_EVENT_TRIGGER: the job has an event trigger and none
  197. // of the other triggers had runs.
  198. // SCHED_S_TASK_NO_VALID_TRIGGERS: the triggers are disabled or
  199. // not set.
  200. // SCHED_S_TASK_DISABLED: the job is disabled.
  201. //
  202. // Notes: The job time list is callee allocated and caller freed. The
  203. // caller must use FreeList to free this list.
  204. //-----------------------------------------------------------------------------
  205. HRESULT
  206. CJob::GetRunTimesP(const SYSTEMTIME * pstBegin, const SYSTEMTIME * pstEnd,
  207. WORD * pCount, WORD cLimit, CTimeRunList * pRunList,
  208. LPTSTR ptszShortName)
  209. {
  210. HRESULT hr;
  211. schAssert(cLimit > 0); // If cLimit is 0, it's not clear what to return
  212. schAssert(cLimit <= TASK_MAX_RUN_TIMES);
  213. schAssert(*pCount <= cLimit);
  214. //
  215. // Test for conditions that would prevent a run time from being returned.
  216. //
  217. if (IsFlagSet(TASK_FLAG_DISABLED))
  218. {
  219. return SCHED_S_TASK_DISABLED;
  220. }
  221. WORD cTriggers = m_Triggers.GetCount();
  222. //
  223. // Don't need any of the internal job flags for run instance processing.
  224. // That bit space is used for run processing specific flags defined in
  225. // runobj.hxx.
  226. //
  227. DWORD rgFlags = GetUserFlags();
  228. BOOL fEventTriggerFound = FALSE;
  229. BOOL fTimeTriggerFound = FALSE;
  230. //
  231. // Loop over the triggers.
  232. //
  233. for (WORD i = 0; i < cTriggers; i++)
  234. {
  235. hr = ::GetTriggerRunTimes(m_Triggers[i],
  236. pstBegin,
  237. pstEnd,
  238. pCount,
  239. cLimit,
  240. pRunList,
  241. ptszShortName,
  242. rgFlags,
  243. m_dwMaxRunTime,
  244. m_wIdleWait,
  245. m_wIdleDeadline);
  246. if (FAILED(hr))
  247. {
  248. ERR_OUT("CJob::GetRunTimes, ::GetRunTimes", hr);
  249. return(hr);
  250. }
  251. if (hr == SCHED_S_EVENT_TRIGGER)
  252. {
  253. fEventTriggerFound = TRUE;
  254. }
  255. else if (hr == S_OK)
  256. {
  257. fTimeTriggerFound = TRUE;
  258. if (*pCount >= cLimit && pRunList == NULL)
  259. {
  260. //
  261. // Special case where all we want is to test *if* there are runs,
  262. // and don't need the run times. Otherwise, we examine all
  263. // triggers because they won't return run times in any particular
  264. // order.
  265. //
  266. break;
  267. }
  268. }
  269. }
  270. //
  271. // Summarize the results in the return code
  272. //
  273. if (fTimeTriggerFound)
  274. {
  275. if (*pCount == 0 && fEventTriggerFound)
  276. {
  277. hr = SCHED_S_EVENT_TRIGGER;
  278. // (BUGBUG Here, we are assuming that *pCount was 0 initially.
  279. // Maybe add this to the comments and assertions at the start
  280. // of this function. However, this might not be necessary if
  281. // *pCount is made a member of CRunList, as it should be.)
  282. }
  283. else
  284. {
  285. hr = S_OK;
  286. }
  287. }
  288. else if (fEventTriggerFound)
  289. {
  290. hr = SCHED_S_EVENT_TRIGGER;
  291. }
  292. else
  293. {
  294. hr = SCHED_S_TASK_NO_VALID_TRIGGERS;
  295. }
  296. return hr;
  297. }
  298. //+----------------------------------------------------------------------------
  299. //
  300. // Synopsis: Skips past the preceeding data and loads only the triggers
  301. //
  302. // Notes: This method will fail if not preceeded at some time during
  303. // the job object's lifetime by a call to LoadP.
  304. //
  305. //-----------------------------------------------------------------------------
  306. HRESULT
  307. CJob::LoadTriggers(void)
  308. {
  309. if (m_ptszFileName == NULL || m_wTriggerOffset == 0)
  310. {
  311. return E_FAIL;
  312. }
  313. //
  314. // Open the file.
  315. //
  316. HANDLE hFile = NULL;
  317. HRESULT hr = OpenFileWithRetry(m_ptszFileName, GENERIC_READ, FILE_SHARE_READ, &hFile);
  318. if (FAILED(hr))
  319. {
  320. ERR_OUT("CJob::LoadTriggers, file open", hr);
  321. return hr;
  322. }
  323. //
  324. // Move to the trigger data.
  325. //
  326. DWORD dwBytes;
  327. dwBytes = SetFilePointer(hFile, m_wTriggerOffset, NULL, FILE_BEGIN);
  328. if (dwBytes == 0xffffffff)
  329. {
  330. hr = HRESULT_FROM_WIN32(GetLastError());
  331. ERR_OUT("CJob::LoadTriggers, move to trigger data", hr);
  332. goto cleanup;
  333. }
  334. //
  335. // Load triggers.
  336. //
  337. hr = this->_LoadTriggers(hFile);
  338. cleanup:
  339. //
  340. // Close the file.
  341. //
  342. CloseHandle(hFile);
  343. return hr;
  344. }
  345. //+----------------------------------------------------------------------------
  346. //
  347. // Member: CJob::_LoadTriggersFromBuffer, private
  348. //
  349. // Synopsis:
  350. //
  351. // Arguments:
  352. //
  353. // Returns:
  354. //
  355. //-----------------------------------------------------------------------------
  356. HRESULT
  357. CJob::_LoadTriggersFromBuffer(CInputBuffer * pBuf)
  358. {
  359. HRESULT hr = S_OK;
  360. WORD cTriggers;
  361. // Read trigger count.
  362. //
  363. if (!pBuf->Read(&cTriggers, sizeof cTriggers))
  364. {
  365. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  366. CHECK_HRESULT(hr);
  367. return hr;
  368. }
  369. // Verify buffer contains that many triggers.
  370. //
  371. BYTE *pTriggers = pBuf->CurrentPosition(); // save current position
  372. if (!pBuf->Advance(cTriggers * sizeof TASK_TRIGGER))
  373. {
  374. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  375. CHECK_HRESULT(hr);
  376. return hr;
  377. }
  378. // Copy triggers into a properly aligned array.
  379. // CODEWORK: Align them in the original buffer instead of allocating
  380. // a separate one.
  381. //
  382. hr = m_Triggers.AllocAndCopy(cTriggers, pTriggers);
  383. if (FAILED(hr))
  384. {
  385. CHECK_HRESULT(hr);
  386. return hr;
  387. }
  388. if (cTriggers)
  389. {
  390. //
  391. // BUGBUG: temporary, remove the next time the job file format
  392. // is revised.
  393. //
  394. TASK_TRIGGER * ajt = m_Triggers.GetArray();
  395. for (WORD i = 0; i < cTriggers; i++)
  396. {
  397. ajt[i].Reserved1 = i;
  398. }
  399. //
  400. // end of temporary code.
  401. //
  402. this->SetFlag(JOB_I_FLAG_HAS_TRIGGERS);
  403. }
  404. else
  405. {
  406. //
  407. // No triggers - clear trigger flag.
  408. //
  409. this->ClearFlag(JOB_I_FLAG_HAS_TRIGGERS);
  410. }
  411. return S_OK;
  412. }
  413. //+----------------------------------------------------------------------------
  414. //
  415. // Member: CJob::_LoadTriggers, private
  416. //
  417. // Synopsis:
  418. //
  419. // Arguments: None.
  420. //
  421. // Returns:
  422. //
  423. // Notes: None.
  424. //
  425. //-----------------------------------------------------------------------------
  426. HRESULT
  427. CJob::_LoadTriggers(HANDLE hFile)
  428. {
  429. HRESULT hr = S_OK;
  430. DWORD dwRead;
  431. WORD cTriggers;
  432. // Read trigger count.
  433. //
  434. if (!ReadFile(hFile, &cTriggers, sizeof(cTriggers), &dwRead, NULL))
  435. {
  436. hr = HRESULT_FROM_WIN32(GetLastError());
  437. CHECK_HRESULT(hr);
  438. return(hr);
  439. }
  440. if (dwRead != sizeof(cTriggers))
  441. {
  442. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  443. CHECK_HRESULT(hr);
  444. return(hr);
  445. }
  446. // Free existing trigger array.
  447. //
  448. m_Triggers.FreeArray();
  449. // Get on with load.
  450. //
  451. if (cTriggers)
  452. {
  453. TASK_TRIGGER * ajt = (TASK_TRIGGER *)
  454. LocalAlloc(LMEM_FIXED, cTriggers * sizeof TASK_TRIGGER);
  455. if (ajt == NULL)
  456. {
  457. hr = HRESULT_FROM_WIN32(GetLastError());
  458. CHECK_HRESULT(hr);
  459. return hr;
  460. }
  461. if (!ReadFile(hFile, ajt, cTriggers * sizeof TASK_TRIGGER, &dwRead,
  462. NULL))
  463. {
  464. hr = HRESULT_FROM_WIN32(GetLastError());
  465. CHECK_HRESULT(hr);
  466. LocalFree(ajt);
  467. return hr;
  468. }
  469. if (dwRead != cTriggers * sizeof TASK_TRIGGER)
  470. {
  471. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  472. CHECK_HRESULT(hr);
  473. LocalFree(ajt);
  474. return hr;
  475. }
  476. for (WORD i = 0; i < cTriggers; i++)
  477. {
  478. //
  479. // BUGBUG: temporary, remove the next time the job file format
  480. // is revised.
  481. //
  482. ajt[i].Reserved1 = i;
  483. //
  484. // end of temporary code.
  485. //
  486. }
  487. m_Triggers.SetArray(cTriggers, ajt);
  488. this->SetFlag(JOB_I_FLAG_HAS_TRIGGERS);
  489. }
  490. else
  491. {
  492. //
  493. // No triggers - clear trigger flag.
  494. //
  495. this->ClearFlag(JOB_I_FLAG_HAS_TRIGGERS);
  496. }
  497. return(hr);
  498. }
  499. //+----------------------------------------------------------------------------
  500. //
  501. // Member: CJob::_SaveTriggers, private
  502. //
  503. // Synopsis:
  504. //
  505. // Arguments: None.
  506. //
  507. // Returns:
  508. //
  509. // Notes: None.
  510. //
  511. //-----------------------------------------------------------------------------
  512. HRESULT
  513. CJob::_SaveTriggers(HANDLE hFile)
  514. {
  515. HRESULT hr = S_OK;
  516. DWORD dwWritten;
  517. DWORD cTriggerDataSize;
  518. WORD cTriggers = m_Triggers.GetCount();
  519. // Write trigger count.
  520. //
  521. if (!WriteFile(hFile, &cTriggers, sizeof(cTriggers), &dwWritten, NULL))
  522. {
  523. hr = HRESULT_FROM_WIN32(GetLastError());
  524. CHECK_HRESULT(hr);
  525. return(hr);
  526. }
  527. if (dwWritten != sizeof(cTriggers))
  528. {
  529. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  530. CHECK_HRESULT(hr);
  531. return(hr);
  532. }
  533. // Write trigger data.
  534. //
  535. if (!WriteFile(hFile,
  536. m_Triggers.GetArray(),
  537. cTriggerDataSize = sizeof(TASK_TRIGGER) * cTriggers,
  538. &dwWritten,
  539. NULL))
  540. {
  541. hr = HRESULT_FROM_WIN32(GetLastError());
  542. CHECK_HRESULT(hr);
  543. return(hr);
  544. }
  545. if (dwWritten != cTriggerDataSize)
  546. {
  547. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  548. CHECK_HRESULT(hr);
  549. }
  550. return(hr);
  551. }
  552. //+----------------------------------------------------------------------------
  553. //
  554. // Member: CJob::SetTriggersDirty, protected
  555. //
  556. // Synopsis: Sets the triggers-dirty flag and clears the unchanged and
  557. // NetSchedule flags.
  558. //
  559. // Notes: IPersistFile::Save will call UpdateJobState if the trigger-
  560. // dirty flag is set. UpdateJobState updates the job's status
  561. // property.
  562. //-----------------------------------------------------------------------------
  563. void
  564. CJob::SetTriggersDirty(void)
  565. {
  566. //
  567. // The JOB_I_FLAG_TRIGGERS_DIRTY flag indicates the in-core object does
  568. // not match the persistent object. This flag is not saved persistently; it
  569. // is cleared on a Save.
  570. //
  571. SetFlag(JOB_I_FLAG_TRIGGERS_DIRTY);
  572. //
  573. // We set this flag here so that a rebuild will occur due to the possible
  574. // run time changes introduced by the trigger change.
  575. //
  576. SetFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
  577. ClearFlag(JOB_I_FLAG_NO_RUN_PROP_CHANGE);
  578. }
  579. // Class members - *not* part of any interface.
  580. //
  581. //+----------------------------------------------------------------------------
  582. //
  583. // Member: CJob::SetTrigger, public
  584. //
  585. // Synopsis:
  586. //
  587. // Arguments: [] -
  588. //
  589. // Returns:
  590. //
  591. // Notes:
  592. //-----------------------------------------------------------------------------
  593. HRESULT
  594. CJob::SetTrigger(WORD iTrigger, PTASK_TRIGGER pTrigger)
  595. {
  596. TASK_TRIGGER * pjt = this->_GetTrigger(iTrigger);
  597. schAssert(pjt != NULL);
  598. if (pjt == NULL)
  599. {
  600. CHECK_HRESULT(E_FAIL);
  601. return(E_FAIL);
  602. }
  603. //
  604. // Check version. Do not modify triggers created by a later version.
  605. //
  606. // BUGBUG : This doesn't seem quite right.
  607. //
  608. if (pTrigger->cbTriggerSize != sizeof(TASK_TRIGGER))
  609. {
  610. CHECK_HRESULT(E_INVALIDARG);
  611. return(E_INVALIDARG);
  612. }
  613. //
  614. // Data validation.
  615. //
  616. if (pTrigger->wStartHour > JOB_MAX_HOUR ||
  617. pTrigger->wStartMinute > JOB_MAX_MINUTE)
  618. {
  619. return E_INVALIDARG;
  620. }
  621. if (!IsValidDate(pTrigger->wBeginMonth,
  622. pTrigger->wBeginDay,
  623. pTrigger->wBeginYear))
  624. {
  625. return E_INVALIDARG;
  626. }
  627. if (pTrigger->rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE &&
  628. !IsValidDate(pTrigger->wEndMonth,
  629. pTrigger->wEndDay,
  630. pTrigger->wEndYear))
  631. {
  632. return E_INVALIDARG;
  633. }
  634. //
  635. // If either MinutesDuration or MinutesInterval is nonzero, then
  636. // MinutesInterval must be less than MinutesDuration.
  637. //
  638. if ((pTrigger->MinutesDuration > 0 || pTrigger->MinutesInterval > 0) &&
  639. (pTrigger->MinutesInterval >= pTrigger->MinutesDuration))
  640. {
  641. return E_INVALIDARG;
  642. }
  643. //
  644. // Type consistency validation and value assignment.
  645. //
  646. switch (pTrigger->TriggerType)
  647. {
  648. case TASK_TIME_TRIGGER_DAILY:
  649. if (pTrigger->Type.Daily.DaysInterval == 0)
  650. {
  651. return E_INVALIDARG;
  652. }
  653. pjt->Type.Daily.DaysInterval = pTrigger->Type.Daily.DaysInterval;
  654. break;
  655. case TASK_TIME_TRIGGER_WEEKLY:
  656. if (pTrigger->Type.Weekly.WeeksInterval == 0 ||
  657. pTrigger->Type.Weekly.rgfDaysOfTheWeek == 0 ||
  658. pTrigger->Type.Weekly.rgfDaysOfTheWeek > JOB_RGFDOW_MAX)
  659. {
  660. return E_INVALIDARG;
  661. }
  662. pjt->Type.Weekly.WeeksInterval = pTrigger->Type.Weekly.WeeksInterval;
  663. pjt->Type.Weekly.rgfDaysOfTheWeek =
  664. pTrigger->Type.Weekly.rgfDaysOfTheWeek;
  665. break;
  666. case TASK_TIME_TRIGGER_MONTHLYDATE:
  667. if (!IsValidMonthlyDateTrigger(pTrigger))
  668. {
  669. return E_INVALIDARG;
  670. }
  671. pjt->Type.MonthlyDate.rgfDays = pTrigger->Type.MonthlyDate.rgfDays;
  672. pjt->Type.MonthlyDate.rgfMonths = pTrigger->Type.MonthlyDate.rgfMonths;
  673. break;
  674. case TASK_TIME_TRIGGER_MONTHLYDOW:
  675. if (pTrigger->Type.MonthlyDOW.wWhichWeek < TASK_FIRST_WEEK ||
  676. pTrigger->Type.MonthlyDOW.wWhichWeek > TASK_LAST_WEEK ||
  677. pTrigger->Type.MonthlyDOW.rgfDaysOfTheWeek == 0 ||
  678. pTrigger->Type.MonthlyDOW.rgfDaysOfTheWeek > JOB_RGFDOW_MAX ||
  679. pTrigger->Type.MonthlyDOW.rgfMonths == 0 ||
  680. pTrigger->Type.MonthlyDOW.rgfMonths > JOB_RGFMONTHS_MAX)
  681. {
  682. return E_INVALIDARG;
  683. }
  684. pjt->Type.MonthlyDOW.wWhichWeek = pTrigger->Type.MonthlyDOW.wWhichWeek;
  685. pjt->Type.MonthlyDOW.rgfDaysOfTheWeek =
  686. pTrigger->Type.MonthlyDOW.rgfDaysOfTheWeek;
  687. pjt->Type.MonthlyDOW.rgfMonths = pTrigger->Type.MonthlyDOW.rgfMonths;
  688. break;
  689. case TASK_TIME_TRIGGER_ONCE:
  690. case TASK_EVENT_TRIGGER_ON_IDLE:
  691. case TASK_EVENT_TRIGGER_AT_SYSTEMSTART:
  692. case TASK_EVENT_TRIGGER_AT_LOGON:
  693. // Not yet implemented:
  694. // case TASK_EVENT_TRIGGER_ON_APM_RESUME:
  695. //
  696. // No type-specific data for these.
  697. //
  698. break;
  699. default:
  700. return E_INVALIDARG;
  701. }
  702. pjt->TriggerType = pTrigger->TriggerType;
  703. pjt->cbTriggerSize = pTrigger->cbTriggerSize;
  704. pjt->wBeginYear = pTrigger->wBeginYear;
  705. pjt->wBeginMonth = pTrigger->wBeginMonth;
  706. pjt->wBeginDay = pTrigger->wBeginDay;
  707. pjt->wEndYear = pTrigger->wEndYear;
  708. pjt->wEndMonth = pTrigger->wEndMonth;
  709. pjt->wEndDay = pTrigger->wEndDay;
  710. pjt->wStartHour = pTrigger->wStartHour;
  711. pjt->wStartMinute = pTrigger->wStartMinute;
  712. pjt->MinutesDuration = pTrigger->MinutesDuration;
  713. pjt->MinutesInterval = pTrigger->MinutesInterval;
  714. //
  715. // The upper word of pjt->rgFlags is reserved, so set only the lower
  716. // word and retain the upper word values.
  717. //
  718. pjt->rgFlags &= JOB_INTERNAL_FLAG_MASK;
  719. pjt->rgFlags = pTrigger->rgFlags & ~JOB_INTERNAL_FLAG_MASK;
  720. //
  721. // This call explicitly set the trigger values, so clear the
  722. // JOB_TRIGGER_I_FLAG_NOT_SET bit.
  723. //
  724. pjt->rgFlags &= ~JOB_TRIGGER_I_FLAG_NOT_SET;
  725. this->SetTriggersDirty();
  726. //
  727. // If GetNextRunTime returned SCHED_S_TASK_NO_MORE_RUNS prior to this
  728. // call, then JOB_I_FLAG_NO_MORE_RUNS is set. Clear it and then call
  729. // UpdateJobState to bring the status current.
  730. //
  731. this->ClearFlag(JOB_I_FLAG_NO_MORE_RUNS);
  732. this->UpdateJobState(FALSE);
  733. return S_OK;
  734. }
  735. //+----------------------------------------------------------------------------
  736. //
  737. // Member: CJob::GetTrigger
  738. //
  739. // Synopsis:
  740. //
  741. // Arguments: [] -
  742. //
  743. // Returns: HRESULTS
  744. //
  745. // Notes:
  746. //
  747. //-----------------------------------------------------------------------------
  748. HRESULT
  749. CJob::GetTrigger(WORD iTrigger, PTASK_TRIGGER pTrigger)
  750. {
  751. TASK_TRIGGER * pjt = this->_GetTrigger(iTrigger);
  752. schAssert(pjt != NULL);
  753. if (pjt == NULL)
  754. {
  755. CHECK_HRESULT(E_FAIL);
  756. return(E_FAIL);
  757. }
  758. //
  759. // Do trigger type specific processing.
  760. //
  761. switch (pjt->TriggerType)
  762. {
  763. case TASK_TIME_TRIGGER_DAILY:
  764. pTrigger->Type.Daily.DaysInterval = pjt->Type.Daily.DaysInterval;
  765. break;
  766. case TASK_TIME_TRIGGER_WEEKLY:
  767. pTrigger->Type.Weekly.WeeksInterval = pjt->Type.Weekly.WeeksInterval;
  768. pTrigger->Type.Weekly.rgfDaysOfTheWeek =
  769. pjt->Type.Weekly.rgfDaysOfTheWeek;
  770. break;
  771. case TASK_TIME_TRIGGER_MONTHLYDATE:
  772. pTrigger->Type.MonthlyDate.rgfDays = pjt->Type.MonthlyDate.rgfDays;
  773. pTrigger->Type.MonthlyDate.rgfMonths = pjt->Type.MonthlyDate.rgfMonths;
  774. break;
  775. case TASK_TIME_TRIGGER_MONTHLYDOW:
  776. pTrigger->Type.MonthlyDOW.wWhichWeek = pjt->Type.MonthlyDOW.wWhichWeek;
  777. pTrigger->Type.MonthlyDOW.rgfDaysOfTheWeek =
  778. pjt->Type.MonthlyDOW.rgfDaysOfTheWeek;
  779. pTrigger->Type.MonthlyDOW.rgfMonths = pjt->Type.MonthlyDOW.rgfMonths;
  780. break;
  781. case TASK_TIME_TRIGGER_ONCE:
  782. case TASK_EVENT_TRIGGER_ON_IDLE:
  783. case TASK_EVENT_TRIGGER_AT_SYSTEMSTART:
  784. case TASK_EVENT_TRIGGER_AT_LOGON:
  785. // Not yet implemented:
  786. // case TASK_EVENT_TRIGGER_ON_APM_RESUME:
  787. //
  788. // No trigger-specific data.
  789. //
  790. break;
  791. default:
  792. //
  793. // In future revisions, different trigger types would be handled
  794. // here.
  795. //
  796. return E_INVALIDARG;
  797. }
  798. pTrigger->TriggerType = pjt->TriggerType;
  799. pTrigger->cbTriggerSize = pjt->cbTriggerSize;
  800. pTrigger->wBeginYear = pjt->wBeginYear;
  801. pTrigger->wBeginMonth = pjt->wBeginMonth;
  802. pTrigger->wBeginDay = pjt->wBeginDay;
  803. pTrigger->wEndYear = pjt->wEndYear;
  804. pTrigger->wEndMonth = pjt->wEndMonth;
  805. pTrigger->wEndDay = pjt->wEndDay;
  806. pTrigger->wStartHour = pjt->wStartHour;
  807. pTrigger->wStartMinute = pjt->wStartMinute;
  808. pTrigger->MinutesDuration = pjt->MinutesDuration;
  809. pTrigger->MinutesInterval = pjt->MinutesInterval;
  810. pTrigger->rgFlags = pjt->rgFlags;
  811. pTrigger->Reserved1 = 0;
  812. pTrigger->Reserved2 = pjt->Reserved2;
  813. pTrigger->wRandomMinutesInterval = pjt->wRandomMinutesInterval;
  814. //
  815. // If this trigger has not been set to non-default values, it will have
  816. // the flag bit JOB_TRIGGER_I_FLAG_NOT_SET set. Since this is an internal
  817. // value, replace it with TASK_TRIGGER_FLAG_DISABLED.
  818. //
  819. if (pTrigger->rgFlags & JOB_TRIGGER_I_FLAG_NOT_SET)
  820. {
  821. pTrigger->rgFlags &= ~JOB_INTERNAL_FLAG_MASK;
  822. pTrigger->rgFlags |= TASK_TRIGGER_FLAG_DISABLED;
  823. }
  824. else
  825. {
  826. pTrigger->rgFlags &= ~JOB_INTERNAL_FLAG_MASK;
  827. }
  828. //
  829. // Struct version check.
  830. //
  831. // In future revisions, different trigger structs would be handled
  832. // here.
  833. //
  834. //if (pTrigger->cbTriggerSize != sizeof(TASK_TRIGGER))
  835. //{
  836. // ;
  837. //}
  838. return S_OK;
  839. }
  840. //+----------------------------------------------------------------------------
  841. //
  842. // Member: CJob::SetErrorRetryCount, public
  843. //
  844. // Synopsis: Set the number of times the service should attempt to run a
  845. // job that is failing to start.
  846. //
  847. // Arguments: [wRetryCount] - zero, of course, means don't retry.
  848. //
  849. // Returns: HRESULTS
  850. //
  851. //-----------------------------------------------------------------------------
  852. HRESULT
  853. CJob::SetErrorRetryCount(WORD wRetryCount)
  854. {
  855. //TRACE(CJob, SetErrorRetryCount);
  856. m_wErrorRetryCount = wRetryCount;
  857. //
  858. // Not implemented yet
  859. //
  860. return E_NOTIMPL;
  861. }
  862. //+----------------------------------------------------------------------------
  863. //
  864. // Member: CJob::GetErrorRetryCount, public
  865. //
  866. // Synopsis: Get the number of times the service will attempt to run a
  867. // job that is failing to start.
  868. //
  869. // Arguments: [pwRetryCount] - the retry count.
  870. //
  871. // Returns: HRESULTS
  872. //
  873. //-----------------------------------------------------------------------------
  874. HRESULT
  875. CJob::GetErrorRetryCount(WORD * pwRetryCount)
  876. {
  877. //TRACE(CJob, GetErrorRetryCount);
  878. *pwRetryCount = m_wErrorRetryCount;
  879. //
  880. // Not implemented yet
  881. //
  882. return E_NOTIMPL;
  883. }
  884. //+----------------------------------------------------------------------------
  885. //
  886. // Member: CJob::SetIdleWait, public
  887. //
  888. // Synopsis: Set the time to wait until idle, in minutes. This is the
  889. // amount of time after the last user keyboard or mouse moverment
  890. // until the idle state will be entered for this task.
  891. //
  892. // Arguments: [wIdleMinutes] - the time to wait till idle in minutes.
  893. // [wDeadlineMinutes] - minutes to wait for [wIdleMinutes] of
  894. // idleness
  895. //
  896. // Returns: S_OK
  897. //
  898. // Notes: The task will wait for up to [wDeadlineMinutes] for an idle
  899. // period of [wIdleMinutes] to occur.
  900. //
  901. // If [wDeadlineMinutes] is 0, the task will not run unless the
  902. // computer has been idle for at least [wIdleMinutes] by the
  903. // task's start time.
  904. //
  905. //-----------------------------------------------------------------------------
  906. HRESULT
  907. CJob::SetIdleWait(WORD wIdleMinutes, WORD wDeadlineMinutes)
  908. {
  909. TRACE3(CJob, SetIdleWait);
  910. m_wIdleWait = wIdleMinutes;
  911. m_wIdleDeadline = wDeadlineMinutes;
  912. // Cause a wait list rebuild
  913. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY | JOB_I_FLAG_RUN_PROP_CHANGE);
  914. return S_OK;
  915. }
  916. //+----------------------------------------------------------------------------
  917. //
  918. // Member: CJob::GetIdleWait, public
  919. //
  920. // Synopsis: Get the idle time requirement and deadline, in minutes.
  921. //
  922. // Arguments: [pwMinutes] - the returned idle time.
  923. // [pwDeadline] - the returned idle deadline
  924. //
  925. // Returns: S_OK
  926. //
  927. //-----------------------------------------------------------------------------
  928. HRESULT
  929. CJob::GetIdleWait(WORD * pwMinutes, WORD * pwDeadline)
  930. {
  931. TRACE3(CJob, GetIdleWait);
  932. *pwMinutes = m_wIdleWait;
  933. *pwDeadline = m_wIdleDeadline;
  934. return S_OK;
  935. }
  936. //+----------------------------------------------------------------------------
  937. //
  938. // Member: CJob::SetErrorRetryInterval, public
  939. //
  940. // Synopsis: Set the interval, in minutes, between successive retries.
  941. //
  942. // Arguments: [wRetryInterval] - the retry interval.
  943. //
  944. // Returns: E_NOTIMPL
  945. //
  946. //-----------------------------------------------------------------------------
  947. HRESULT
  948. CJob::SetErrorRetryInterval(WORD wRetryInterval)
  949. {
  950. //TRACE(CJob, SetErrorRetryInterval);
  951. m_wErrorRetryInterval = wRetryInterval;
  952. //
  953. // Not implemented yet
  954. //
  955. return E_NOTIMPL;
  956. }
  957. //+----------------------------------------------------------------------------
  958. //
  959. // Member: CJob::GetErrorRetryInterval, public
  960. //
  961. // Synopsis: Get the interval, in minutes, between successive retries.
  962. //
  963. // Arguments: [pwRetryInterval] - the returned interval.
  964. //
  965. // Returns: E_NOTIMPL
  966. //
  967. //-----------------------------------------------------------------------------
  968. HRESULT
  969. CJob::GetErrorRetryInterval(WORD * pwRetryInterval)
  970. {
  971. //TRACE(CJob, GetErrorRetryInterval);
  972. *pwRetryInterval = m_wErrorRetryInterval;
  973. //
  974. // Not implemented yet
  975. //
  976. return E_NOTIMPL;
  977. }