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.

1199 lines
36 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Job Schedule Application Job Object Handler
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  7. //
  8. // File: util.cxx
  9. //
  10. // Contents: job & trigger objects IUnknown methods, class factory, DLL fcns,
  11. // plus misc utility fcns
  12. //
  13. // Classes: CJob (continued), CJobCF, CTrigger
  14. //
  15. // Interfaces: IUnknown, IClassFactory
  16. //
  17. // History: 24-May-95 EricB created
  18. //
  19. //-----------------------------------------------------------------------------
  20. #include "..\pch\headers.hxx"
  21. #pragma hdrstop
  22. #include "job.hxx"
  23. #include <StrSafe.h>
  24. //+----------------------------------------------------------------------------
  25. //
  26. // Member: CJob::GetAtInfo
  27. //
  28. // Synopsis: for a downlevel job, return its data in an AT_INFO struct.
  29. //
  30. // Arguments: [pAt] - pointer to the AT_INFO struct
  31. // [pwszCommand] - buffer for the command string
  32. // [pcchCommand] - on input, size of supplied buffer, on output,
  33. // size needed/used.
  34. //
  35. // Returns: HRESULTS - ERROR_INSUFFICIENT_BUFFER if too small
  36. // - SCHED_E_NOT_AN_AT_JOB if not an AT job
  37. //
  38. // Notes: This method is not exposed to external clients, thus it is not
  39. // part of a public interface.
  40. //-----------------------------------------------------------------------------
  41. HRESULT
  42. CJob::GetAtInfo(PAT_INFO pAt, LPWSTR pwszCommand, DWORD * pcchCommand)
  43. {
  44. TRACE(CJob, GetAtInfo);
  45. HRESULT hr = S_OK;
  46. if (!(m_rgFlags & JOB_I_FLAG_NET_SCHEDULE))
  47. {
  48. schDebugOut((DEB_ERROR,
  49. "CJob::GetAtInfo: Task object is not an AT job!\n"));
  50. return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  51. }
  52. //
  53. // The ApplicationName and Parameters properties need to be concatonated
  54. // and returned as pwszCommand. If there is any white space in the app
  55. // name string, then it must be enclosed in quotes.
  56. //
  57. LPWSTR pwszAppName, pwszParams;
  58. hr = GetApplicationName(&pwszAppName);
  59. if (FAILED(hr))
  60. {
  61. ERR_OUT("GetAtInfo: GetApplicationName", hr);
  62. return hr;
  63. }
  64. hr = GetParameters(&pwszParams);
  65. if (FAILED(hr))
  66. {
  67. ERR_OUT("GetAtInfo: GetParameters", hr);
  68. CoTaskMemFree(pwszAppName);
  69. return hr;
  70. }
  71. //
  72. // Check for whitespace in the app name.
  73. //
  74. BOOL fAppNameHasSpaces = HasSpaces(pwszAppName);
  75. //
  76. // If there is app name whitespace, add two for the quotes to be added.
  77. //
  78. DWORD cchApp = wcslen(pwszAppName) + (fAppNameHasSpaces ? 2 : 0);
  79. DWORD cchParam = wcslen(pwszParams);
  80. //
  81. // Add one for the terminating null and ont to concatenate the params
  82. //
  83. DWORD cch = cchApp + cchParam + 1 + 1;
  84. if (cch > *pcchCommand)
  85. {
  86. *pcchCommand = cch;
  87. CoTaskMemFree(pwszAppName);
  88. CoTaskMemFree(pwszParams);
  89. return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  90. }
  91. *pcchCommand = cch;
  92. if (fAppNameHasSpaces)
  93. {
  94. StringCchCopy(pwszCommand, *pcchCommand, L"\"");
  95. StringCchCat(pwszCommand, *pcchCommand, pwszAppName);
  96. StringCchCat(pwszCommand, *pcchCommand, L"\"");
  97. }
  98. else
  99. {
  100. StringCchCopy(pwszCommand, *pcchCommand, pwszAppName);
  101. }
  102. if (cchParam > 0)
  103. {
  104. StringCchCat(pwszCommand, *pcchCommand, L" ");
  105. StringCchCat(pwszCommand, *pcchCommand, pwszParams);
  106. }
  107. CoTaskMemFree(pwszAppName);
  108. CoTaskMemFree(pwszParams);
  109. //
  110. // An AT job can have one or two triggers of type TASK_TIME_TRIGGER_WEEKLY
  111. // and/or TASK_TIME_TRIGGER_MONTHLYDATE. It may also have, instead, a single trigger
  112. // of type TASK_TIME_TRIGGER_ONCE, which indicates it runs either today or
  113. // tomorrow, only.
  114. //
  115. WORD cTriggers = m_Triggers.GetCount();
  116. if (cTriggers == 0 || cTriggers > 2)
  117. {
  118. ERR_OUT("GetAtInfo: Incorrect Trigger Count", E_FAIL);
  119. return E_FAIL;
  120. }
  121. PTASK_TRIGGER pjt;
  122. pjt = _GetTrigger(0);
  123. if (pjt == NULL)
  124. {
  125. schAssert(!"GetCount > 0 but no trigger 0");
  126. return E_FAIL;
  127. }
  128. pAt->JobTime = (pjt->wStartHour * JOB_MINS_PER_HOUR +
  129. pjt->wStartMinute) * JOB_MILLISECONDS_PER_MINUTE;
  130. pAt->DaysOfMonth = pAt->DaysOfWeek = 0;
  131. switch (pjt->TriggerType)
  132. {
  133. case TASK_TIME_TRIGGER_WEEKLY:
  134. //
  135. // Convert Scheduler DOW to AT_INFO DOW:
  136. // Scheduler rgfDaysOfTheWeek: Sunday = bit 0, Monday = bit 1.
  137. // AT_INFO DaysOfWeek: Monday = bit 0, Sunday = bit 6.
  138. //
  139. pAt->DaysOfWeek = pjt->Type.Weekly.rgfDaysOfTheWeek >> 1;
  140. if (pjt->Type.Weekly.rgfDaysOfTheWeek & 0x0001)
  141. {
  142. pAt->DaysOfWeek |= 0x0040;
  143. }
  144. break;
  145. case TASK_TIME_TRIGGER_MONTHLYDATE:
  146. pAt->DaysOfMonth = pjt->Type.MonthlyDate.rgfDays;
  147. break;
  148. case TASK_TIME_TRIGGER_ONCE:
  149. // Day of Month & Week are NULL if the job runs only once.
  150. break;
  151. default:
  152. schAssert(FALSE && "GetAtInfo: wrong trigger type");
  153. ERR_OUT("GetAtInfo: wrong trigger type", hr);
  154. return E_FAIL;
  155. }
  156. if (cTriggers == 2)
  157. {
  158. pjt = _GetTrigger(1);
  159. switch (pjt->TriggerType)
  160. {
  161. case TASK_TIME_TRIGGER_WEEKLY:
  162. //
  163. // Convert Scheduler DOW to AT_INFO DOW:
  164. // Scheduler rgfDaysOfTheWeek: Sunday = bit 0, Monday = bit 1.
  165. // AT_INFO DaysOfWeek: Monday = bit 0, Sunday = bit 6.
  166. //
  167. pAt->DaysOfWeek = pjt->Type.Weekly.rgfDaysOfTheWeek >> 1;
  168. if (pjt->Type.Weekly.rgfDaysOfTheWeek & 0x0001)
  169. {
  170. pAt->DaysOfWeek |= 0x0040;
  171. }
  172. break;
  173. case TASK_TIME_TRIGGER_MONTHLYDATE:
  174. pAt->DaysOfMonth = pjt->Type.MonthlyDate.rgfDays;
  175. break;
  176. case TASK_TIME_TRIGGER_ONCE:
  177. schAssert(FALSE && "GetAtInfo: Once triggers not allowed in multiple triggers!");
  178. ERR_OUT("GetAtInfo: Once triggers not allowed in multiple triggers!", 0);
  179. return E_FAIL;
  180. break;
  181. default:
  182. schAssert(FALSE && "GetAtInfo: wrong trigger type");
  183. ERR_OUT("GetAtInfo: wrong trigger type", 0);
  184. return E_FAIL;
  185. }
  186. }
  187. //
  188. // Set the AT_INFO.Flags.
  189. //
  190. pAt->Flags = 0;
  191. if (!(m_rgFlags & TASK_FLAG_INTERACTIVE))
  192. {
  193. pAt->Flags |= JOB_NONINTERACTIVE;
  194. }
  195. if (!(pjt->rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE) &&
  196. (pjt->TriggerType != TASK_TIME_TRIGGER_ONCE))
  197. {
  198. pAt->Flags |= JOB_RUN_PERIODICALLY;
  199. }
  200. //
  201. // Check whether job runs today, or is even running in a time window
  202. // that is valid for a NetScheduleJob.
  203. //
  204. SYSTEMTIME stNow, stMidnight, stTomorrow;
  205. GetLocalTime(&stNow);
  206. WORD cRuns = 0;
  207. // Check to see if there is a run today and set the flag JOB_RUNS_TODAY
  208. stMidnight = stNow;
  209. stMidnight.wHour = stMidnight.wMinute = stMidnight.wSecond
  210. = stMidnight.wMilliseconds = 0;
  211. IncrementDay(&stMidnight);
  212. // Zero out cRuns - we used it, and it is not initialized in GetRunTimesP
  213. cRuns = 0;
  214. hr = GetRunTimesP(&stNow, &stMidnight, &cRuns, 1, NULL, NULL);
  215. if (FAILED(hr))
  216. {
  217. ERR_OUT("GetAtInfo: GetRunTimes", hr);
  218. return hr;
  219. }
  220. if (cRuns > 0)
  221. {
  222. pAt->Flags |= JOB_RUNS_TODAY;
  223. }
  224. //
  225. // Check exit status of last run and set TASK_EXEC_ERROR as needed.
  226. //
  227. if (IsFlagSet(JOB_I_FLAG_LAST_LAUNCH_FAILED) ||
  228. IsFlagSet(JOB_I_FLAG_ERROR_IN_LAST_RUN))
  229. {
  230. pAt->Flags |= JOB_EXEC_ERROR;
  231. }
  232. //
  233. // Safety check - if the trigger is a ONCE trigger, the job
  234. // must within the next 24 hours (or already be running)
  235. //
  236. //
  237. if (pjt->TriggerType == TASK_TIME_TRIGGER_ONCE &&
  238. !(pAt->Flags & JOB_EXEC_ERROR))
  239. {
  240. stTomorrow = stNow;
  241. IncrementDay(&stTomorrow);
  242. hr = GetRunTimesP(&stNow, &stTomorrow, &cRuns, 1, NULL, NULL);
  243. if (FAILED(hr))
  244. {
  245. ERR_OUT("GetAtInfo: GetRunTimes for Once Trigger", hr);
  246. return hr;
  247. }
  248. HRESULT status;
  249. GetStatus(&status);
  250. if (cRuns == 0)
  251. {
  252. if (status == SCHED_S_TASK_RUNNING)
  253. pAt->Flags |= JOB_RUNS_TODAY;
  254. else
  255. {
  256. ERR_OUT("GetAtInfo: Once trigger outside permitted time interval", 0);
  257. return E_FAIL;
  258. }
  259. }
  260. }
  261. //
  262. // Omit jobs whose end date has passed (usually jobs that haven't yet
  263. // been deleted because they're still running)
  264. //
  265. if (pjt->rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE)
  266. {
  267. SYSTEMTIME stEnd;
  268. stEnd.wYear = pjt->wEndYear;
  269. stEnd.wMonth = pjt->wEndMonth;
  270. stEnd.wDayOfWeek = 0;
  271. stEnd.wDay = pjt->wEndDay;
  272. stEnd.wHour = pjt->wStartHour;
  273. stEnd.wMinute = pjt->wStartMinute;
  274. stEnd.wSecond = 0;
  275. stEnd.wMilliseconds = 0;
  276. if (IsFirstTimeEarlier(&stEnd, &stNow) &&
  277. !(pAt->Flags & JOB_EXEC_ERROR))
  278. {
  279. return E_FAIL;
  280. }
  281. }
  282. return S_OK;
  283. }
  284. //+----------------------------------------------------------------------------
  285. //
  286. // Member: CJob::UpdateJobState
  287. //
  288. // Synopsis: Update the job flags and status depending on whether there are
  289. // valid triggers with more run times.
  290. //
  291. // Arguments: [fRunning] - optional param, defaults to false.
  292. // CSchedWorker::RunJobs sets this to true while
  293. // setting the new job status to
  294. // SCHED_S_TASK_RUNNING. So, don't change the job's
  295. // status if fRunning.
  296. //
  297. // Returns: HRESULTS
  298. //
  299. // Notes: The triggers have to be loaded.
  300. //-----------------------------------------------------------------------------
  301. HRESULT
  302. CJob::UpdateJobState(BOOL fRunning)
  303. {
  304. //TRACE(CJob, UpdateJobState);
  305. if (!IsFlagSet(JOB_I_FLAG_HAS_APPNAME))
  306. {
  307. //
  308. // The job can't run without an appname.
  309. //
  310. SetStatus(SCHED_S_TASK_NOT_SCHEDULED);
  311. return S_OK;
  312. }
  313. SYSTEMTIME stNow;
  314. GetLocalTime(&stNow);
  315. if (fRunning)
  316. {
  317. //
  318. // Increment the minute value so that the current run won't be returned.
  319. //
  320. stNow.wMinute++;
  321. if (stNow.wMinute >= JOB_MINS_PER_HOUR)
  322. {
  323. stNow.wHour++;
  324. stNow.wMinute = 0;
  325. if (stNow.wHour >= JOB_HOURS_PER_DAY)
  326. {
  327. stNow.wHour = 0;
  328. IncrementDay(&stNow);
  329. }
  330. }
  331. }
  332. WORD cRuns = 0;
  333. HRESULT hr;
  334. hr = GetRunTimesP(&stNow, NULL, &cRuns, 1, NULL, NULL);
  335. if (FAILED(hr))
  336. {
  337. ERR_OUT("CJob::UpdateJobState", hr);
  338. return hr;
  339. }
  340. //
  341. // Update the job flags and status properties.
  342. //
  343. if (hr == SCHED_S_TASK_NO_VALID_TRIGGERS)
  344. {
  345. SetFlag(JOB_I_FLAG_NO_VALID_TRIGGERS);
  346. SetStatus(SCHED_S_TASK_NOT_SCHEDULED);
  347. }
  348. else
  349. {
  350. ClearFlag(JOB_I_FLAG_NO_VALID_TRIGGERS);
  351. if (cRuns == 0 && hr != SCHED_S_EVENT_TRIGGER)
  352. {
  353. SetFlag(JOB_I_FLAG_NO_MORE_RUNS);
  354. }
  355. else
  356. {
  357. ClearFlag(JOB_I_FLAG_NO_MORE_RUNS);
  358. //
  359. // If the job isn't currently running and had not been ready to run but
  360. // now have both valid triggers and valid properties, set the status.
  361. // TODO: test TASK_FLAG_HAS_OBJPATH and TASK_FLAG_HAS_ACCOUNT when those
  362. // properties are functional.
  363. //
  364. if (!fRunning && IsFlagSet(JOB_I_FLAG_HAS_APPNAME))
  365. {
  366. if (m_stMostRecentRunTime.wYear == 0)
  367. {
  368. //
  369. // Job has never run if last-run-time property is null (all
  370. // elements will be zero if null, testing year is sufficient).
  371. //
  372. SetStatus(SCHED_S_TASK_HAS_NOT_RUN);
  373. }
  374. else
  375. {
  376. //
  377. // Job has run in the past, so it is now waiting to run again.
  378. //
  379. SetStatus(SCHED_S_TASK_READY);
  380. }
  381. }
  382. }
  383. }
  384. //
  385. // The disabled flag takes precedence over other states.
  386. //
  387. if (IsFlagSet(TASK_FLAG_DISABLED))
  388. {
  389. SetStatus(SCHED_S_TASK_DISABLED);
  390. }
  391. return S_OK;
  392. }
  393. //+----------------------------------------------------------------------------
  394. //
  395. // Member: CJob::PostRunUpdate
  396. //
  397. // Synopsis: update the status of a job object after a run exits
  398. //
  399. // Arguments: [ExitCode] - the run's exit code
  400. // [fFinishedOK] - only set the job object's exit code if TRUE.
  401. //
  402. // Returns: HRESULTS
  403. //
  404. // Notes: Set the ExitCode and Status values for the job and log the run
  405. // if the LogRunHistory property is set.
  406. // This method is not exposed to external clients, thus it is not
  407. // part of a public interface.
  408. // If any of the variable length properties or the triggers are
  409. // needed, then the caller will need to do a full activation of
  410. // the job.
  411. //
  412. //-----------------------------------------------------------------------------
  413. HRESULT
  414. CJob::PostRunUpdate(long ExitCode, BOOL fFinishedOK)
  415. {
  416. schDebugOut((DEB_ITRACE, "PostRunUpdate: decrementing running instance "
  417. "count (%d before decrement)\n", m_cRunningInstances));
  418. if (m_cRunningInstances > 0)
  419. {
  420. m_cRunningInstances--;
  421. }
  422. //
  423. // Don't update the status unless the running instance count is back to
  424. // zero.
  425. //
  426. if (m_cRunningInstances == 0)
  427. {
  428. if (IsFlagSet(JOB_I_FLAG_NO_VALID_TRIGGERS))
  429. {
  430. SetStatus(SCHED_S_TASK_NO_VALID_TRIGGERS);
  431. }
  432. else
  433. {
  434. SetStatus(SCHED_S_TASK_READY);
  435. }
  436. }
  437. if (fFinishedOK)
  438. {
  439. m_ExitCode = ExitCode;
  440. }
  441. ClearFlag(JOB_I_FLAG_ABORT_NOW);
  442. return S_OK;
  443. }
  444. //+----------------------------------------------------------------------------
  445. //
  446. // Member: CJob::IfEventJobAddToList
  447. //
  448. // Synopsis: Check if the job has any triggers of the specified event type.
  449. // If so, allocate and initialize a CRun object and add it to the
  450. // list. Add it to pIdleWaitList if it needs to wait for an
  451. // idle period, otherwise add it to pRunList.
  452. //
  453. // Arguments: [EventType] - the event trigger type
  454. // [ptszJobName] - the short job name to pass to CRun.
  455. // [pRunList] - a pointer to a run list object.
  456. // [pIdleWaitList] - a pointer to a run list object sorted by
  457. // idle wait times.
  458. //
  459. // Returns: S_OK if the event trigger is found, S_FALSE if not, or a
  460. // fatal error code.
  461. //
  462. // Assumes: Triggers have already been loaded.
  463. // If EventType is not TASK_EVENT_TRIGGER_ON_IDLE, this method is
  464. // only called when the event has occurred. (If EventType is
  465. // TASK_EVENT_TRIGGER_ON_IDLE, this method is called when
  466. // building the wait list from the tasks folder's contents.)
  467. //
  468. //-----------------------------------------------------------------------------
  469. HRESULT
  470. CJob::IfEventJobAddToList(TASK_TRIGGER_TYPE EventType, LPCTSTR ptszJobName,
  471. CRunList * pRunList, CIdleRunList * pIdleWaitList)
  472. {
  473. schAssert(EventType == TASK_EVENT_TRIGGER_ON_IDLE ||
  474. EventType == TASK_EVENT_TRIGGER_AT_SYSTEMSTART ||
  475. EventType == TASK_EVENT_TRIGGER_AT_LOGON);
  476. if (! IsFlagSet(JOB_I_FLAG_HAS_TRIGGERS))
  477. {
  478. // (An optimization; the function would still work without it)
  479. return S_FALSE;
  480. }
  481. if (EventType == TASK_EVENT_TRIGGER_ON_IDLE && m_wIdleWait == 0)
  482. {
  483. //
  484. // We ignore all idle triggers if the idle wait time is 0.
  485. //
  486. return S_FALSE;
  487. }
  488. //
  489. // Will the job need to wait for an idle period before being started?
  490. //
  491. BOOL fNeedIdleWait = ((EventType == TASK_EVENT_TRIGGER_ON_IDLE ||
  492. IsFlagSet(TASK_FLAG_START_ONLY_IF_IDLE))
  493. && m_wIdleWait > 0);
  494. HRESULT hr;
  495. FILETIME ftNow = GetLocalTimeAsFileTime();
  496. // BUGBUG ftNow should be passed in, and the same ftNow should be used
  497. // when checking against the deadlines. Otherwise a run can be missed
  498. // if we're slow here and the deadline elapses.
  499. //
  500. // See if the job has any triggers of the specified type.
  501. // Find the latest deadline of these triggers.
  502. //
  503. FILETIME ftLatestDeadline = { 0, 0 };
  504. for (WORD i = 0; i < m_Triggers.GetCount(); i++)
  505. {
  506. if (m_Triggers[i].TriggerType != EventType)
  507. {
  508. continue;
  509. }
  510. //
  511. // If the trigger is an idle trigger then the trigger's deadline
  512. // is simply midnight on the trigger's end date.
  513. //
  514. // If the job does not have TASK_FLAG_START_ONLY_IF_IDLE then
  515. // the trigger's deadline is simply midnight on the trigger's
  516. // end date.
  517. //
  518. // If the job does have TASK_FLAG_START_ONLY_IF_IDLE then the
  519. // trigger's deadline is
  520. // min( trigger's end date,
  521. // job's start time + trigger's MinutesDuration)
  522. //
  523. FILETIME ftDeadline // This trigger's deadline
  524. = MAX_FILETIME; // End date if no end date set
  525. if (m_Triggers[i].rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE)
  526. {
  527. SYSTEMTIME stEnd = // This trigger's end date
  528. {
  529. m_Triggers[i].wEndYear,
  530. m_Triggers[i].wEndMonth,
  531. 0, // wDayOfWeek
  532. m_Triggers[i].wEndDay,
  533. 23, // wHour
  534. 59, // wMinute
  535. 0, // wSecond
  536. 0 // wMilliseconds
  537. };
  538. if (!SystemTimeToFileTime(&stEnd, &ftDeadline))
  539. {
  540. // Presume the trigger had an invalid end date.
  541. // Ignore the trigger. BUGBUG return an error ?
  542. continue;
  543. }
  544. }
  545. if (EventType != TASK_EVENT_TRIGGER_ON_IDLE && fNeedIdleWait)
  546. {
  547. //
  548. // Calculate (job's start time + trigger's MinutesDuration)
  549. // This method is only called when the event that fires the
  550. // trigger has occurred, so the job's start time is now.
  551. //
  552. FILETIME ftDurationEnd = ftNow;
  553. AddMinutesToFileTime(&ftDurationEnd, m_Triggers[i].MinutesDuration);
  554. ftDeadline = minFileTime(ftDeadline, ftDurationEnd);
  555. }
  556. ftLatestDeadline = maxFileTime(ftLatestDeadline, ftDeadline);
  557. }
  558. if (CompareFileTime(&ftLatestDeadline, &ftNow) < 0)
  559. {
  560. //
  561. // All the triggers of this type have expired.
  562. //
  563. return S_FALSE;
  564. }
  565. //
  566. // Add the job to the appropriate run list.
  567. //
  568. CRun * pNewRun;
  569. if (fNeedIdleWait)
  570. {
  571. DBG_OUT("Adding idle job to list.");
  572. pNewRun = new CRun(m_dwMaxRunTime, GetUserFlags(), m_wIdleWait,
  573. ftLatestDeadline,
  574. (EventType == TASK_EVENT_TRIGGER_ON_IDLE));
  575. }
  576. else
  577. {
  578. pNewRun = new CRun(m_dwMaxRunTime, GetUserFlags(), MAX_FILETIME,
  579. FALSE);
  580. }
  581. if (pNewRun == NULL)
  582. {
  583. ERR_OUT("CJob::IfEventJobAddToList new CRun", E_OUTOFMEMORY);
  584. return E_OUTOFMEMORY;
  585. }
  586. // Complete job info object initialization.
  587. //
  588. hr = pNewRun->Initialize(ptszJobName);
  589. if (FAILED(hr))
  590. {
  591. ERR_OUT("CJob::IfEventJobAddToList, CRun->Initialize", hr);
  592. delete pNewRun;
  593. return hr;
  594. }
  595. if (fNeedIdleWait)
  596. {
  597. pIdleWaitList->AddSortedByIdleWait(pNewRun);
  598. }
  599. else
  600. {
  601. pRunList->Add(pNewRun);
  602. }
  603. return S_OK;
  604. }
  605. //+----------------------------------------------------------------------------
  606. //
  607. // Member: CJob::IfStartupJobAddToList
  608. //
  609. // Synopsis: Check if the job has any startup triggers. If so, allocate and
  610. // initialize a CRun object and add it to the list.
  611. //
  612. // Arguments: [ptszJobName] - the short job name to pass to CRun.
  613. // [pRunList] - a pointer to a run list object.
  614. //
  615. // Returns: S_OK if the event trigger is found, S_FALSE if not, or a
  616. // fatal error code.
  617. //
  618. // Assumes: Triggers have already been loaded.
  619. //
  620. //-----------------------------------------------------------------------------
  621. HRESULT
  622. CJob::IfStartupJobAddToList(LPTSTR ptszJobName, CRunList * pRunList,
  623. CIdleRunList * pIdleWaitList)
  624. {
  625. return IfEventJobAddToList(TASK_EVENT_TRIGGER_AT_SYSTEMSTART, ptszJobName,
  626. pRunList, pIdleWaitList);
  627. }
  628. //+----------------------------------------------------------------------------
  629. //
  630. // Member: CJob::IfLogonJobAddToList
  631. //
  632. // Synopsis: Check if the job has any logon triggers. If so, allocate and
  633. // initialize a CRun object and add it to the list.
  634. //
  635. // Arguments: [ptszJobName] - the short job name to pass to CRun.
  636. // [pRunList] - a pointer to a run list object.
  637. //
  638. // Returns: S_OK if the event trigger is found, S_FALSE if not, or a
  639. // fatal error code.
  640. //
  641. // Assumes: Triggers have already been loaded.
  642. //
  643. //-----------------------------------------------------------------------------
  644. HRESULT
  645. CJob::IfLogonJobAddToList(LPTSTR ptszJobName, CRunList * pRunList,
  646. CIdleRunList * pIdleWaitList)
  647. {
  648. return IfEventJobAddToList(TASK_EVENT_TRIGGER_AT_LOGON, ptszJobName,
  649. pRunList, pIdleWaitList);
  650. }
  651. //+----------------------------------------------------------------------------
  652. //
  653. // Member: CJob::IfIdleJobAddToList
  654. //
  655. // Synopsis: Check if the job has any idle triggers. If so, allocate and
  656. // initialize a CRun object and add it to the list.
  657. //
  658. // Arguments: [ptszJobName] - the short job name to pass to CRun.
  659. // [pIdleWaitList] - a pointer to a run list object.
  660. //
  661. // Returns: S_OK if the event trigger is found, S_FALSE if not, or a
  662. // fatal error code.
  663. //
  664. // Assumes: Triggers have already been loaded.
  665. //
  666. //-----------------------------------------------------------------------------
  667. HRESULT
  668. CJob::IfIdleJobAddToList(LPTSTR ptszJobName, CIdleRunList * pIdleWaitList)
  669. {
  670. return IfEventJobAddToList(TASK_EVENT_TRIGGER_ON_IDLE, ptszJobName,
  671. NULL, pIdleWaitList);
  672. }
  673. //+----------------------------------------------------------------------------
  674. //
  675. // Member: CJob::Delete
  676. //
  677. // Synopsis: Remove the file object for this job.
  678. //
  679. //-----------------------------------------------------------------------------
  680. HRESULT
  681. CJob::Delete(void)
  682. {
  683. schDebugOut((DEB_ITRACE, "CJob:Delete on %S\n", m_ptszFileName));
  684. if (m_ptszFileName == NULL)
  685. {
  686. return E_INVALIDARG;
  687. }
  688. if (!DeleteFile(m_ptszFileName))
  689. {
  690. schDebugOut((DEB_ITRACE, "DeleteFile failed with error %d\n",
  691. GetLastError()));
  692. return HRESULT_FROM_WIN32(GetLastError());
  693. }
  694. return S_OK;
  695. }
  696. //+----------------------------------------------------------------------------
  697. //
  698. // CJob::IUnknown methods
  699. //
  700. //-----------------------------------------------------------------------------
  701. //+----------------------------------------------------------------------------
  702. //
  703. // Member: CJob::IUnknown::QueryInterface
  704. //
  705. // Synopsis: Returns requested interface pointer
  706. //
  707. //-----------------------------------------------------------------------------
  708. STDMETHODIMP
  709. CJob::QueryInterface(REFIID riid, void ** ppvObject)
  710. {
  711. //schDebugOut((DEB_ITRACE, "CJob::QueryInterface\n"));
  712. if (IID_IUnknown == riid)
  713. {
  714. *ppvObject = (IUnknown *)(ITask *)this;
  715. }
  716. else if (IID_ITask == riid)
  717. {
  718. *ppvObject = (IUnknown *)(ITask *)this;
  719. }
  720. else if (IID_IScheduledWorkItem == riid)
  721. {
  722. *ppvObject = (IUnknown *)(IScheduledWorkItem *)this;
  723. }
  724. else if (IID_IPersist == riid)
  725. {
  726. *ppvObject = (IUnknown *)(IPersist *)this;
  727. }
  728. else if (IID_IPersistFile == riid)
  729. {
  730. *ppvObject = (IUnknown *)(IPersistFile *)this;
  731. }
  732. else if (IID_IProvideTaskPage == riid)
  733. {
  734. *ppvObject = (IUnknown *)(IProvideTaskPage *)this;
  735. }
  736. else
  737. {
  738. *ppvObject = NULL;
  739. return E_NOINTERFACE;
  740. }
  741. AddRef();
  742. return S_OK;
  743. }
  744. //+----------------------------------------------------------------------------
  745. //
  746. // Member: CJob::IUnknown::AddRef
  747. //
  748. // Synopsis: increments reference count
  749. //
  750. // Returns: the reference count
  751. //
  752. //-----------------------------------------------------------------------------
  753. STDMETHODIMP_(ULONG)
  754. CJob::AddRef(void)
  755. {
  756. //schDebugOut((DEB_ITRACE, "CJob::AddRef refcount going in %d\n", m_cReferences));
  757. return InterlockedIncrement((long *)&m_cReferences);
  758. }
  759. //+----------------------------------------------------------------------------
  760. //
  761. // Member: CJob::IUnknown::Release
  762. //
  763. // Synopsis: Decrements the object's reference count and frees it when
  764. // no longer referenced.
  765. //
  766. // Returns: the reference count
  767. //
  768. // Notes: BUGBUG: do we need to check the refcount on the triggers
  769. // before freeing the job object?
  770. //-----------------------------------------------------------------------------
  771. STDMETHODIMP_(ULONG)
  772. CJob::Release(void)
  773. {
  774. //schDebugOut((DEB_ITRACE, "CJob::Release ref count going in %d\n", m_cReferences));
  775. unsigned long uTmp;
  776. if ((uTmp = InterlockedDecrement((long *)&m_cReferences)) == 0)
  777. {
  778. delete this;
  779. }
  780. return uTmp;
  781. }
  782. //+----------------------------------------------------------------------------
  783. //
  784. // CJobCF - class factory for the Job object
  785. //
  786. //-----------------------------------------------------------------------------
  787. //+----------------------------------------------------------------------------
  788. //
  789. // Member: CJobCF::Create
  790. //
  791. // Synopsis: creates a new class factory object
  792. //
  793. //-----------------------------------------------------------------------------
  794. IClassFactory *
  795. CJobCF::Create(void)
  796. {
  797. //schDebugOut((DEB_ITRACE, "CJobCF::Create\n"));
  798. return new CJobCF;
  799. }
  800. //+----------------------------------------------------------------------------
  801. //
  802. // Member: CJobCF::CJobCF
  803. //
  804. // Synopsis: ctor
  805. //
  806. //-----------------------------------------------------------------------------
  807. CJobCF::CJobCF(void)
  808. {
  809. m_uRefs = 1;
  810. }
  811. //+----------------------------------------------------------------------------
  812. //
  813. // Member: CJobCF::~CJobCF
  814. //
  815. // Synopsis: dtor
  816. //
  817. //-----------------------------------------------------------------------------
  818. CJobCF::~CJobCF(void)
  819. {
  820. //schDebugOut((DEB_ITRACE, "~CJobCF: DLL ref count going in %d\n",
  821. // g_cDllRefs));
  822. }
  823. //+----------------------------------------------------------------------------
  824. //
  825. // Member: CJobCF::IUnknown::QueryInterface
  826. //
  827. // Synopsis: Returns requested interface pointer
  828. //
  829. //-----------------------------------------------------------------------------
  830. STDMETHODIMP
  831. CJobCF::QueryInterface(REFIID riid, void ** ppvObject)
  832. {
  833. //schDebugOut((DEB_ITRACE, "CJobCF::QueryInterface"));
  834. if (IID_IUnknown == riid)
  835. {
  836. *ppvObject = (IUnknown *)this;
  837. }
  838. else if (IsEqualIID(IID_IClassFactory, riid))
  839. {
  840. *ppvObject = (IClassFactory *)this;
  841. }
  842. else
  843. {
  844. *ppvObject = NULL;
  845. return E_NOINTERFACE;
  846. }
  847. AddRef();
  848. return S_OK;
  849. }
  850. //+----------------------------------------------------------------------------
  851. //
  852. // Member: CJobCF::IUnknown::AddRef
  853. //
  854. // Synopsis: increments reference count
  855. //
  856. // Returns: the reference count
  857. //
  858. //-----------------------------------------------------------------------------
  859. STDMETHODIMP_(ULONG)
  860. CJobCF::AddRef(void)
  861. {
  862. //schDebugOut((DEB_ITRACE, "CJobCF::AddRef\n"));
  863. return InterlockedIncrement((long *)&m_uRefs);
  864. }
  865. //+----------------------------------------------------------------------------
  866. //
  867. // Member: CJobCF::IUnknown::Release
  868. //
  869. // Synopsis: Decrements the object's reference count and frees it when
  870. // no longer referenced.
  871. //
  872. // Returns: the reference count
  873. //
  874. //-----------------------------------------------------------------------------
  875. STDMETHODIMP_(ULONG)
  876. CJobCF::Release(void)
  877. {
  878. //schDebugOut((DEB_ITRACE, "CJobCF::Release ref count going in %d\n",
  879. // m_uRefs));
  880. unsigned long uTmp;
  881. if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0)
  882. {
  883. delete this;
  884. }
  885. return uTmp;
  886. }
  887. //+----------------------------------------------------------------------------
  888. //
  889. // Member: CJobCF::IClassFactory::CreateInstance
  890. //
  891. // Synopsis: create an incore instance of the job class object
  892. //
  893. // Arguments: [pUnkOuter] - aggregator
  894. // [riid] - requested interface
  895. // [ppvObject] - receptor for itf ptr
  896. //
  897. // Returns: HRESULTS
  898. //
  899. //-----------------------------------------------------------------------------
  900. STDMETHODIMP
  901. CJobCF::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
  902. {
  903. //schDebugOut((DEB_ITRACE, "CJobCF::CreateInstance\n"));
  904. SCODE sc = S_OK;
  905. ITask * pJob = CJob::Create();
  906. if (pJob == NULL)
  907. {
  908. *ppvObject = NULL;
  909. return E_OUTOFMEMORY;
  910. }
  911. sc = pJob->QueryInterface(riid, ppvObject);
  912. if (FAILED(sc))
  913. {
  914. *ppvObject = NULL;
  915. return sc;
  916. }
  917. // we got a refcount of one when launched, and the above QI increments it
  918. // to 2, so call release to take it back to 1
  919. pJob->Release();
  920. return sc;
  921. }
  922. //+----------------------------------------------------------------------------
  923. //
  924. // Member: CJobCF::IClassFactory::LockServer
  925. //
  926. // Synopsis: Called with fLock set to TRUE to indicate that the server
  927. // should continue to run even if none of its objects are active
  928. //
  929. // Arguments: [fLock] - increment/decrement the instance count
  930. //
  931. // Returns: HRESULTS
  932. //
  933. // Notes: This function is a no-op since the server is in-proc.
  934. //
  935. //-----------------------------------------------------------------------------
  936. STDMETHODIMP
  937. CJobCF::LockServer(BOOL fLock)
  938. {
  939. return S_OK;
  940. }
  941. //+----------------------------------------------------------------------------
  942. //
  943. // CTrigger::IUnknown methods
  944. //
  945. // Notes: A trigger is not a first class COM object. They do not exist as
  946. // separate entities outside of Job objects; you cannot do a
  947. // CoCreateInstance on one and they do not go away when their ref
  948. // count goes to zero. When a job object is instanciated, its triggers
  949. // are also instanciated and they are freed from memory when the job
  950. // object is freed from memory. Trigger ref counting is used only to
  951. // prevent a client from deleting a trigger while it is holding a
  952. // pointer to that trigger.
  953. //
  954. // Note also that the containing job object is AddRef'd and Release'd
  955. // along with each trigger so that the job object will not be deleted
  956. // while clients are holding trigger pointers.
  957. //
  958. //-----------------------------------------------------------------------------
  959. //+----------------------------------------------------------------------------
  960. //
  961. // Member: CTrigger::IUnknown::QueryInterface
  962. //
  963. // Synopsis: Returns requested interface pointer
  964. //
  965. //-----------------------------------------------------------------------------
  966. STDMETHODIMP
  967. CTrigger::QueryInterface(REFIID riid, void ** ppvObject)
  968. {
  969. if (IID_IUnknown == riid)
  970. {
  971. *ppvObject = (IUnknown *)(ITaskTrigger *)this;
  972. }
  973. else
  974. if (IID_ITaskTrigger == riid)
  975. {
  976. *ppvObject = (IUnknown *)(ITaskTrigger *)this;
  977. }
  978. else
  979. {
  980. *ppvObject = NULL;
  981. return E_NOINTERFACE;
  982. }
  983. AddRef();
  984. return S_OK;
  985. }
  986. //+----------------------------------------------------------------------------
  987. //
  988. // Member: CTrigger::IUnknown::AddRef
  989. //
  990. // Synopsis: increments reference count
  991. //
  992. // Returns: the reference count
  993. //
  994. //-----------------------------------------------------------------------------
  995. STDMETHODIMP_(ULONG)
  996. CTrigger::AddRef(void)
  997. {
  998. return InterlockedIncrement((long *)&m_cReferences);
  999. }
  1000. //+----------------------------------------------------------------------------
  1001. //
  1002. // Member: CTrigger::IUnknown::Release
  1003. //
  1004. // Synopsis: Decrements the object's reference count.
  1005. //
  1006. // Returns: the reference count
  1007. //
  1008. //-----------------------------------------------------------------------------
  1009. STDMETHODIMP_(ULONG)
  1010. CTrigger::Release(void)
  1011. {
  1012. unsigned long uTmp;
  1013. if ((uTmp = InterlockedDecrement((long *)&m_cReferences)) == 0)
  1014. {
  1015. delete this;
  1016. }
  1017. return uTmp;
  1018. }
  1019. //+--------------------------------------------------------------------------
  1020. //
  1021. // Function: IsValidMonthlyDateTrigger
  1022. //
  1023. // Synopsis: Return TRUE if [pTrigger] has a valid combination of month
  1024. // and day bits set.
  1025. //
  1026. // History: 10-07-1997 DavidMun Created
  1027. //
  1028. //---------------------------------------------------------------------------
  1029. BOOL
  1030. IsValidMonthlyDateTrigger(
  1031. PTASK_TRIGGER pTrigger)
  1032. {
  1033. if (pTrigger->Type.MonthlyDate.rgfDays > JOB_RGFDAYS_MAX ||
  1034. pTrigger->Type.MonthlyDate.rgfMonths == 0 ||
  1035. pTrigger->Type.MonthlyDate.rgfMonths > JOB_RGFMONTHS_MAX)
  1036. {
  1037. return FALSE;
  1038. }
  1039. if (pTrigger->Type.MonthlyDate.rgfDays == 0)
  1040. {
  1041. //
  1042. // rgfDays must be non-zero.
  1043. //
  1044. return FALSE;
  1045. }
  1046. //
  1047. // More detailed testing to see if non-existent dates have been
  1048. // specified, for example: Feb. 30th, without specifying valid dates.
  1049. // That is, it is OK to specify invalid dates as long as there are
  1050. // valid dates. E.g., someone may want to specify: run on the 25th
  1051. // through 31st of every month. While that is acceptable, saying run
  1052. // only on Feb 31st is invalid.
  1053. //
  1054. if (pTrigger->Type.MonthlyDate.rgfDays & 0x40000000 &&
  1055. pTrigger->Type.MonthlyDate.rgfMonths & (TASK_FEBRUARY | TASK_APRIL |
  1056. TASK_JUNE | TASK_SEPTEMBER |
  1057. TASK_NOVEMBER) &&
  1058. !(pTrigger->Type.MonthlyDate.rgfMonths & ~(TASK_FEBRUARY |
  1059. TASK_APRIL |
  1060. TASK_JUNE |
  1061. TASK_SEPTEMBER |
  1062. TASK_NOVEMBER)))
  1063. {
  1064. //
  1065. // None of these months have a 31st day.
  1066. //
  1067. return FALSE;
  1068. }
  1069. if (pTrigger->Type.MonthlyDate.rgfDays & 0x20000000 &&
  1070. pTrigger->Type.MonthlyDate.rgfMonths & TASK_FEBRUARY &&
  1071. !(pTrigger->Type.MonthlyDate.rgfMonths & ~TASK_FEBRUARY))
  1072. {
  1073. //
  1074. // February does not have a 30th day. Allow for the specification
  1075. // of the 29th to run on leap year.
  1076. //
  1077. return FALSE;
  1078. }
  1079. return TRUE;
  1080. }