Source code of Windows XP (NT5)
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.

2316 lines
55 KiB

  1. /************************************************************************
  2. Copyright (c) 2000 - 2000 Microsoft Corporation
  3. Module Name :
  4. cmgr.cpp
  5. Abstract :
  6. implements CJobManager
  7. Author :
  8. Revision History :
  9. ***********************************************************************/
  10. #include "stdafx.h"
  11. #include <dbt.h>
  12. #include <ioevent.h>
  13. #include <malloc.h>
  14. #if !defined(BITS_V12_ON_NT4)
  15. #include "cmanager.tmh"
  16. #endif
  17. //
  18. // If DownloadCurrentFile fails with TRANSIENT_ERROR, the downloader will sleep for this many seconds.
  19. //
  20. const DWORD DELAY_AFTER_TRANSIENT_ERROR = 60;
  21. //
  22. // After a network-alive notification, we wait this long before attempting a download.
  23. //
  24. const UINT64 NETWORK_INIT_TOLERANCE_SECS = 60; // in seconds
  25. //------------------------------------------------------------------------
  26. CJobManager * g_Manager;
  27. extern SERVICE_STATUS_HANDLE ghServiceHandle;
  28. //------------------------------------------------------------------------
  29. void GetGuidString( GUID Guid, wchar_t pStr[] )
  30. {
  31. if (!StringFromGUID2( Guid, pStr, MAX_GUID_CHARS ))
  32. {
  33. wcsncpy( pStr, L"(can't convert)", MAX_GUID_CHARS );
  34. }
  35. }
  36. long g_cCalls = 0;
  37. //
  38. // COM uses this to determine when a DLL can unload safely.
  39. //
  40. long g_cLocks = 0;
  41. HRESULT GlobalLockServer(BOOL fLock)
  42. {
  43. #if !defined( BITS_V12_ON_NT4 )
  44. if (WPP_LEVEL_ENABLED(LogFlagRefCount))
  45. {
  46. LogInfo("%d", fLock );
  47. }
  48. #endif
  49. if (fLock)
  50. InterlockedIncrement(&g_cLocks);
  51. else
  52. InterlockedDecrement(&g_cLocks);
  53. return S_OK;
  54. }
  55. //
  56. // The job manager.
  57. //
  58. MANAGER_STATE g_ServiceState = MANAGER_INACTIVE;
  59. long g_ServiceInstance = 0;
  60. CJobManager::CJobManager() :
  61. m_ComId_0_5( 0 ),
  62. m_ComId_1_0( 0 ),
  63. m_ComId_1_5( 0 ),
  64. m_hWininet(NULL),
  65. m_pPD( NULL ),
  66. m_CurrentJob( NULL ),
  67. m_Users( m_TaskScheduler ),
  68. m_ExternalInterface( new CJobManagerExternal ),
  69. m_OldQmgrInterface( new COldQmgrInterface ),
  70. m_hQuantumTimer(NULL)
  71. {
  72. try
  73. {
  74. QMErrInfo ErrInfo;
  75. // use manual reset to insure that we are reseting it when
  76. // the downloader task is reinserted.
  77. m_hQuantumTimer = CreateWaitableTimer( NULL, TRUE, NULL );
  78. if ( !m_hQuantumTimer )
  79. {
  80. throw ComError( HRESULT_FROM_WIN32( GetLastError()));
  81. }
  82. #if !defined( BITS_V12_ON_NT4 )
  83. THROW_HRESULT( m_NetworkMonitor.Listen( NetworkChangeCallback, this ));
  84. #endif
  85. //
  86. // Create the HTTP downloader.
  87. //
  88. THROW_HRESULT( CreateHttpDownloader( &m_pPD, &ErrInfo ));
  89. GetExternalInterface()->SetInterfaceClass( this );
  90. GetOldExternalInterface()->SetInterfaceClass( this );
  91. }
  92. catch( ComError Error )
  93. {
  94. Cleanup();
  95. throw;
  96. }
  97. }
  98. HRESULT
  99. CJobManager::RegisterClassObjects()
  100. {
  101. try
  102. {
  103. g_ServiceState = MANAGER_ACTIVE;
  104. THROW_HRESULT(
  105. CoRegisterClassObject(CLSID_BackgroundCopyManager1_5,
  106. (LPUNKNOWN) static_cast<IClassFactory *>(GetExternalInterface() ),
  107. CLSCTX_LOCAL_SERVER,
  108. REGCLS_MULTIPLEUSE,
  109. &m_ComId_1_5 ) );
  110. THROW_HRESULT(
  111. CoRegisterClassObject(CLSID_BackgroundCopyManager,
  112. (LPUNKNOWN) static_cast<IClassFactory *>(GetExternalInterface() ),
  113. CLSCTX_LOCAL_SERVER,
  114. REGCLS_MULTIPLEUSE,
  115. &m_ComId_1_0 ) );
  116. THROW_HRESULT(
  117. CoRegisterClassObject(CLSID_BackgroundCopyQMgr,
  118. (LPUNKNOWN) static_cast<IClassFactory *>(GetOldExternalInterface() ),
  119. CLSCTX_LOCAL_SERVER,
  120. REGCLS_MULTIPLEUSE,
  121. &m_ComId_0_5 ) );
  122. return S_OK;
  123. }
  124. catch ( ComError error )
  125. {
  126. RevokeClassObjects();
  127. return error.Error();
  128. }
  129. }
  130. void
  131. CJobManager::RevokeClassObjects()
  132. {
  133. if (m_ComId_1_5)
  134. {
  135. CoRevokeClassObject( m_ComId_1_5 );
  136. m_ComId_1_5 = 0;
  137. }
  138. if (m_ComId_1_0)
  139. {
  140. CoRevokeClassObject( m_ComId_1_0 );
  141. m_ComId_1_0 = 0;
  142. }
  143. if (m_ComId_0_5)
  144. {
  145. CoRevokeClassObject( m_ComId_0_5 );
  146. m_ComId_0_5 = 0;
  147. }
  148. }
  149. void CJobManager::Cleanup()
  150. {
  151. RevokeClassObjects();
  152. if (m_pPD)
  153. {
  154. DeleteHttpDownloader( m_pPD );
  155. m_pPD = NULL;
  156. }
  157. if (m_hWininet)
  158. {
  159. FreeLibrary(m_hWininet);
  160. m_hWininet = NULL;
  161. }
  162. if ( m_hQuantumTimer )
  163. {
  164. CloseHandle( m_hQuantumTimer );
  165. m_hQuantumTimer = NULL;
  166. }
  167. LogInfo( "cleanup: marking manager inactive" );
  168. g_ServiceState = MANAGER_INACTIVE;
  169. }
  170. CJobManager::~CJobManager()
  171. {
  172. Cleanup();
  173. }
  174. void
  175. CJobManager::Shutdown()
  176. {
  177. g_ServiceState = MANAGER_TERMINATING;
  178. // 1. Block creation of new manager proxies.
  179. LogInfo( "shutdown: revoking class objects" );
  180. RevokeClassObjects();
  181. m_TaskScheduler.KillBackgroundTasks();
  182. #if !defined( BITS_V12_ON_NT4 )
  183. // 1.5 halt network-change notification
  184. m_NetworkMonitor.CancelListen();
  185. #endif
  186. // 5. Wait for calls in progress to finish.
  187. // releasing the reference for the hook thread, added during the constructor.
  188. //
  189. LogInfo("release: internal usage");
  190. NotifyInternalDelete();
  191. while (ActiveCallCount() > 0)
  192. {
  193. Sleep(50);
  194. }
  195. LogInfo( "shutdown: finished" );
  196. }
  197. void
  198. CJobManager::TaskThread()
  199. {
  200. HANDLE hWorkItemAvailable = m_TaskScheduler.GetWaitableObject();
  201. while (1)
  202. {
  203. DWORD dwRet = MsgWaitForMultipleObjectsEx( 1,
  204. &hWorkItemAvailable,
  205. INFINITE,
  206. QS_ALLINPUT,
  207. MWMO_ALERTABLE
  208. );
  209. switch ( dwRet )
  210. {
  211. case WAIT_OBJECT_0:
  212. m_TaskScheduler.DispatchWorkItem();
  213. break;
  214. case WAIT_OBJECT_0 + 1:
  215. // There is one or more window message available. Dispatch them
  216. MSG msg;
  217. while (PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
  218. {
  219. if ( msg.message == WM_QUIT )
  220. return;
  221. TranslateMessage(&msg);
  222. DispatchMessage(&msg);
  223. if (WaitForSingleObject(hWorkItemAvailable, 0) == WAIT_OBJECT_0)
  224. m_TaskScheduler.DispatchWorkItem();
  225. }
  226. break;
  227. case WAIT_IO_COMPLETION:
  228. //
  229. // an APC fired.
  230. //
  231. break;
  232. default:
  233. Sleep( 20 * 1000 );
  234. break;
  235. }
  236. }
  237. }
  238. //------------------------------------------------------------------------
  239. HRESULT
  240. CJobManager::CreateJob(
  241. LPCWSTR DisplayName,
  242. BG_JOB_TYPE Type,
  243. GUID Id,
  244. SidHandle sid,
  245. CJob ** ppJob,
  246. bool OldStyleJob
  247. )
  248. {
  249. HRESULT Hr = S_OK;
  250. *ppJob = NULL;
  251. //
  252. // create the job
  253. //
  254. try
  255. {
  256. if (Type != BG_JOB_TYPE_DOWNLOAD
  257. #if !defined(BITS_V12)
  258. && Type != BG_JOB_TYPE_UPLOAD
  259. && Type != BG_JOB_TYPE_UPLOAD_REPLY
  260. #endif
  261. )
  262. {
  263. throw ComError( E_NOTIMPL );
  264. }
  265. // Do not allow duplicate guids
  266. if ( m_OnlineJobs.Find( Id ) ||
  267. m_OfflineJobs.Find( Id ) )
  268. throw ComError( E_INVALIDARG );
  269. auto_ptr<WCHAR> TempDisplayName(NULL);
  270. DisplayName = TruncateString( DisplayName, MAX_DISPLAYNAME, TempDisplayName );
  271. ExtendMetadata();
  272. if (Type == BG_JOB_TYPE_DOWNLOAD)
  273. {
  274. *ppJob = new CJob( DisplayName, Type, Id, sid );
  275. if ( OldStyleJob )
  276. {
  277. COldGroupInterface *pOldGroup = new COldGroupInterface( *ppJob );
  278. (*ppJob)->SetOldExternalGroupInterface( pOldGroup );
  279. }
  280. }
  281. else
  282. {
  283. *ppJob = new CUploadJob( DisplayName, Type, Id, sid );
  284. }
  285. m_OnlineJobs.Add( *ppJob );
  286. m_TaskScheduler.InsertDelayedWorkItem( static_cast<CJobInactivityTimeout *>(*ppJob),
  287. g_GlobalInfo->m_JobInactivityTimeout
  288. );
  289. Serialize();
  290. }
  291. catch( ComError exception )
  292. {
  293. Hr = exception.Error();
  294. if (*ppJob)
  295. {
  296. (*ppJob)->UnlinkFromExternalInterfaces();
  297. delete *ppJob;
  298. *ppJob = NULL;
  299. }
  300. ShrinkMetadata();
  301. }
  302. return Hr;
  303. }
  304. HRESULT
  305. CJobManager::GetJob(
  306. REFGUID id,
  307. CJob ** ppJob
  308. )
  309. {
  310. *ppJob = NULL;
  311. CJob * job = m_OnlineJobs.Find( id );
  312. if (job != NULL)
  313. {
  314. if (S_OK != job->IsVisible())
  315. {
  316. return E_ACCESSDENIED;
  317. }
  318. job->UpdateLastAccessTime();
  319. *ppJob = job;
  320. return S_OK;
  321. }
  322. job = m_OfflineJobs.Find( id );
  323. if (job != NULL)
  324. {
  325. if (S_OK != job->IsVisible())
  326. {
  327. return E_ACCESSDENIED;
  328. }
  329. job->UpdateLastAccessTime();
  330. *ppJob = job;
  331. return S_OK;
  332. }
  333. return BG_E_NOT_FOUND;
  334. }
  335. HRESULT
  336. CJobManager::SuspendJob(
  337. CJob * job
  338. )
  339. {
  340. BG_JOB_STATE state = job->_GetState();
  341. switch (state)
  342. {
  343. case BG_JOB_STATE_SUSPENDED:
  344. {
  345. return S_OK;
  346. }
  347. case BG_JOB_STATE_CONNECTING:
  348. case BG_JOB_STATE_TRANSFERRING:
  349. InterruptDownload();
  350. // OK to fall through here
  351. case BG_JOB_STATE_TRANSFERRED:
  352. m_TaskScheduler.CancelWorkItem( static_cast<CJobCallbackItem *>(job) );
  353. // fall through
  354. case BG_JOB_STATE_QUEUED:
  355. case BG_JOB_STATE_TRANSIENT_ERROR:
  356. case BG_JOB_STATE_ERROR:
  357. m_TaskScheduler.CancelWorkItem( static_cast<CJobNoProgressItem *>(job) );
  358. job->SetState( BG_JOB_STATE_SUSPENDED );
  359. job->UpdateModificationTime();
  360. ScheduleAnotherGroup();
  361. return S_OK;
  362. case BG_JOB_STATE_CANCELLED:
  363. case BG_JOB_STATE_ACKNOWLEDGED:
  364. {
  365. return BG_E_INVALID_STATE;
  366. }
  367. default:
  368. {
  369. ASSERT( 0 );
  370. return S_OK;
  371. }
  372. }
  373. ASSERT( 0 );
  374. return S_OK;
  375. }
  376. bool
  377. CJobManager::IsUserLoggedOn( SidHandle sid )
  378. {
  379. CUser * user = m_Users.FindUser( sid, ANY_SESSION );
  380. if (!user)
  381. {
  382. return false;
  383. }
  384. user->DecrementRefCount();
  385. return true;
  386. }
  387. HRESULT
  388. CJobManager::CloneUserToken(
  389. SidHandle sid,
  390. DWORD session,
  391. HANDLE * pToken
  392. )
  393. {
  394. CUser * user = m_Users.FindUser( sid, session );
  395. if (!user)
  396. {
  397. return HRESULT_FROM_WIN32( ERROR_NOT_LOGGED_ON );
  398. }
  399. HRESULT hr = user->CopyToken( pToken );
  400. user->DecrementRefCount();
  401. return hr;
  402. }
  403. void CJobManager::TransferCurrentJob()
  404. {
  405. LogDl("***********START********************");
  406. if (m_TaskScheduler.LockWriter() )
  407. {
  408. m_TaskScheduler.AcknowledgeWorkItemCancel();
  409. return;
  410. }
  411. if (NULL == m_CurrentJob)
  412. {
  413. LogDl( "no more items" );
  414. }
  415. else
  416. {
  417. LogDl("transferring job %p", m_CurrentJob);
  418. m_CurrentJob->Transfer();
  419. }
  420. // It's OK if the item has already been completed.
  421. //
  422. m_TaskScheduler.CompleteWorkItem();
  423. ScheduleAnotherGroup();
  424. m_TaskScheduler.UnlockWriter();
  425. LogDl("************END*********************");
  426. }
  427. void CJobManager::MoveJobOffline(
  428. CJob * job
  429. )
  430. {
  431. m_OnlineJobs.Remove( job );
  432. m_OfflineJobs.Add( job );
  433. }
  434. void CJobManager::AppendOnline(
  435. CJob * job
  436. )
  437. //
  438. // moves a job to the end of the active list.
  439. //
  440. {
  441. if (!m_OnlineJobs.Remove( job ))
  442. {
  443. m_OfflineJobs.Remove( job );
  444. }
  445. m_OnlineJobs.Add( job );
  446. }
  447. void
  448. CJobManager::UpdateRemoteSizes(
  449. CUnknownFileSizeList *pUnknownFileSizeList,
  450. HANDLE hToken,
  451. QMErrInfo *pErrorInfo,
  452. const PROXY_SETTINGS *ProxySettings,
  453. const CCredentialsContainer * Credentials
  454. )
  455. {
  456. for(CUnknownFileSizeList::iterator iter = pUnknownFileSizeList->begin();
  457. iter != pUnknownFileSizeList->end(); iter++ )
  458. {
  459. CFile *pFile = iter->m_file;
  460. const WCHAR *pURL = (const WCHAR*)iter->m_url;
  461. pErrorInfo->Clear();
  462. LogDl( "Retrieving remote infomation for %ls", pURL );
  463. UINT64 FileSize;
  464. FILETIME FileTime;
  465. m_pPD->GetRemoteFileInformation(
  466. hToken,
  467. pURL,
  468. &FileSize,
  469. &FileTime,
  470. pErrorInfo,
  471. ProxySettings,
  472. Credentials
  473. );
  474. // If we can't get the size for one file, skip that file
  475. // and move to other files in the file.
  476. if (pErrorInfo->result != QM_FILE_DONE )
  477. {
  478. LogWarning( "Unable to retrieve remote information for %ls", pURL );
  479. continue;
  480. }
  481. // Update size in file.
  482. if ( m_TaskScheduler.LockWriter() )
  483. {
  484. m_TaskScheduler.AcknowledgeWorkItemCancel();
  485. pErrorInfo->result = QM_FILE_ABORTED;
  486. return;
  487. }
  488. pFile->SetBytesTotal( FileSize );
  489. //
  490. // A zero-length file will not be downloading any info, so it skips the normal path
  491. // for setting the correct creation time. Set it here.
  492. //
  493. if (FileSize == 0 &&
  494. (FileTime.dwLowDateTime != 0 || FileTime.dwHighDateTime != 0))
  495. {
  496. DWORD err = pFile->SetLocalFileTime( FileTime );
  497. if (err)
  498. {
  499. pErrorInfo->result = QM_FILE_TRANSIENT_ERROR;
  500. pErrorInfo->Set( SOURCE_QMGR_FILE, ERROR_STYLE_WIN32, err, NULL );
  501. }
  502. }
  503. m_TaskScheduler.UnlockWriter();
  504. }
  505. }
  506. void
  507. CJobManager::InterruptDownload()
  508. {
  509. LogInfo( "Interrupting download...\n");
  510. // Cancel the downloader workitem. CancelWorkItem
  511. // should ignore the request if the a download isn't running or pending.
  512. // Writer lock required!!!!!
  513. m_TaskScheduler.CancelWorkItem( this );
  514. // Now you must call ScheduleAnotherGroup, in order for the downloader to download anything.
  515. }
  516. void
  517. CJobManager::ScheduleAnotherGroup(
  518. bool fInsertNetworkDelay
  519. )
  520. /*++
  521. Description:
  522. Called by any thread to make sure that the highest-priority available job is being downloaded.
  523. It finds the highest-priority job that is in the QUEUED or RUNNING state.
  524. If this job is different from the current download job, the current job is cancelled and
  525. the new job is started. If no job is currently running, the new job is started.
  526. Starting the new job requires interrupting the download thread.
  527. At entry:
  528. m_CurrentJob is the job being downloaded
  529. If the downloader thread is active, then the manager's work item is in the scheduler.
  530. Otherwise it is not.
  531. At exit:
  532. the best available job to download is in m_CurrentJob, or NULL if none available
  533. If a job is available, the work item is in the task scheduler.
  534. --*/
  535. {
  536. CJob *pOldCurrentJob = m_CurrentJob;
  537. if (IsServiceShuttingDown())
  538. {
  539. LogInfo("no job scheduled; service is shutting down.");
  540. m_CurrentJob = NULL;
  541. }
  542. else
  543. {
  544. //
  545. // Choose the best candidate, which may be the old current job.
  546. //
  547. ChooseCurrentJob();
  548. #if DBG
  549. // do some validation checking on the queue
  550. ULONG RunningJobs = 0;
  551. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  552. {
  553. if ( iter->IsRunning() )
  554. RunningJobs++;
  555. }
  556. if (m_CurrentJob == NULL)
  557. {
  558. ASSERT( RunningJobs == 0 );
  559. }
  560. else
  561. {
  562. // zero if the download item is queued, one if running
  563. //
  564. ASSERT( RunningJobs == 0 || RunningJobs == 1 );
  565. }
  566. #endif
  567. }
  568. if (m_CurrentJob)
  569. {
  570. if ( m_CurrentJob != pOldCurrentJob )
  571. m_TaskScheduler.CancelWorkItem( this );
  572. if (!m_TaskScheduler.IsWorkItemInScheduler( this ))
  573. {
  574. if (fInsertNetworkDelay)
  575. {
  576. m_TaskScheduler.InsertDelayedWorkItem( this,
  577. NETWORK_INIT_TOLERANCE_SECS * NanoSec100PerSec );
  578. }
  579. else
  580. {
  581. m_TaskScheduler.InsertWorkItem( this );
  582. }
  583. }
  584. }
  585. else
  586. {
  587. m_TaskScheduler.CancelWorkItem( this );
  588. }
  589. }
  590. void
  591. CJobManager::ChooseCurrentJob()
  592. {
  593. CJob * NewJob = NULL;
  594. #if !defined( BITS_V12_ON_NT4 )
  595. if (m_NetworkMonitor.GetAddressCount() == 0)
  596. {
  597. NewJob = NULL;
  598. }
  599. else
  600. {
  601. #endif
  602. // Look at all the jobs and choose the best one
  603. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  604. {
  605. if (iter->IsRunnable())
  606. {
  607. BG_JOB_PRIORITY priority = iter->_GetPriority();
  608. if ( !NewJob || ( priority < NewJob->_GetPriority() ) )
  609. {
  610. NewJob = &(*iter);
  611. }
  612. }
  613. }
  614. #if !defined( BITS_V12_ON_NT4 )
  615. }
  616. #endif
  617. LogInfo( "scheduler: current=%p new=%p", m_CurrentJob, NewJob );
  618. if (m_CurrentJob == NewJob)
  619. {
  620. return;
  621. }
  622. if ( m_CurrentJob )
  623. {
  624. LogInfo( "scheduler: current priority %u", m_CurrentJob->_GetPriority() );
  625. //
  626. // an inactive job goes to QUEUED state if we have network connectivity,
  627. // TRANSIENT_ERROR state otherwise.
  628. //
  629. if ( m_CurrentJob->IsRunning() )
  630. {
  631. m_CurrentJob->MoveToInactiveState();
  632. m_CurrentJob->ScheduleModificationCallback();
  633. }
  634. }
  635. if ( NewJob )
  636. {
  637. LogInfo( "scheduler: new priority %u", NewJob->_GetPriority() );
  638. SetQuantumTimeout();
  639. }
  640. m_CurrentJob = NewJob;
  641. }
  642. void
  643. CJobManager::RetaskJob( CJob *pJob )
  644. {
  645. if ( pJob->IsRunning() )
  646. {
  647. InterruptDownload();
  648. }
  649. ScheduleAnotherGroup();
  650. }
  651. void CALLBACK
  652. CJobManager::NetworkChangeCallback(
  653. PVOID arg
  654. )
  655. {
  656. reinterpret_cast<CJobManager *>(arg)->OnNetworkChange();
  657. }
  658. void
  659. CJobManager::OnNetworkChange()
  660. {
  661. #if !defined( BITS_V12_ON_NT4 )
  662. if (g_ServiceState == MANAGER_TERMINATING)
  663. {
  664. LogInfo("network change: manager terminating");
  665. return;
  666. }
  667. LogInfo("network adapters changed: now %d active", m_NetworkMonitor.GetAddressCount());
  668. if (m_NetworkMonitor.GetAddressCount() > 0)
  669. {
  670. ReactivateTransientErrorJobs();
  671. }
  672. else
  673. {
  674. MarkJobsWithNetDisconnected();
  675. }
  676. {
  677. HoldWriterLock lock( &m_TaskScheduler );
  678. //
  679. // The previous proxy data is incorrect if we have switched from a corporate net to roaming or vice-versa..
  680. //
  681. g_ProxyCache->Clear();
  682. ScheduleAnotherGroup();
  683. }
  684. for (int i=1; i <= 3; ++i)
  685. {
  686. HRESULT hr;
  687. hr = m_NetworkMonitor.Listen( NetworkChangeCallback, this );
  688. if (SUCCEEDED(hr))
  689. {
  690. return;
  691. }
  692. LogError( "re-listen failed %x", hr);
  693. Sleep( 1000 );
  694. }
  695. #endif
  696. }
  697. void
  698. CJobManager::MarkJobsWithNetDisconnected()
  699. {
  700. HoldWriterLock lock( &m_TaskScheduler );
  701. for (CJobList::iterator iter=m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  702. {
  703. iter->OnNetworkDisconnect();
  704. }
  705. for (CJobList::iterator iter=m_OfflineJobs.begin(); iter != m_OfflineJobs.end(); ++iter)
  706. {
  707. iter->OnNetworkDisconnect();
  708. }
  709. ScheduleAnotherGroup();
  710. }
  711. void
  712. CJobManager::ReactivateTransientErrorJobs()
  713. {
  714. HoldWriterLock lock( &m_TaskScheduler );
  715. for (CJobList::iterator iter=m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  716. {
  717. iter->OnNetworkConnect();
  718. }
  719. for (CJobList::iterator iter=m_OfflineJobs.begin(); iter != m_OfflineJobs.end(); ++iter)
  720. {
  721. iter->OnNetworkConnect();
  722. }
  723. ScheduleAnotherGroup( true );
  724. }
  725. void
  726. CJobManager::UserLoggedOn(
  727. SidHandle sid
  728. )
  729. {
  730. HoldWriterLock LockHolder( &m_TaskScheduler );
  731. CJobList::iterator iter = m_OfflineJobs.begin();
  732. while (iter != m_OfflineJobs.end())
  733. {
  734. if (false == iter->IsOwner( sid ))
  735. {
  736. ++iter;
  737. continue;
  738. }
  739. LogInfo("manager : moving job %p to online list", &(*iter) );
  740. //
  741. // move the job to the online list.
  742. //
  743. CJobList::iterator next = iter;
  744. ++next;
  745. m_OfflineJobs.erase( iter );
  746. m_OnlineJobs.push_back( *iter );
  747. iter = next;
  748. }
  749. //
  750. // Make sure a group is running.
  751. //
  752. ScheduleAnotherGroup();
  753. }
  754. void
  755. CJobManager::UserLoggedOff(
  756. SidHandle sid
  757. )
  758. {
  759. bool fReschedule = false;
  760. HoldWriterLock LockHolder( &m_TaskScheduler );
  761. //
  762. // If a job is in progress and the user owns it, cancel it.
  763. //
  764. if (m_CurrentJob &&
  765. m_CurrentJob->IsOwner( sid ))
  766. {
  767. InterruptDownload();
  768. fReschedule = true;
  769. }
  770. //
  771. // Move all the user's jobs into the offline list.
  772. //
  773. CJobList::iterator iter = m_OnlineJobs.begin();
  774. while (iter != m_OnlineJobs.end())
  775. {
  776. //
  777. // Skip over other users' job.
  778. // Also skip over the currently downloading job, which will be handled by the download thread.
  779. //
  780. if (false == iter->IsOwner( sid ) ||
  781. &(*iter) == m_CurrentJob)
  782. {
  783. ++iter;
  784. continue;
  785. }
  786. LogInfo("manager : moving job %p to offline list", &(*iter) );
  787. /*
  788. this should't ever be true, since we skip over m_CurrentJob.
  789. if (iter->IsRunning() )
  790. {
  791. iter->MoveToInactiveState();
  792. }
  793. */ ASSERT( false == iter->IsRunning() );
  794. //
  795. // move the job to the online list.
  796. //
  797. CJobList::iterator next = iter;
  798. ++next;
  799. m_OnlineJobs.erase( iter );
  800. m_OfflineJobs.push_back( *iter );
  801. iter = next;
  802. }
  803. if (fReschedule)
  804. {
  805. ScheduleAnotherGroup();
  806. }
  807. }
  808. void
  809. CJobManager::ResetOnlineStatus(
  810. CJob *pJob,
  811. SidHandle sid
  812. )
  813. //
  814. // Called when a job owner changes. This fn checks whether the job needs to be moved
  815. // from the offline list to the online list. (If the job needs to move from the online
  816. // list to the offline list, the downloader thread will take care of it when the job
  817. // becomes the current job.)
  818. //
  819. {
  820. if ( IsUserLoggedOn( sid ) &&
  821. m_OfflineJobs.Remove( pJob ) )
  822. {
  823. m_OnlineJobs.Add( pJob );
  824. }
  825. }
  826. size_t
  827. CJobManager::MoveActiveJobToListEnd(
  828. CJob *pJob
  829. )
  830. {
  831. #if !defined( BITS_V12_ON_NT4 )
  832. if (m_NetworkMonitor.GetAddressCount() == 0)
  833. {
  834. // if the net is disconnected, no need to rearrange jobs.
  835. //
  836. return 1;
  837. }
  838. #endif
  839. ASSERT( m_TaskScheduler.IsWriter() );
  840. // Returns the number of queue or running jobs with a higher or same priority as ourself.
  841. ASSERT( pJob->IsRunnable() );
  842. size_t PossibleActiveJobs = 0;
  843. CJobList::iterator jobpos = m_OnlineJobs.end();
  844. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  845. {
  846. if ( iter->IsRunnable() &&
  847. iter->_GetPriority() <= pJob->_GetPriority() )
  848. {
  849. PossibleActiveJobs++;
  850. }
  851. if ( &(*iter) == pJob )
  852. {
  853. jobpos = iter;
  854. }
  855. }
  856. //
  857. // If the job is online, and another job can be pushed to the front,
  858. // push our job to the back.
  859. //
  860. if ( PossibleActiveJobs > 1 && jobpos != m_OnlineJobs.end())
  861. {
  862. // move job to the end of the list.
  863. m_OnlineJobs.erase( jobpos );
  864. m_OnlineJobs.push_back( *pJob );
  865. }
  866. else if (jobpos == m_OnlineJobs.end())
  867. {
  868. LogWarning("resuming an offline job");
  869. }
  870. return PossibleActiveJobs;
  871. }
  872. void
  873. CJobManager::SetQuantumTimeout()
  874. {
  875. LARGE_INTEGER QuantumTime;
  876. QuantumTime.QuadPart = -g_GlobalInfo->m_TimeQuantaLength;
  877. BOOL bResult =
  878. SetWaitableTimer(
  879. m_hQuantumTimer,
  880. &QuantumTime,
  881. 0,
  882. NULL,
  883. NULL,
  884. FALSE );
  885. ASSERT( bResult );
  886. }
  887. bool
  888. CJobManager::CheckForQuantumTimeout()
  889. {
  890. DWORD dwResult =
  891. WaitForSingleObject( m_hQuantumTimer, 0 );
  892. if ( WAIT_OBJECT_0 != dwResult)
  893. {
  894. // The timer didn't expire, so we have nothing to do.
  895. return false;
  896. }
  897. // The timeout fired so we need to move the current job
  898. // to the end of the list and signal the downloader to abort.
  899. // Do not cancel the current work item and do not change the current
  900. // job. Just let the downloader exit and let it call ScheduleAnotherGroup
  901. // to switch jobs if needed.
  902. // Special case. If only one RUNNING or QUEUED job exists in the list with
  903. // a priority >= our own, then we have no reason to switch tasks.
  904. // Just reset the timer and continue.
  905. bool fTookWriter = false;
  906. if (!m_TaskScheduler.IsWriter())
  907. {
  908. if (m_TaskScheduler.LockWriter() )
  909. {
  910. // cancelled; can't tell whether there is more than one job - assume the worst.
  911. return true;
  912. }
  913. fTookWriter = true;
  914. }
  915. ASSERT( m_CurrentJob );
  916. size_t PossibleActiveJobs = MoveActiveJobToListEnd( m_CurrentJob );
  917. if (fTookWriter)
  918. {
  919. m_TaskScheduler.UnlockWriter();
  920. }
  921. if ( 1 == PossibleActiveJobs )
  922. {
  923. LogInfo( "Time quantum fired, but nothing else can run. Ignoring and resetting timer.");
  924. SetQuantumTimeout();
  925. return false;
  926. }
  927. LogInfo( "Time quantum fired, moving job to the end of the queue.");
  928. return true; // signal downloader to abort
  929. }
  930. extern HMODULE g_hInstance;
  931. HRESULT
  932. CJobManager::GetErrorDescription(
  933. HRESULT hResult,
  934. DWORD LanguageId,
  935. LPWSTR *pErrorDescription )
  936. {
  937. // Do not allow 0 for now, untill propagation of thread error is investigated more.
  938. if (!LanguageId)
  939. {
  940. return E_INVALIDARG;
  941. }
  942. TCHAR *pBuffer = NULL;
  943. //
  944. // Use the following search path to find the message.
  945. //
  946. // 1. This DLL
  947. // 2. wininet.dll
  948. // 3. the system
  949. DWORD dwSize =
  950. FormatMessage(
  951. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
  952. g_hInstance,
  953. (DWORD)hResult,
  954. LanguageId,
  955. (LPTSTR)&pBuffer,
  956. 0,
  957. NULL );
  958. if ( !dwSize )
  959. {
  960. if ( GetLastError() == ERROR_OUTOFMEMORY )
  961. {
  962. return HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
  963. }
  964. {
  965. #if defined( USE_WININET )
  966. if (!m_hWininet)
  967. {
  968. m_hWininet =
  969. LoadLibraryEx( _T("wininet.dll"), NULL, LOAD_LIBRARY_AS_DATAFILE );
  970. }
  971. #else
  972. if (!m_hWininet)
  973. {
  974. m_hWininet =
  975. LoadLibraryEx( _T("winhttp.dll"), NULL, LOAD_LIBRARY_AS_DATAFILE );
  976. }
  977. #endif
  978. if ( m_hWininet )
  979. {
  980. dwSize =
  981. FormatMessage(
  982. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
  983. m_hWininet,
  984. (DWORD)(0x0000FFFF & (hResult)),
  985. LanguageId,
  986. (LPTSTR)&pBuffer,
  987. 0,
  988. NULL );
  989. if ( !dwSize && ( GetLastError() == ERROR_OUTOFMEMORY ) )
  990. {
  991. return HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
  992. }
  993. }
  994. }
  995. if ( !dwSize )
  996. {
  997. dwSize =
  998. FormatMessage(
  999. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  1000. NULL,
  1001. (DWORD)hResult,
  1002. LanguageId,
  1003. (LPTSTR)&pBuffer,
  1004. 0,
  1005. NULL );
  1006. if (!dwSize)
  1007. {
  1008. return HRESULT_FROM_WIN32( GetLastError() );
  1009. }
  1010. }
  1011. }
  1012. ++dwSize; // needs to include trailing NULL
  1013. ASSERT( pBuffer );
  1014. #if !defined(_UNICODE)
  1015. #error need to add ASCII to unicode conversion here
  1016. #else
  1017. *pErrorDescription = MidlCopyString( pBuffer );
  1018. LocalFree( pBuffer );
  1019. return (*pErrorDescription) ? S_OK : E_OUTOFMEMORY;
  1020. #endif
  1021. }
  1022. // {C82BF713-9940-4a12-9F1A-3AAEBD894EEA}
  1023. static const GUID PriorityQueuesStorage =
  1024. { 0xc82bf713, 0x9940, 0x4a12, { 0x9f, 0x1a, 0x3a, 0xae, 0xbd, 0x89, 0x4e, 0xea } };
  1025. HRESULT
  1026. CJobManager::Serialize()
  1027. {
  1028. //
  1029. // If this function changes, be sure that the metadata extension
  1030. // constants are adequate.
  1031. //
  1032. HRESULT hr;
  1033. try
  1034. {
  1035. //
  1036. // Serialization requires the thread to run in local-system context.
  1037. // If the thread is impersonating a COM client, it must revert.
  1038. //
  1039. CSaveThreadToken tok;
  1040. RevertToSelf();
  1041. // The service should automatically start if any groups
  1042. // are in the waiting/Running state or a logged off user has groups.
  1043. bool bAutomaticStart;
  1044. bAutomaticStart = (m_OnlineJobs.size() > 0) || (m_OfflineJobs.size() > 0);
  1045. LogSerial("Need to set service to %s start", bAutomaticStart ? "auto" : "manual" );
  1046. if ( bAutomaticStart )
  1047. {
  1048. // If we can't set the service to autostart, it's a fatal error.
  1049. // Fail the serialize at this point.
  1050. THROW_HRESULT( SetServiceStartup( bAutomaticStart ) );
  1051. }
  1052. CQmgrWriteStateFile StateFile( *this );
  1053. HANDLE hFile = StateFile.GetHandle();
  1054. SafeWriteBlockBegin( hFile, PriorityQueuesStorage );
  1055. m_OnlineJobs.Serialize( hFile );
  1056. m_OfflineJobs.Serialize( hFile );
  1057. SafeWriteBlockEnd( hFile, PriorityQueuesStorage );
  1058. StateFile.CommitFile();
  1059. if ( !bAutomaticStart )
  1060. {
  1061. // If we can't set the service to manual, its not a big deal. The worst
  1062. // that should happen is we start when we really don't need to.
  1063. hr = SetServiceStartup( bAutomaticStart );
  1064. if ( !SUCCEEDED( hr ) )
  1065. {
  1066. LogWarning("Couldn't set service startup to manual, ignoring. Hr 0x%8.8X", hr );
  1067. }
  1068. }
  1069. LogSerial( "finished");
  1070. hr = S_OK;
  1071. }
  1072. catch( ComError Error )
  1073. {
  1074. LogWarning("Error %u writing metadata\n", Error.Error() );
  1075. hr = Error.Error();
  1076. }
  1077. return hr;
  1078. }
  1079. HRESULT
  1080. CJobManager::Unserialize()
  1081. {
  1082. HRESULT hr;
  1083. try
  1084. {
  1085. BOOL fIncludeLogoffList;
  1086. CQmgrReadStateFile StateFile( *this );
  1087. HANDLE hFile = StateFile.GetHandle();
  1088. SafeReadBlockBegin( hFile, PriorityQueuesStorage );
  1089. //
  1090. // In the Serialize() code, the first is online jobs and the second is offline.
  1091. // When unserializing, the set of logged-in users is likely to be different, so
  1092. // we pull them all in and then lazily move them to the offline list.
  1093. //
  1094. m_OnlineJobs.Unserialize( hFile );
  1095. m_OnlineJobs.Unserialize( hFile );
  1096. SafeReadBlockEnd( hFile, PriorityQueuesStorage );
  1097. StateFile.ValidateEndOfFile();
  1098. hr = S_OK;
  1099. }
  1100. catch( ComError err )
  1101. {
  1102. //
  1103. // File corruption is reason to delete the group data and start fresh.
  1104. // Other errors, like out-of-memory, are not.
  1105. //
  1106. LogError( "Error %u reading metadata", err.Error() );
  1107. hr = err.Error();
  1108. if (hr == HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ) ||
  1109. hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ))
  1110. {
  1111. LogSerial("clearing job list");
  1112. m_OnlineJobs.Clear();
  1113. m_OfflineJobs.Clear();
  1114. hr = S_OK;
  1115. }
  1116. }
  1117. return hr;
  1118. }
  1119. void
  1120. CJobManager::OnDeviceLock(
  1121. const WCHAR *CanonicalVolume )
  1122. {
  1123. bool fChanged = false;
  1124. // Look at all the jobs and move the ones for this drive to the
  1125. // transient error state.
  1126. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  1127. {
  1128. fChanged |= iter->OnDeviceLock( CanonicalVolume );
  1129. }
  1130. if (fChanged)
  1131. {
  1132. ScheduleAnotherGroup();
  1133. Serialize();
  1134. }
  1135. }
  1136. void
  1137. CJobManager::OnDeviceUnlock(
  1138. const WCHAR *CanonicalVolume )
  1139. {
  1140. bool fChanged = false;
  1141. // Look at all the jobs and retry the ones that are in the transient error state
  1142. // do to this drive being locked.
  1143. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  1144. {
  1145. fChanged |= iter->OnDeviceUnlock( CanonicalVolume );
  1146. }
  1147. if (fChanged)
  1148. {
  1149. ScheduleAnotherGroup();
  1150. Serialize();
  1151. }
  1152. }
  1153. void
  1154. CJobManager::OnDiskChange(
  1155. const WCHAR *CanonicalVolume,
  1156. DWORD VolumeSerialNumber )
  1157. {
  1158. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  1159. {
  1160. iter->OnDiskChange( CanonicalVolume, VolumeSerialNumber );
  1161. }
  1162. ScheduleAnotherGroup();
  1163. Serialize();
  1164. }
  1165. void
  1166. CJobManager::OnDismount(
  1167. const WCHAR *CanonicalVolume )
  1168. {
  1169. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  1170. {
  1171. iter->OnDismount( CanonicalVolume );
  1172. }
  1173. ScheduleAnotherGroup();
  1174. Serialize();
  1175. }
  1176. BOOL
  1177. CJobList::Add(
  1178. CJob * job
  1179. )
  1180. //
  1181. // adds a single group to the list.
  1182. //
  1183. {
  1184. push_back( *job );
  1185. return TRUE;
  1186. }
  1187. CJob *
  1188. CJobList::Find(
  1189. REFGUID id
  1190. )
  1191. {
  1192. iterator iter;
  1193. for (iter=begin(); iter != end(); ++iter)
  1194. {
  1195. GUID jobid = iter->GetId();
  1196. if (id == jobid)
  1197. {
  1198. return &(*iter);
  1199. }
  1200. }
  1201. return NULL;
  1202. }
  1203. BOOL
  1204. CJobList::Remove(
  1205. CJob * job
  1206. )
  1207. //
  1208. // removes a single group to the list. Quite inefficient for large lists.
  1209. //
  1210. {
  1211. iterator iter;
  1212. for (iter=begin(); iter != end(); ++iter)
  1213. {
  1214. if (job == &(*iter))
  1215. {
  1216. erase( iter );
  1217. return TRUE;
  1218. }
  1219. }
  1220. return FALSE;
  1221. }
  1222. void
  1223. CJobList::Clear()
  1224. {
  1225. iterator iter;
  1226. while ((iter=begin()) != end())
  1227. {
  1228. CJob * job = &(*iter);
  1229. erase( iter );
  1230. job->Release();
  1231. }
  1232. }
  1233. // {005F4447-BDA9-44ba-9851-C47BB6C07ACE}
  1234. static const GUID GroupListStorageGuid =
  1235. { 0x5f4447, 0xbda9, 0x44ba, { 0x98, 0x51, 0xc4, 0x7b, 0xb6, 0xc0, 0x7a, 0xce } };
  1236. void
  1237. CJobList::Serialize( HANDLE hFile )
  1238. {
  1239. DWORD dwNumberOfGroups = 0;
  1240. dwNumberOfGroups = size();
  1241. SafeWriteBlockBegin( hFile, GroupListStorageGuid );
  1242. SafeWriteFile( hFile, dwNumberOfGroups );
  1243. iterator iter;
  1244. for (iter=begin(); iter != end(); ++iter)
  1245. {
  1246. iter->Serialize(hFile);
  1247. }
  1248. SafeWriteBlockEnd( hFile, GroupListStorageGuid );
  1249. }
  1250. void
  1251. CJobList::Unserialize(
  1252. HANDLE hFile
  1253. )
  1254. {
  1255. SafeReadBlockBegin( hFile, GroupListStorageGuid );
  1256. DWORD dwNumberOfGroups;
  1257. SafeReadFile( hFile, &dwNumberOfGroups );
  1258. for (int i = 0; i < dwNumberOfGroups; i++)
  1259. {
  1260. CJob * job = NULL;
  1261. try
  1262. {
  1263. job = CJob::UnserializeJob( hFile );
  1264. push_back( *job );
  1265. LogSerial( "added job %p to queue %p, priority %d",
  1266. job, this, job->_GetPriority() );
  1267. }
  1268. catch ( ComError err )
  1269. {
  1270. LogError( "error in joblist unserialize 0x%x", err.Error() );
  1271. throw;
  1272. }
  1273. }
  1274. SafeReadBlockEnd( hFile, GroupListStorageGuid );
  1275. }
  1276. CJobList::~CJobList()
  1277. {
  1278. ASSERT( g_ServiceState != MANAGER_ACTIVE );
  1279. iterator iter;
  1280. while ( (iter=begin()) != end() )
  1281. {
  1282. CJob * job = &(*iter);
  1283. LogInfo("deleting job %p", job );
  1284. iter.excise();
  1285. job->UnlinkFromExternalInterfaces();
  1286. delete job;
  1287. }
  1288. }
  1289. CJobManagerExternal::CJobManagerExternal() :
  1290. m_ServiceInstance( g_ServiceInstance ),
  1291. m_refs(1),
  1292. m_pJobManager( NULL )
  1293. {
  1294. }
  1295. STDMETHODIMP
  1296. CJobManagerExternal::QueryInterface(
  1297. REFIID iid,
  1298. void** ppvObject
  1299. )
  1300. {
  1301. BEGIN_EXTERNAL_FUNC
  1302. HRESULT Hr = S_OK;
  1303. *ppvObject = NULL;
  1304. if (iid == IID_IUnknown)
  1305. {
  1306. *ppvObject = static_cast<IBackgroundCopyManager *>(this);
  1307. LogInfo("mgr: QI for IUnknown");
  1308. ((IUnknown *)(*ppvObject))->AddRef();
  1309. }
  1310. else if (iid == IID_IBackgroundCopyManager)
  1311. {
  1312. *ppvObject = static_cast<IBackgroundCopyManager *>(this);
  1313. LogInfo("mgr: QI for IManager");
  1314. ((IUnknown *)(*ppvObject))->AddRef();
  1315. }
  1316. else if (iid == IID_IClassFactory)
  1317. {
  1318. *ppvObject = static_cast<IClassFactory *>(this);
  1319. LogInfo("mgr: QI for IFactory");
  1320. ((IUnknown *)(*ppvObject))->AddRef();
  1321. }
  1322. else if (iid == __uuidof(IBitsTest1))
  1323. {
  1324. *ppvObject = static_cast<IBitsTest1 *>(this);
  1325. LogInfo("mgr: QI for IFactory");
  1326. ((IUnknown *)(*ppvObject))->AddRef();
  1327. }
  1328. else
  1329. {
  1330. Hr = E_NOINTERFACE;
  1331. }
  1332. LogRef( "iid %!guid!, Hr %x", &iid, Hr );
  1333. return Hr;
  1334. END_EXTERNAL_FUNC
  1335. }
  1336. ULONG
  1337. CJobManagerExternal::AddRef()
  1338. {
  1339. BEGIN_EXTERNAL_FUNC;
  1340. ULONG newrefs = InterlockedIncrement(&m_refs);
  1341. LogRef( "new refs = %d", newrefs );
  1342. return newrefs;
  1343. END_EXTERNAL_FUNC;
  1344. }
  1345. ULONG
  1346. CJobManagerExternal::Release()
  1347. {
  1348. BEGIN_EXTERNAL_FUNC;
  1349. ULONG newrefs = InterlockedDecrement(&m_refs);
  1350. LogRef( "new refs = %d", newrefs );
  1351. if (newrefs == 0)
  1352. {
  1353. delete this;
  1354. }
  1355. return newrefs;
  1356. END_EXTERNAL_FUNC;
  1357. }
  1358. /************************************************************************************
  1359. IClassFactory Implementation
  1360. ************************************************************************************/
  1361. HRESULT CJobManagerExternal::CreateInstance(IUnknown* pUnkOuter, REFIID iid, void** ppvObject)
  1362. {
  1363. BEGIN_EXTERNAL_FUNC
  1364. HRESULT hr = S_OK;
  1365. if (pUnkOuter != NULL)
  1366. {
  1367. hr = CLASS_E_NOAGGREGATION;
  1368. }
  1369. else
  1370. {
  1371. if ((iid == IID_IBackgroundCopyManager) || (iid == IID_IUnknown))
  1372. {
  1373. hr = QueryInterface(iid, ppvObject);
  1374. }
  1375. else
  1376. {
  1377. hr = E_NOTIMPL;
  1378. }
  1379. }
  1380. LogRef( "iid %!guid!, Hr %x, object at %p", &iid, hr, *ppvObject );
  1381. return hr;
  1382. END_EXTERNAL_FUNC
  1383. }
  1384. HRESULT CJobManagerExternal::LockServer(BOOL fLock)
  1385. {
  1386. BEGIN_EXTERNAL_FUNC
  1387. LogRef( "LockServer(%d)", fLock);
  1388. return GlobalLockServer( fLock );
  1389. END_EXTERNAL_FUNC
  1390. }
  1391. /************************************************************************************
  1392. IBackgroundCopyManager Implementation
  1393. ************************************************************************************/
  1394. HRESULT STDMETHODCALLTYPE
  1395. CJobManagerExternal::CreateJobInternal (
  1396. /* [in] */ LPCWSTR DisplayName,
  1397. /* [in] */ BG_JOB_TYPE Type,
  1398. /* [out] */ GUID *pJobId,
  1399. /* [out] */ IBackgroundCopyJob **ppJob)
  1400. {
  1401. CLockedJobManagerWritePointer LockedJobManager(m_pJobManager );
  1402. LogPublicApiBegin( "DisplayName %S, Type %u", DisplayName, Type );
  1403. HRESULT Hr = S_OK;
  1404. CJob * job = NULL;
  1405. //
  1406. // create the job
  1407. //
  1408. try
  1409. {
  1410. Hr = LockedJobManager.ValidateAccess();
  1411. if (FAILED(Hr))
  1412. throw ComError( Hr );
  1413. //
  1414. // validate parameters
  1415. //
  1416. if (DisplayName == NULL ||
  1417. pJobId == NULL ||
  1418. ppJob == NULL)
  1419. {
  1420. throw ComError( E_INVALIDARG );
  1421. }
  1422. *ppJob = NULL;
  1423. GUID Id;
  1424. if (0 !=UuidCreate( &Id ))
  1425. {
  1426. throw ComError( HRESULT_FROM_WIN32( GetLastError() ));
  1427. }
  1428. THROW_HRESULT( LockedJobManager->CreateJob( DisplayName, Type, Id, GetThreadClientSid(), &job ));
  1429. *ppJob = job->GetExternalInterface();
  1430. (*ppJob)->AddRef();
  1431. *pJobId = Id;
  1432. Hr = S_OK;
  1433. }
  1434. catch( ComError exception )
  1435. {
  1436. Hr = exception.Error();
  1437. memset(pJobId, 0, sizeof(*pJobId) );
  1438. }
  1439. LogPublicApiEnd( "pJobId %p(%!guid!), ppJob %p(%p)",
  1440. pJobId, pJobId, ppJob, *ppJob );
  1441. return Hr;
  1442. }
  1443. HRESULT STDMETHODCALLTYPE
  1444. CJobManagerExternal::GetJobInternal(
  1445. /* [in] */ REFGUID jobID,
  1446. /* [out] */ IBackgroundCopyJob **ppJob)
  1447. {
  1448. CLockedJobManagerReadPointer LockedJobManager(m_pJobManager);
  1449. LogPublicApiBegin( "jobID %!guid!", &jobID );
  1450. HRESULT Hr = LockedJobManager.ValidateAccess();
  1451. if (SUCCEEDED( Hr ) )
  1452. {
  1453. Hr = BG_E_NOT_FOUND;
  1454. *ppJob = NULL;
  1455. CJob *pJob = NULL;
  1456. Hr = LockedJobManager->GetJob( jobID, &pJob );
  1457. if (SUCCEEDED(Hr))
  1458. {
  1459. *ppJob = pJob->GetExternalInterface();
  1460. (*ppJob)->AddRef();
  1461. Hr = S_OK;
  1462. }
  1463. }
  1464. LogPublicApiEnd( "jobID %!guid!, pJob %p", &jobID, *ppJob );
  1465. return Hr;
  1466. }
  1467. HRESULT STDMETHODCALLTYPE
  1468. CJobManagerExternal::EnumJobsInternal(
  1469. /* [in] */ DWORD dwFlags,
  1470. /* [out] */ IEnumBackgroundCopyJobs **ppEnum)
  1471. {
  1472. HRESULT Hr = S_OK;
  1473. CLockedJobManagerReadPointer LockedJobManager(m_pJobManager );
  1474. LogPublicApiBegin( "dwFlags %u, ppEnum %p", dwFlags, ppEnum );
  1475. *ppEnum = NULL;
  1476. CEnumJobs *pEnum = NULL;
  1477. try
  1478. {
  1479. THROW_HRESULT( LockedJobManager.ValidateAccess() );
  1480. if ( dwFlags & ~(BG_JOB_ENUM_ALL_USERS) )
  1481. {
  1482. throw ComError(E_NOTIMPL);
  1483. }
  1484. bool bHideJobs = !( dwFlags & BG_JOB_ENUM_ALL_USERS );
  1485. if (!bHideJobs)
  1486. THROW_HRESULT( DenyNonAdminAccess() );
  1487. SidHandle sid;
  1488. if (bHideJobs)
  1489. {
  1490. sid = GetThreadClientSid();
  1491. }
  1492. pEnum = new CEnumJobs;
  1493. for (CJobList::iterator iter = LockedJobManager->m_OnlineJobs.begin();
  1494. iter != LockedJobManager->m_OnlineJobs.end();
  1495. ++iter)
  1496. {
  1497. if ( bHideJobs )
  1498. {
  1499. if (!iter->IsOwner( sid ))
  1500. {
  1501. continue;
  1502. }
  1503. }
  1504. pEnum->Add( iter->GetExternalInterface() );
  1505. }
  1506. for (CJobList::iterator iter = LockedJobManager->m_OfflineJobs.begin();
  1507. iter != LockedJobManager->m_OfflineJobs.end();
  1508. ++iter)
  1509. {
  1510. if ( bHideJobs )
  1511. {
  1512. if (!iter->IsOwner( sid ))
  1513. {
  1514. continue;
  1515. }
  1516. }
  1517. pEnum->Add( iter->GetExternalInterface() );
  1518. }
  1519. *ppEnum = pEnum;
  1520. }
  1521. catch( ComError exception )
  1522. {
  1523. Hr = exception.Error();
  1524. SafeRelease( pEnum );
  1525. }
  1526. LogPublicApiEnd( "dwFlags %u, ppEnum %p(%p)", dwFlags, ppEnum, *ppEnum );
  1527. return Hr;
  1528. }
  1529. STDMETHODIMP
  1530. CJobManagerExternal::GetErrorDescriptionInternal(
  1531. HRESULT hResult,
  1532. DWORD LanguageId,
  1533. LPWSTR *pErrorDescription
  1534. )
  1535. {
  1536. HRESULT Hr = S_OK;
  1537. LogPublicApiBegin( "hResult %!winerr!, LanguageId %u, pErrorDescription %p", hResult, LanguageId, pErrorDescription );
  1538. *pErrorDescription = NULL;
  1539. Hr = DenyRemoteAccess();
  1540. if (SUCCEEDED(Hr))
  1541. {
  1542. Hr = g_Manager->GetErrorDescription( hResult, LanguageId, pErrorDescription );
  1543. }
  1544. LogPublicApiEnd( "hResult %!winerr!, LanguageId %u, pErrorDescription %p(%S)", hResult, LanguageId, pErrorDescription,
  1545. (*pErrorDescription ? *pErrorDescription : L"NULL") );
  1546. return Hr;
  1547. }
  1548. STDMETHODIMP
  1549. CJobManagerExternal::GetBitsDllPath(
  1550. LPWSTR *pVal
  1551. )
  1552. {
  1553. HRESULT Hr = S_OK;
  1554. *pVal = NULL;
  1555. Hr = DenyRemoteAccess();
  1556. if (SUCCEEDED(Hr))
  1557. {
  1558. *pVal = (LPWSTR) CoTaskMemAlloc((1+MAX_PATH)*sizeof(wchar_t));
  1559. if (*pVal == NULL)
  1560. {
  1561. Hr = E_OUTOFMEMORY;
  1562. }
  1563. else
  1564. {
  1565. if (!GetModuleFileName( g_hInstance, *pVal, 1+MAX_PATH))
  1566. {
  1567. Hr = HRESULT_FROM_WIN32( GetLastError() );
  1568. CoTaskMemFree( *pVal );
  1569. }
  1570. }
  1571. }
  1572. LogPublicApiEnd( "hResult %!winerr!, path (%S)", Hr, (*pVal ? *pVal : L"NULL") );
  1573. return Hr;
  1574. }
  1575. #if !defined( BITS_V12_ON_NT4 )
  1576. CDeviceNotificationController::~CDeviceNotificationController()
  1577. {
  1578. for( CHandleToNotify::iterator iter = m_HandleToNotify.begin(); iter != m_HandleToNotify.end(); iter++ )
  1579. {
  1580. UnregisterDeviceNotification( iter->second->m_hDeviceNotify );
  1581. delete iter->second;
  1582. }
  1583. }
  1584. void
  1585. CDeviceNotificationController::DeleteNotify(
  1586. CDriveNotify *pNotify
  1587. )
  1588. {
  1589. RTL_VERIFY( m_HandleToNotify.erase( pNotify->m_hDeviceNotify ) );
  1590. RTL_VERIFY( m_CanonicalVolumeToNotify.erase( pNotify->m_CanonicalName ) );
  1591. UnregisterDeviceNotification( pNotify->m_hDeviceNotify );
  1592. ASSERT( NULL != pNotify );
  1593. delete pNotify;
  1594. }
  1595. DWORD
  1596. CDeviceNotificationController::OnDeviceEvent(
  1597. DWORD dwEventType,
  1598. LPVOID lpEventData )
  1599. {
  1600. switch( dwEventType )
  1601. {
  1602. case DBT_CUSTOMEVENT:
  1603. {
  1604. PDEV_BROADCAST_HANDLE pdev = (PDEV_BROADCAST_HANDLE)lpEventData;
  1605. LogInfo( "Received DBT_CUSTOMEVENT(%!guid!) event for handle %p",
  1606. &pdev->dbch_eventguid, pdev->dbch_hdevnotify );
  1607. CHandleToNotify::iterator iter = m_HandleToNotify.find( pdev->dbch_hdevnotify );
  1608. if ( m_HandleToNotify.end() == iter )
  1609. {
  1610. LogWarning("DBT_CUSTOMEVENT(%!guid!) received for unknown notify handle %p",
  1611. &pdev->dbch_eventguid, pdev->dbch_hdevnotify );
  1612. return NO_ERROR;
  1613. }
  1614. CDriveNotify *pNotify = iter->second;
  1615. ASSERT( pNotify );
  1616. if ( ( GUID_IO_VOLUME_LOCK == pdev->dbch_eventguid ) ||
  1617. ( GUID_IO_VOLUME_DISMOUNT == pdev->dbch_eventguid ) )
  1618. {
  1619. LogInfo( "GUID_IO_VOLUME_LOCK or _VOLUME_DISMOUNT received for drive %ls, new lockcount %d",
  1620. (const WCHAR*)pNotify->m_CanonicalName, pNotify->m_LockCount + 1 );
  1621. if ( !(pNotify->m_LockCount++) )
  1622. OnDeviceLock( pNotify->m_CanonicalName );
  1623. return NO_ERROR;
  1624. }
  1625. else if ( ( GUID_IO_VOLUME_UNLOCK == pdev->dbch_eventguid ) ||
  1626. ( GUID_IO_VOLUME_LOCK_FAILED == pdev->dbch_eventguid ) ||
  1627. ( GUID_IO_VOLUME_DISMOUNT_FAILED == pdev->dbch_eventguid ) )
  1628. {
  1629. LogInfo( "GUID_IO_VOLUME_UNLOCK, _LOCK_FAILED or _DISMOUNT_FAILED received for drive %ls",
  1630. (const WCHAR*)pNotify->m_CanonicalName);
  1631. // UNLOCK, LOCK_FAILED and DISMOUNT_FAILED all mean that the volume is unlocked
  1632. // regardless of the number of previous locks/dismounts.
  1633. pNotify->m_LockCount = 0;
  1634. OnDeviceUnlock( pNotify->m_CanonicalName );
  1635. return NO_ERROR;
  1636. }
  1637. else
  1638. {
  1639. LogWarning("Received unknown DBT_CUSTOMEVENT(%!guid!) event for handle %p",
  1640. &pdev->dbch_eventguid, pdev->dbch_hdevnotify );
  1641. return NO_ERROR;
  1642. }
  1643. }
  1644. case DBT_DEVICEQUERYREMOVE:
  1645. case DBT_DEVICEQUERYREMOVEFAILED:
  1646. case DBT_DEVICEREMOVEPENDING:
  1647. case DBT_DEVICEREMOVECOMPLETE:
  1648. {
  1649. PDEV_BROADCAST_HANDLE pdev = (PDEV_BROADCAST_HANDLE)lpEventData;
  1650. LogInfo( "Received devicechange event %u received for handle %p", dwEventType, pdev->dbch_hdevnotify );
  1651. CHandleToNotify::iterator iter = m_HandleToNotify.find( pdev->dbch_hdevnotify );
  1652. if ( m_HandleToNotify.end() == iter )
  1653. {
  1654. LogWarning("device change event received for unknown notify handle %p", pdev->dbch_hdevnotify );
  1655. return NO_ERROR;
  1656. }
  1657. CDriveNotify *pNotify = iter->second;
  1658. ASSERT( pNotify );
  1659. switch( dwEventType )
  1660. {
  1661. case DBT_DEVICEQUERYREMOVE:
  1662. LogInfo( "DBT_DEVICEQUERYREMOVE received for drive %ls, new lockcount %d",
  1663. (const WCHAR*)pNotify->m_CanonicalName, pNotify->m_LockCount + 1 );
  1664. if ( !(pNotify->m_LockCount++) )
  1665. OnDeviceLock( pNotify->m_CanonicalName );
  1666. return NO_ERROR;
  1667. case DBT_DEVICEQUERYREMOVEFAILED:
  1668. LogInfo( "DBT_DEVICEQUERYREMOVEFAILED received for drive %ls, new lockcount %d",
  1669. (const WCHAR*)pNotify->m_CanonicalName, (pNotify->m_LockCount <= 0) ? 0 : (pNotify->m_LockCount - 1) );
  1670. if ( 1 == pNotify->m_LockCount )
  1671. OnDeviceUnlock( pNotify->m_CanonicalName );
  1672. if ( pNotify->m_LockCount <= 1 )
  1673. pNotify->m_LockCount = 0;
  1674. return NO_ERROR;
  1675. case DBT_DEVICEREMOVECOMPLETE:
  1676. case DBT_DEVICEREMOVEPENDING:
  1677. LogInfo( "DBT_DEVICEREMOVECOMPLETE or DBT_DEVICEREMOVEPENDING received for drive %ls, failing jobs",
  1678. ( const WCHAR*) pNotify->m_CanonicalName );
  1679. OnDismount( pNotify->m_CanonicalName );
  1680. DeleteNotify( pNotify );
  1681. return NO_ERROR;
  1682. default:
  1683. ASSERT(0);
  1684. return NO_ERROR;
  1685. }
  1686. }
  1687. default:
  1688. LogInfo( "Unknown device event %u", dwEventType );
  1689. return NO_ERROR;
  1690. }
  1691. }
  1692. HRESULT
  1693. CDeviceNotificationController::IsVolumeLocked(
  1694. const WCHAR *CanonicalVolume
  1695. )
  1696. {
  1697. HRESULT Hr = S_OK;
  1698. try
  1699. {
  1700. CCanonicalVolumeToNotify::iterator iter = m_CanonicalVolumeToNotify.find( CanonicalVolume );
  1701. if ( m_CanonicalVolumeToNotify.end() == iter )
  1702. {
  1703. LogInfo( "Canonical volume %ls has not been registered, register now\n", CanonicalVolume );
  1704. //
  1705. // Register for device-lock notification. If it fails, it is of small consequence:
  1706. // if CHKDSK and BITS try to access a file simultanteously, the job would go into
  1707. // ERROR state instead of TRANSIENT_ERROR state.
  1708. //
  1709. Hr = RegisterNotification( CanonicalVolume );
  1710. if (FAILED(Hr))
  1711. {
  1712. LogWarning("unable to register: 0x%x", Hr);
  1713. }
  1714. Hr = S_OK;
  1715. }
  1716. else
  1717. {
  1718. CDriveNotify *pNotify = iter->second;
  1719. if ( pNotify->m_LockCount )
  1720. throw ComError( BG_E_DESTINATION_LOCKED );
  1721. }
  1722. }
  1723. catch(ComError Error)
  1724. {
  1725. Hr = Error.Error();
  1726. }
  1727. return Hr;
  1728. }
  1729. HRESULT
  1730. CDeviceNotificationController::RegisterNotification(
  1731. const WCHAR *CanonicalVolume
  1732. )
  1733. {
  1734. HRESULT Hr = S_OK;
  1735. HANDLE hDriveHandle = INVALID_HANDLE_VALUE;
  1736. HDEVNOTIFY hNotify = NULL;
  1737. CDriveNotify *pNotify = NULL;
  1738. StringHandle wCanonicalVolume;
  1739. try
  1740. {
  1741. wCanonicalVolume = CanonicalVolume;
  1742. CCanonicalVolumeToNotify::iterator iter = m_CanonicalVolumeToNotify.find( wCanonicalVolume );
  1743. if ( m_CanonicalVolumeToNotify.end() != iter )
  1744. {
  1745. LogInfo( "Canonical volume %ls has already been registered, nothing to do.", CanonicalVolume );
  1746. return S_OK;
  1747. }
  1748. // Need to remove the trailing / from the volume name.
  1749. ASSERTMSG( "Canonical name has an unexpected size", wCanonicalVolume.Size() );
  1750. CAutoString TempVolumePath = CAutoString( CopyString( wCanonicalVolume ));
  1751. ASSERT( wCanonicalVolume.Size() > 0 );
  1752. TempVolumePath.get()[ wCanonicalVolume.Size() - 1 ] = L'\0';
  1753. hDriveHandle =
  1754. CreateFile( TempVolumePath.get(),
  1755. GENERIC_READ,
  1756. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1757. NULL,
  1758. OPEN_EXISTING,
  1759. 0,
  1760. NULL );
  1761. if ( INVALID_HANDLE_VALUE == hDriveHandle )
  1762. throw ComError( HRESULT_FROM_WIN32( GetLastError() ) );
  1763. DEV_BROADCAST_HANDLE DbtHandle;
  1764. memset( &DbtHandle, 0, sizeof(DbtHandle) );
  1765. DbtHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
  1766. DbtHandle.dbch_devicetype = DBT_DEVTYP_HANDLE;
  1767. DbtHandle.dbch_handle = hDriveHandle;
  1768. hNotify =
  1769. RegisterDeviceNotification( (HANDLE) ghServiceHandle,
  1770. &DbtHandle,
  1771. DEVICE_NOTIFY_SERVICE_HANDLE );
  1772. if ( !hNotify )
  1773. throw ComError( HRESULT_FROM_WIN32( GetLastError() ) );
  1774. CloseHandle( hDriveHandle );
  1775. hDriveHandle = NULL;
  1776. pNotify = new CDriveNotify( hNotify, CanonicalVolume );
  1777. if ( !pNotify )
  1778. throw ComError( E_OUTOFMEMORY );
  1779. RTL_VERIFY( m_CanonicalVolumeToNotify.insert( CCanonicalVolumeToNotify::value_type( wCanonicalVolume, pNotify ) ).second );
  1780. RTL_VERIFY( m_HandleToNotify.insert( CHandleToNotify::value_type( hNotify, pNotify ) ).second );
  1781. }
  1782. catch(ComError Error)
  1783. {
  1784. Hr = Error.Error();
  1785. }
  1786. if ( FAILED(Hr) )
  1787. {
  1788. if ( hNotify )
  1789. UnregisterDeviceNotification( hNotify );
  1790. if ( hDriveHandle != INVALID_HANDLE_VALUE )
  1791. CloseHandle( hDriveHandle );
  1792. if ( pNotify )
  1793. {
  1794. m_CanonicalVolumeToNotify.erase( wCanonicalVolume );
  1795. m_HandleToNotify.erase( hNotify );
  1796. delete pNotify;
  1797. }
  1798. }
  1799. return Hr;
  1800. }
  1801. #endif
  1802. HRESULT
  1803. SessionLogonCallback(
  1804. DWORD SessionId
  1805. )
  1806. {
  1807. return g_Manager->m_Users.LogonSession( SessionId );
  1808. }
  1809. HRESULT
  1810. SessionLogoffCallback(
  1811. DWORD SessionId
  1812. )
  1813. {
  1814. return g_Manager->m_Users.LogoffSession( SessionId );
  1815. }
  1816. #if !defined( BITS_V12_ON_NT4 )
  1817. DWORD
  1818. DeviceEventCallback(
  1819. DWORD dwEventType,
  1820. LPVOID lpEventData
  1821. )
  1822. {
  1823. return g_Manager->OnDeviceEvent( dwEventType, lpEventData );
  1824. }
  1825. #endif