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.

589 lines
18 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1995 - 1995.
  5. //
  6. // File: dllmain.hxx
  7. //
  8. // Contents: DLL initialization entrypoint and global variables
  9. //
  10. // History: 4-Apr-95 BruceFo Created
  11. //
  12. //--------------------------------------------------------------------------
  13. #include "headers.hxx"
  14. #pragma hdrstop
  15. #include "resource.h"
  16. #include "critsec.hxx"
  17. #include "cache.hxx"
  18. #include "strhash.hxx"
  19. #include "dllmain.hxx"
  20. #include "util.hxx"
  21. //--------------------------------------------------------------------------
  22. // Globals used elsewhere
  23. UINT g_NonOLEDLLRefs = 0;
  24. HINSTANCE g_hInstance = NULL;
  25. BOOL g_fSharingEnabled = FALSE; // until proven otherwise
  26. UINT g_uiMaxUsers = 0; // max number of users based on product type
  27. WCHAR g_szAdminShare[] = L"ADMIN$";
  28. WCHAR g_szIpcShare[] = L"IPC$";
  29. //--------------------------------------------------------------------------
  30. // Globals used only in this file
  31. CRITICAL_SECTION g_csOneTimeInit;
  32. BOOL g_fOneTimeInitDone = FALSE;
  33. // Note: the total wait time is:
  34. // min( (the wait hint / WAIT_FRACTION), MAX_WAIT_PERIOD ) * MAX_WAIT_COUNT
  35. // In the case of the server, with a 30 second hint, about 2 minutes, 30 sec.
  36. #define WAIT_FRACTION 4 // the fraction of the hint to wait
  37. #define MAX_WAIT_COUNT 20 // the maximum number of wait failures to tolerate before quiting
  38. #define MAX_WAIT_PERIOD 15000L // 15 seconds
  39. #define WAIT_TO_BEGIN_GRANULARITY 4000 // 4 seconds
  40. #define MAX_WAIT_TO_BEGIN 90000L // 90 seconds: time to wait before giving up that it will ever start
  41. //--------------------------------------------------------------------------
  42. // Debugging
  43. #define SZ_DEBUGINI "ccshell.ini"
  44. #define SZ_DEBUGSECTION "Sharing"
  45. #define SZ_MODULE "Sharing"
  46. #define DECLARE_DEBUG 1
  47. #include <debug.h>
  48. //--------------------------------------------------------------------------
  49. VOID
  50. InitializeShareCache(
  51. VOID
  52. );
  53. DWORD WINAPI
  54. WaitForServerThread(
  55. IN LPVOID ThreadParameter
  56. );
  57. BOOL
  58. CheckServiceController(
  59. VOID
  60. );
  61. BOOL
  62. ServerConfiguredToStart(
  63. SC_HANDLE hScManager
  64. );
  65. BOOL
  66. WaitForServerToBeginStarting(
  67. SC_HANDLE hService,
  68. LPSERVICE_STATUS pServiceStatus // so we don't need to re-query on successful return
  69. );
  70. //--------------------------------------------------------------------------
  71. //+--------------------------------------------------------------------------
  72. //
  73. // Function: DllMain
  74. //
  75. // Synopsis: Win32 DLL initialization function
  76. //
  77. // Arguments: [hInstance] - Handle to this dll
  78. // [dwReason] - Reason this function was called. Can be
  79. // Process/Thread Attach/Detach.
  80. //
  81. // Returns: BOOL - TRUE if no error. FALSE otherwise
  82. //
  83. // History: 4-Apr-95 BruceFo Created
  84. //
  85. //---------------------------------------------------------------------------
  86. extern "C"
  87. BOOL
  88. DllMain(
  89. HINSTANCE hInstance,
  90. DWORD dwReason,
  91. LPVOID /*lpReserved*/
  92. )
  93. {
  94. switch (dwReason)
  95. {
  96. case DLL_PROCESS_ATTACH:
  97. {
  98. appDebugOut((DEB_TRACE, "ntshrui.dll: DllMain enter\n"));
  99. // Disable thread notification from OS
  100. DisableThreadLibraryCalls(hInstance);
  101. CcshellGetDebugFlags();
  102. g_hInstance = hInstance;
  103. // Be specific about where to get your manifest from - it's in the dll, at the
  104. // default resource ID for the shell, which is 123.
  105. SHFusionInitializeFromModuleID(hInstance, SHFUSION_DEFAULT_RESOURCE_ID);
  106. InitCommonControls(); // get up/down control
  107. InitializeCriticalSection(&g_csOneTimeInit);
  108. break;
  109. }
  110. case DLL_PROCESS_DETACH:
  111. appDebugOut((DEB_TRACE, "ntshrui.dll: DllMain leave\n"));
  112. SHFusionUninitialize();
  113. DeleteCriticalSection(&g_csOneTimeInit);
  114. break;
  115. }
  116. return TRUE;
  117. }
  118. //+-------------------------------------------------------------------------
  119. //
  120. // Function: OneTimeInit
  121. //
  122. // Synopsis: Initialization code: check if SMB server is running
  123. //
  124. // History: 21-Apr-95 BruceFo Created
  125. //
  126. // Returns:
  127. // Note: We don't want to do this in the DLL initialization code, so
  128. // we call it for every entrypoint, namely DllGetClassObject,
  129. // DllCanUnloadNow, and IsPathShared.
  130. //
  131. //--------------------------------------------------------------------------
  132. VOID
  133. OneTimeInit(
  134. IN BOOL bDialog // TRUE if for dialog API
  135. )
  136. {
  137. // quick check; no critical section
  138. if (g_fOneTimeInitDone)
  139. {
  140. return;
  141. }
  142. {
  143. CTakeCriticalSection t(&g_csOneTimeInit); // scope it
  144. // Since there wasn't a critical section on the above check, multiple
  145. // threads might have fallen through to the critical section taking,
  146. // and wait. After the one-time initialization is complete, the
  147. // first thread sets g_fOneTimeInitDone to TRUE and leaves the
  148. // critical section. At this point, the other threads will wake up
  149. // here. Do the check again, and return if another thread did the
  150. // initialization.
  151. if (g_fOneTimeInitDone)
  152. {
  153. return;
  154. }
  155. // Now, do the actual initialization
  156. if (!bDialog)
  157. {
  158. // First, determine if the server is running. If not, see if
  159. // we should wait for it. If we wait and it still isn't
  160. // started, then give up.
  161. InitializeShareCache();
  162. }
  163. // if it is a dialog call, then we don't load up the cache because
  164. // that's the first thing the dialog code does.
  165. // Determine the maximum number of users
  166. g_uiMaxUsers = IsOS(OS_ANYSERVER) ? MAX_USERS_ON_SERVER : MAX_USERS_ON_WORKSTATION;
  167. g_fOneTimeInitDone = TRUE; // set this *last*
  168. }
  169. }
  170. VOID
  171. InitializeShareCache(
  172. VOID
  173. )
  174. /*++
  175. Routine Description:
  176. This routine initializes the share cache. It determines if the LanMan
  177. server is running. If it is, great.
  178. If it isn't, determine if it is starting. If so, wait for a while. After
  179. a while, if it still hasn't started, then give up and assume it's hung.
  180. If the server isn't starting, then determine if the configuration says it
  181. is going to start (set to autostart). If so, wait to see if it ever goes
  182. into the "start pending" state. Give up after a reasonable period. If it
  183. does go into this state, then wait for it to finish starting, as described
  184. before.
  185. Both LanMan and Service Controller APIs are used in this endeavor.
  186. Arguments:
  187. None
  188. Return Value:
  189. Returns TRUE if the server service has been started; otherwise
  190. returns FALSE. Any API errors return FALSE, and hence assume
  191. the server isn't started or won't start.
  192. --*/
  193. {
  194. appDebugOut((DEB_TRACE, "InitializeShareCache: enter\n"));
  195. g_ShareCache.Refresh(); // sets g_fSharingEnabled
  196. if (g_fSharingEnabled)
  197. {
  198. // well, we've got a cache so no need to start up a thread and wait
  199. // for the server to start
  200. return;
  201. }
  202. // The server isn't currently started. Create a thread that waits for it
  203. // to start, and if it does, refreshes the shell.
  204. DWORD threadId;
  205. HANDLE hThread = CreateThread(
  206. NULL,
  207. 0,
  208. WaitForServerThread,
  209. NULL,
  210. 0,
  211. &threadId);
  212. if (NULL == hThread)
  213. {
  214. appDebugOut((DEB_ERROR, "Error creating thread\n"));
  215. }
  216. else
  217. {
  218. CloseHandle(hThread); // No reason to keep handle around
  219. }
  220. }
  221. //+-------------------------------------------------------------------------
  222. //
  223. // Function: WaitForServerThread
  224. //
  225. // Synopsis: Thread procedure for the thread that waits for the server
  226. // to start.
  227. //
  228. // History: 25-Apr-95 BruceFo Created
  229. //
  230. //--------------------------------------------------------------------------
  231. DWORD WINAPI
  232. WaitForServerThread(
  233. IN LPVOID /*ThreadParameter*/
  234. )
  235. {
  236. appDebugOut((DEB_TRACE, "Created thread to wait for server to start\n"));
  237. if (CheckServiceController())
  238. {
  239. // the server has started
  240. appDebugOut((DEB_TRACE, "The server finally started, after waiting in a thread. Refresh all!\n"));
  241. g_ShareCache.Refresh(); // sets g_fSharingEnabled
  242. }
  243. return 0;
  244. }
  245. //+-------------------------------------------------------------------------
  246. //
  247. // Function: CheckServiceController
  248. //
  249. // Synopsis: Returns TRUE if the server starts, based on consulting the
  250. // service controller and waiting for the server to start.
  251. //
  252. // History: 25-Apr-95 BruceFo Created
  253. //
  254. //--------------------------------------------------------------------------
  255. BOOL
  256. CheckServiceController(
  257. VOID
  258. )
  259. {
  260. // See if it is currently starting.
  261. SC_HANDLE hScManager;
  262. SC_HANDLE hService;
  263. SERVICE_STATUS serviceStatus;
  264. hScManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  265. if (hScManager == NULL)
  266. {
  267. appDebugOut((DEB_ERROR,
  268. "CheckServiceController: OpenSCManager failed: 0x%08lx\n",
  269. GetLastError()));
  270. return FALSE;
  271. }
  272. appDebugOut((DEB_TRACE, "CheckServiceController: opening server service\n"));
  273. hService = OpenService(hScManager, SERVICE_SERVER, SERVICE_QUERY_STATUS);
  274. if (hService == NULL)
  275. {
  276. appDebugOut((DEB_ERROR,
  277. "CheckServiceController: OpenService failed: 0x%08lx\n",
  278. GetLastError()));
  279. CloseServiceHandle(hScManager);
  280. return FALSE;
  281. }
  282. // Now we've got a handle to the server service. See if it's started.
  283. appDebugOut((DEB_TRACE, "CheckServiceController: querying server service\n"));
  284. if (!QueryServiceStatus(hService, &serviceStatus))
  285. {
  286. appDebugOut((DEB_ERROR,
  287. "CheckServiceController: QueryServiceStatus failed: 0x%08lx\n",
  288. GetLastError()));
  289. CloseServiceHandle(hScManager);
  290. CloseServiceHandle(hService);
  291. return FALSE;
  292. }
  293. if (serviceStatus.dwCurrentState == SERVICE_RUNNING)
  294. {
  295. appDebugOut((DEB_TRACE, "CheckServiceController: Server is running!\n"));
  296. // we've off to the races!
  297. CloseServiceHandle(hScManager);
  298. CloseServiceHandle(hService);
  299. return TRUE;
  300. }
  301. if (serviceStatus.dwCurrentState != SERVICE_START_PENDING)
  302. {
  303. appDebugOut((DEB_TRACE, "CheckServiceController: Server is not running nor is it starting! State = %d\n", serviceStatus.dwCurrentState));
  304. // The server is not in the process of starting. Go check its
  305. // configuration and see if it is even configured to start.
  306. if (!ServerConfiguredToStart(hScManager))
  307. {
  308. // the service is not in the process of starting, nor is it
  309. // configured to start, so we give up.
  310. CloseServiceHandle(hScManager);
  311. CloseServiceHandle(hService);
  312. return FALSE;
  313. }
  314. if (!WaitForServerToBeginStarting(hService, &serviceStatus))
  315. {
  316. // The server is configured to start, but we already waited for
  317. // it to commence starting, and it never did. So give up on
  318. // it.
  319. CloseServiceHandle(hScManager);
  320. CloseServiceHandle(hService);
  321. return FALSE;
  322. }
  323. // the server is configured to start, we waited for it to commence
  324. // its startup sequence, and it actually did commence starting!
  325. }
  326. // In this case, the service is trying to start. Wait until it either
  327. // starts or we think it's hung.
  328. appDebugOut((DEB_TRACE, "CheckServiceController: Server is starting\n"));
  329. //
  330. // record the current check point. The service should "periodically
  331. // increment" this if it is making progress.
  332. //
  333. DWORD lastCheckPoint = serviceStatus.dwCheckPoint;
  334. DWORD waitCount = 0;
  335. while (serviceStatus.dwCurrentState == SERVICE_START_PENDING)
  336. {
  337. if (lastCheckPoint == serviceStatus.dwCheckPoint)
  338. {
  339. ++waitCount;
  340. if (waitCount > MAX_WAIT_COUNT)
  341. {
  342. appDebugOut((DEB_TRACE,
  343. "CheckServiceController: Server service is HUNG\n"));
  344. CloseServiceHandle(hScManager);
  345. CloseServiceHandle(hService);
  346. return FALSE;
  347. }
  348. }
  349. else
  350. {
  351. waitCount = 0;
  352. lastCheckPoint = serviceStatus.dwCheckPoint;
  353. }
  354. // Ideally, we would wait the wait hint and be done with it. However,
  355. // We don't want to be waiting if the service gives us an overly
  356. // generous wait hint and finishes while we're still waiting. So,
  357. // wait a fraction of the wait hint. The exact fraction is
  358. // 1/WAIT_FRACTION. The effect is that we wait no more than
  359. // MAX_WAIT_COUNT / WAIT_FRACTION times the wait hint before
  360. // giving up. We make sure we wait at least 1 second between checks.
  361. // Finally, cap the wait hint in case it is far to large, possibly
  362. // in error.
  363. DWORD dwWait = serviceStatus.dwWaitHint / WAIT_FRACTION;
  364. dwWait = (dwWait > MAX_WAIT_PERIOD) ? MAX_WAIT_PERIOD : dwWait;
  365. dwWait = (dwWait < 1000) ? 1000 : dwWait; // at least 1 second
  366. appDebugOut((DEB_TRACE,
  367. "CheckServiceController: sleeping. hint = %d, actually waiting %d\n",
  368. serviceStatus.dwWaitHint, dwWait));
  369. Sleep(dwWait);
  370. if (!QueryServiceStatus(hService, &serviceStatus))
  371. {
  372. appDebugOut((DEB_ERROR,
  373. "CheckServiceController: QueryServiceStatus failed: 0x%08lx\n",
  374. GetLastError()));
  375. CloseServiceHandle(hScManager);
  376. CloseServiceHandle(hService);
  377. return FALSE;
  378. }
  379. }
  380. CloseServiceHandle(hScManager);
  381. CloseServiceHandle(hService);
  382. if (serviceStatus.dwCurrentState == SERVICE_RUNNING)
  383. {
  384. appDebugOut((DEB_TRACE, "CheckServiceController: service finally started\n"));
  385. // This magic line refreshes *all* the explorer windows. Unfortunately,
  386. // it causes a share cache refresh for each one as well...
  387. SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
  388. return TRUE; // Finally! It's running!
  389. }
  390. else
  391. {
  392. appDebugOut((DEB_TRACE, "CheckServiceController: service never started\n"));
  393. return FALSE;
  394. }
  395. }
  396. BOOL
  397. ServerConfiguredToStart(
  398. SC_HANDLE hScManager
  399. )
  400. {
  401. // We re-open the service because we want a different type of permission
  402. SC_HANDLE hService = OpenService(hScManager, SERVICE_SERVER, SERVICE_QUERY_CONFIG);
  403. if (hService == NULL)
  404. {
  405. appDebugOut((DEB_ERROR,
  406. "ServerConfiguredToStart: OpenService failed: 0x%08lx\n",
  407. GetLastError()));
  408. return FALSE;
  409. }
  410. BOOL b;
  411. DWORD cbBytesNeeded;
  412. BYTE buffer[1024]; // a large buffer...
  413. LPQUERY_SERVICE_CONFIG lpqscServConfig = (LPQUERY_SERVICE_CONFIG)buffer;
  414. b = QueryServiceConfig(
  415. hService,
  416. lpqscServConfig,
  417. sizeof(buffer),
  418. &cbBytesNeeded);
  419. if (!b)
  420. {
  421. appDebugOut((DEB_ERROR,
  422. "ServerConfiguredToStart: QueryServiceConfig failed: 0x%08lx\n",
  423. GetLastError()));
  424. return FALSE;
  425. }
  426. b = (lpqscServConfig->dwStartType == SERVICE_AUTO_START);
  427. CloseServiceHandle(hService);
  428. appDebugOut((DEB_TRACE,
  429. "ServerConfiguredToStart: configured to start? %s\n",
  430. b ? "yes" : "no"));
  431. return b;
  432. }
  433. BOOL
  434. WaitForServerToBeginStarting(
  435. SC_HANDLE hService,
  436. LPSERVICE_STATUS pServiceStatus // so we don't need to re-query on successful return
  437. )
  438. {
  439. // Here's the algorithm:
  440. // wait WAIT_TO_BEGIN_GRANULARITY milliseconds
  441. // query status
  442. // if the service is running then return TRUE
  443. // if we've waited MAX_WAIT_TO_BEGIN ms, return FALSE
  444. // go back and wait again...
  445. DWORD dwWaitedMilliseconds;
  446. for (dwWaitedMilliseconds = 0;
  447. dwWaitedMilliseconds < MAX_WAIT_TO_BEGIN;
  448. dwWaitedMilliseconds += WAIT_TO_BEGIN_GRANULARITY)
  449. {
  450. appDebugOut((DEB_TRACE,
  451. "WaitForServerToBeginStarting: sleeping\n"));
  452. Sleep(WAIT_TO_BEGIN_GRANULARITY);
  453. if (!QueryServiceStatus(hService, pServiceStatus))
  454. {
  455. appDebugOut((DEB_ERROR,
  456. "WaitForServerToBeginStarting: QueryServiceStatus failed: 0x%08lx\n",
  457. GetLastError()));
  458. return FALSE;
  459. }
  460. if ( pServiceStatus->dwCurrentState == SERVICE_RUNNING
  461. || pServiceStatus->dwCurrentState == SERVICE_START_PENDING
  462. )
  463. {
  464. appDebugOut((DEB_TRACE,
  465. "WaitForServerToBeginStarting: server commenced startup\n"));
  466. return TRUE;
  467. }
  468. }
  469. appDebugOut((DEB_TRACE,
  470. "WaitForServerToBeginStarting: waited %d milliseconds for server to commence startup, then gave up\n",
  471. MAX_WAIT_TO_BEGIN));
  472. return FALSE;
  473. }