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.

612 lines
15 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1993 **/
  4. /**********************************************************************/
  5. /*
  6. main.cxx
  7. This module contains the main startup code for the FTPD Service.
  8. Functions exported by this module:
  9. ServiceEntry
  10. FILE HISTORY:
  11. KeithMo 07-Mar-1993 Created.
  12. KeithMo 07-Jan-1994 Made it a DLL (part of TCPSVCS.EXE).
  13. MuraliK 21-March-1995 Modified it to use InternetServices
  14. common dll ( tcpsvcs.dll)
  15. MuraliK 11-April-1995 Added global ftp server config objects etc.
  16. ( removed usage of Init and Terminate UserDatabase)
  17. */
  18. #include <ftpdp.hxx>
  19. #include <apiutil.h>
  20. #include <inetsvcs.h>
  21. //
  22. // Private constants.
  23. //
  24. #define FTPD_MODULE_NAME_A "ftpsvc2.dll"
  25. #define DEFAULT_RECV_BUFFER_SIZE (8192)
  26. //
  27. // Global variables for service info and debug variables.
  28. //
  29. DEFINE_TSVC_INFO_INTERFACE( );
  30. DECLARE_DEBUG_PRINTS_OBJECT();
  31. //
  32. // The following critical section synchronizes execution in ServiceEntry().
  33. // This is necessary because the NT Service Controller may reissue a service
  34. // start notification immediately after we have set our status to stopped.
  35. // This can lead to an unpleasant race condition in ServiceEntry() as one
  36. // thread cleans up global state as another thread is initializing it.
  37. //
  38. CRITICAL_SECTION g_csServiceEntryLock;
  39. //
  40. // Private prototypes.
  41. //
  42. APIERR
  43. InitializeService(
  44. LPVOID lpContext
  45. );
  46. APIERR
  47. TerminateService(
  48. LPVOID lpContext
  49. );
  50. extern
  51. VOID
  52. FtpdNewConnection(
  53. IN SOCKET sNew,
  54. IN SOCKADDR_IN * psockaddr,
  55. IN PVOID EndpointContext,
  56. IN PVOID EndpointObject
  57. );
  58. extern
  59. VOID
  60. FtpdNewConnectionEx(
  61. IN PVOID patqContext,
  62. IN DWORD cbWritten,
  63. IN DWORD dwError,
  64. IN OVERLAPPED * lpo
  65. );
  66. DWORD
  67. PrintOutCurrentTime(
  68. IN CHAR * pszFile,
  69. IN int lineNum
  70. );
  71. # ifdef CHECK_DBG
  72. # define PRINT_CURRENT_TIME_TO_DBG() PrintOutCurrentTime( __FILE__, __LINE__)
  73. # else
  74. # define PRINT_CURRENT_TIME_TO_DBG() ( NO_ERROR)
  75. # endif // CHECK_DBG
  76. VOID
  77. ServiceEntry(
  78. DWORD cArgs,
  79. LPSTR pArgs[],
  80. PTCPSVCS_GLOBAL_DATA pGlobalData // unused
  81. )
  82. /*++
  83. Routine:
  84. This is the "real" entrypoint for the service. When
  85. the Service Controller dispatcher is requested to
  86. start a service, it creates a thread that will begin
  87. executing this routine.
  88. Arguments:
  89. cArgs - Number of command line arguments to this service.
  90. pArgs - Pointers to the command line arguments.
  91. Returns:
  92. None. Does not return until service is stopped.
  93. --*/
  94. {
  95. APIERR err = NO_ERROR;
  96. BOOL fInitSvcObject = FALSE;
  97. EnterCriticalSection( &g_csServiceEntryLock );
  98. if ( !InitCommonDlls() )
  99. {
  100. err = GetLastError();
  101. LeaveCriticalSection( &g_csServiceEntryLock );
  102. goto notify_scm;
  103. }
  104. //
  105. // Initialize the service status structure.
  106. //
  107. g_pInetSvc = new FTP_IIS_SERVICE(
  108. FTPD_SERVICE_NAME,
  109. FTPD_MODULE_NAME_A,
  110. FTPD_PARAMETERS_KEY_A,
  111. INET_FTP_SVC_ID,
  112. INET_FTP_SVCLOC_ID,
  113. FALSE,
  114. 0,
  115. FtpdNewConnection,
  116. FtpdNewConnectionEx,
  117. ProcessAtqCompletion
  118. );
  119. //
  120. // If we couldn't allocate memory for the service info structure,
  121. // then we're totally hozed.
  122. //
  123. if( (g_pInetSvc != NULL) && g_pInetSvc->IsActive()) {
  124. fInitSvcObject = TRUE;
  125. //
  126. // Start the service. This blocks until the service is shutdown.
  127. //
  128. err = g_pInetSvc->StartServiceOperation(
  129. SERVICE_CTRL_HANDLER(),
  130. InitializeService,
  131. TerminateService
  132. );
  133. if( err != NO_ERROR) {
  134. //
  135. // The event has already been logged.
  136. //
  137. DBGPRINTF(( DBG_CONTEXT,
  138. "FTP ServiceEntry: StartServiceOperation returned %d\n",
  139. err ));
  140. }
  141. } else {
  142. if ( g_pInetSvc ) {
  143. err = g_pInetSvc->QueryCurrentServiceError();
  144. } else {
  145. err = ERROR_NOT_ENOUGH_MEMORY;
  146. }
  147. }
  148. if( g_pInetSvc != NULL ) {
  149. //
  150. // delete the service object
  151. //
  152. g_pInetSvc->CloseService( );
  153. g_pInetSvc = NULL;
  154. }
  155. TerminateCommonDlls();
  156. LeaveCriticalSection( &g_csServiceEntryLock );
  157. notify_scm:
  158. //
  159. // We need to tell the Service Control Manager that the service
  160. // is stopped if we haven't called g_pInetSvc->StartServiceOperation.
  161. // 1) InitCommonDlls fails, or
  162. // 2) new operator failed, or
  163. // 3) FTP_IIS_SERVICE constructor couldn't initialize properly
  164. //
  165. if ( !fInitSvcObject ) {
  166. SERVICE_STATUS_HANDLE hsvcStatus;
  167. SERVICE_STATUS svcStatus;
  168. hsvcStatus = RegisterServiceCtrlHandler( FTPD_SERVICE_NAME,
  169. SERVICE_CTRL_HANDLER() );
  170. if ( hsvcStatus != NULL_SERVICE_STATUS_HANDLE ) {
  171. svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  172. svcStatus.dwCurrentState = SERVICE_STOPPED;
  173. svcStatus.dwWin32ExitCode = err;
  174. svcStatus.dwServiceSpecificExitCode = err;
  175. svcStatus.dwControlsAccepted = 0;
  176. svcStatus.dwCheckPoint = 0;
  177. svcStatus.dwWaitHint = 0;
  178. SetServiceStatus( hsvcStatus, (LPSERVICE_STATUS) &svcStatus );
  179. }
  180. }
  181. } // ServiceEntry()
  182. //
  183. // Private functions.
  184. //
  185. DWORD
  186. InitializeInstances(
  187. PFTP_IIS_SERVICE pService
  188. )
  189. /*++
  190. Routine Description:
  191. Read the instances from the registry
  192. Arguments:
  193. pService - Server instance added to.
  194. Return Value:
  195. Win32
  196. --*/
  197. {
  198. DWORD err = NO_ERROR;
  199. DWORD i;
  200. DWORD cInstances = 0;
  201. MB mb( (IMDCOM*) pService->QueryMDObject() );
  202. BUFFER buff;
  203. CHAR szKeyName[MAX_PATH+1];
  204. BOOL fMigrateRoots = FALSE;
  205. //
  206. // Open the metabase for write to get an atomic snapshot
  207. //
  208. if ( !mb.Open( "/LM/MSFTPSVC/",
  209. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ))
  210. {
  211. DBGPRINTF(( DBG_CONTEXT,
  212. "InitializeInstances: Cannot open path %s, error %lu\n",
  213. "/LM/MSFTPSVC/", GetLastError() ));
  214. #if 1 // Temporary until setup is modified to create the instances in the metabase
  215. if ( !mb.Open( METADATA_MASTER_ROOT_HANDLE,
  216. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ||
  217. !mb.AddObject( "/LM/MSFTPSVC/" ) )
  218. {
  219. DBGPRINTF(( DBG_CONTEXT,
  220. "Unable to create service, error %d\n",
  221. GetLastError() ));
  222. return GetLastError();
  223. }
  224. #else
  225. return GetLastError();
  226. #endif
  227. }
  228. TryAgain:
  229. i = 0;
  230. while ( mb.EnumObjects( "",
  231. szKeyName,
  232. i++ ))
  233. {
  234. DWORD dwInstance;
  235. //
  236. // Get the instance id
  237. //
  238. dwInstance = atoi( szKeyName );
  239. if ( dwInstance == 0 ) {
  240. continue;
  241. }
  242. if ( buff.QuerySize() < (cInstances + 1) * sizeof(DWORD) )
  243. {
  244. if ( !buff.Resize( (cInstances + 10) * sizeof(DWORD)) )
  245. {
  246. return GetLastError();
  247. }
  248. }
  249. ((DWORD *) buff.QueryPtr())[cInstances++] = dwInstance;
  250. }
  251. if ( cInstances == 0 )
  252. {
  253. DBGPRINTF(( DBG_CONTEXT,
  254. "No defined instances\n" ));
  255. if ( !mb.AddObject( "1" ))
  256. {
  257. DBGPRINTF(( DBG_CONTEXT,
  258. "Unable to create first instance, error %d\n",
  259. GetLastError() ));
  260. return GetLastError();
  261. }
  262. fMigrateRoots = TRUE; // Force reg->metabase migration of virtual directories
  263. goto TryAgain;
  264. }
  265. DBG_REQUIRE( mb.Close() );
  266. for ( i = 0; i < cInstances; i++ )
  267. {
  268. DWORD dwInstance = ((DWORD *)buff.QueryPtr())[i];
  269. if( !g_pInetSvc->AddInstanceInfo( dwInstance, fMigrateRoots ) ) {
  270. err = GetLastError();
  271. DBGPRINTF((
  272. DBG_CONTEXT,
  273. "InitializeInstances: cannot create instance %lu, error %lu\n",
  274. dwInstance,
  275. err
  276. ));
  277. break;
  278. }
  279. }
  280. return err;
  281. } // InitializeInstances
  282. APIERR
  283. InitializeService(
  284. LPVOID lpContext
  285. )
  286. /*++
  287. Routine:
  288. This function initializes the various FTPD Service components.
  289. Arguments:
  290. lpContext - Pointer to the service object
  291. Returns:
  292. NO_ERROR if successful, otherwise a Win32
  293. status code.
  294. --*/
  295. {
  296. APIERR err = NO_ERROR;
  297. PFTP_IIS_SERVICE pInetSvc = (PFTP_IIS_SERVICE)lpContext;
  298. DBG_ASSERT( lpContext == g_pInetSvc);
  299. IF_DEBUG( SERVICE_CTRL ) {
  300. DBGPRINTF(( DBG_CONTEXT,"Initializing ftp service\n" ));
  301. }
  302. //
  303. // Initialize various components. The ordering of the
  304. // components is somewhat limited.
  305. // We should initialize connections as the last item,
  306. // since it kicks off the connection thread.
  307. //
  308. err = PRINT_CURRENT_TIME_TO_DBG();
  309. if(( err = InitializeGlobals() ) != NO_ERROR ||
  310. ( err = PRINT_CURRENT_TIME_TO_DBG()) != NO_ERROR ||
  311. ( err = pInetSvc->InitializeSockets()) != NO_ERROR ||
  312. ( err = PRINT_CURRENT_TIME_TO_DBG()) != NO_ERROR ||
  313. ( err = pInetSvc->InitializeDiscovery( )) != NO_ERROR ||
  314. ( err = PRINT_CURRENT_TIME_TO_DBG()) != NO_ERROR ) {
  315. DBGPRINTF(( DBG_CONTEXT,
  316. "cannot initialize ftp service, error %lu\n",err ));
  317. goto exit;
  318. }
  319. //
  320. // Success!
  321. //
  322. DBG_ASSERT( err == NO_ERROR);
  323. //
  324. // From discusssions with KeithMo, we decided to punt on the
  325. // default buffer size for now. Later on if performance is
  326. // critical, we will try to improve on this by proper values
  327. // for listen socket.
  328. //
  329. g_SocketBufferSize = DEFAULT_RECV_BUFFER_SIZE;
  330. IF_DEBUG( SERVICE_CTRL ) {
  331. DBGPRINTF(( DBG_CONTEXT, " %s service initialized\n",
  332. pInetSvc->QueryServiceName())
  333. );
  334. }
  335. //
  336. // Initialize all instances
  337. //
  338. InitializeInstances(pInetSvc);
  339. g_pFTPStats->UpdateStartTime();
  340. exit:
  341. PRINT_CURRENT_TIME_TO_DBG();
  342. return ( err);
  343. } // InitializeService()
  344. APIERR
  345. TerminateService(
  346. LPVOID lpContext
  347. )
  348. /*++
  349. Routine:
  350. This function cleans up the various FTPD Service components.
  351. Arguments:
  352. lpContext - Pointer to the service object
  353. Returns:
  354. NO_ERROR if successful, otherwise a Win32
  355. status code.
  356. --*/
  357. {
  358. APIERR err = NO_ERROR;
  359. PFTP_IIS_SERVICE pInetSvc = (PFTP_IIS_SERVICE)lpContext;
  360. DBG_ASSERT( lpContext == g_pInetSvc);
  361. IF_DEBUG( SERVICE_CTRL ) {
  362. DBGPRINTF(( DBG_CONTEXT, "terminating service\n" ));
  363. }
  364. PRINT_CURRENT_TIME_TO_DBG();
  365. if (g_pFTPStats) {
  366. g_pFTPStats->UpdateStopTime();
  367. }
  368. //
  369. // Components should be terminated in reverse
  370. // initialization order.
  371. //
  372. g_pInetSvc->ShutdownService( );
  373. PRINT_CURRENT_TIME_TO_DBG();
  374. IF_DEBUG( SERVICE_CTRL ) {
  375. DBGPRINTF(( DBG_CONTEXT, "Ftp service terminated\n" ));
  376. }
  377. PRINT_CURRENT_TIME_TO_DBG();
  378. err = pInetSvc->TerminateDiscovery();
  379. if ( err != NO_ERROR) {
  380. DBGPRINTF( ( DBG_CONTEXT,
  381. "CleanupService( %s):"
  382. " TerminateDiscovery failed, err=%lu\n",
  383. pInetSvc->QueryServiceName(),
  384. err));
  385. }
  386. PRINT_CURRENT_TIME_TO_DBG();
  387. pInetSvc->CleanupSockets();
  388. PRINT_CURRENT_TIME_TO_DBG();
  389. TsCacheFlush( INET_FTP_SVC_ID );
  390. TsFlushMetaCache(METACACHE_FTP_SERVER_ID, TRUE);
  391. TerminateGlobals();
  392. return ( err);
  393. } // TerminateService()
  394. # ifdef CHECK_DBG
  395. DWORD PrintOutCurrentTime(IN CHAR * pszFile, IN int lineNum)
  396. /*++
  397. This function generates the current time and prints it out to debugger
  398. for tracing out the path traversed, if need be.
  399. Arguments:
  400. pszFile pointer to string containing the name of the file
  401. lineNum line number within the file where this function is called.
  402. Returns:
  403. NO_ERROR always.
  404. --*/
  405. {
  406. CHAR szBuffer[1000];
  407. DBG_REQUIRE( _snprintf( szBuffer, sizeof( szBuffer ),
  408. "[%u]( %40s, %10d) TickCount = %u\n",
  409. GetCurrentThreadId(),
  410. pszFile,
  411. lineNum,
  412. GetTickCount()
  413. ) > 0);
  414. OutputDebugString( szBuffer);
  415. return ( NO_ERROR);
  416. } // PrintOutCurrentTime()
  417. # endif // CHECK_DBG
  418. extern "C" {
  419. BOOL
  420. WINAPI
  421. DllMain(
  422. HINSTANCE hDll,
  423. DWORD dwReason,
  424. LPVOID lpvReserved
  425. )
  426. {
  427. switch ( dwReason ) {
  428. case DLL_PROCESS_ATTACH:
  429. CREATE_DEBUG_PRINT_OBJECT( FTPD_SERVICE_NAME);
  430. if ( !VALID_DEBUG_PRINT_OBJECT()) {
  431. return FALSE; // Nothing can be done. Debug Print object failed!
  432. }
  433. LOAD_DEBUG_FLAGS_FROM_REG_STR(FTPD_PARAMETERS_KEY_A, 0);
  434. DBG_REQUIRE( DisableThreadLibraryCalls( hDll ) );
  435. INITIALIZE_CRITICAL_SECTION( &g_csServiceEntryLock );
  436. break;
  437. case DLL_PROCESS_DETACH:
  438. DELETE_DEBUG_PRINT_OBJECT();
  439. DeleteCriticalSection( &g_csServiceEntryLock );
  440. break;
  441. }
  442. return TRUE;
  443. } // DLLEntry
  444. } // extern "C"
  445. /************************ End Of File ************************/