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.

624 lines
23 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. service.c
  5. Abstract:
  6. This module contains functions to implement TFTP
  7. as a NT system service. It contains the startup
  8. and cleanup code for the service.
  9. Author:
  10. Jeffrey C. Venable, Sr. (jeffv) 01-Jun-2001
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #include <winsvc.h>
  15. TFTPD_GLOBALS globals;
  16. void
  17. TftpdServiceCleanup() {
  18. NTSTATUS status;
  19. UINT x;
  20. TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceCleanup().\n"));
  21. // Destroy the hash table.
  22. ASSERT(globals.hash.numEntries == 0);
  23. if (globals.initialized.contextHashTable) {
  24. for (x = 0; x < globals.parameters.hashEntries; x++)
  25. DeleteCriticalSection(&globals.hash.table[x].cs);
  26. HeapFree(globals.hServiceHeap, 0, globals.hash.table);
  27. globals.hash.table = NULL;
  28. globals.initialized.contextHashTable = FALSE;
  29. }
  30. // Destroy the timeout timer queue.
  31. if (!DeleteTimerQueueEx(globals.io.hTimerQueue, INVALID_HANDLE_VALUE)) {
  32. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  33. "TftpdServiceCleanup(): DeleteTimerQueueEx() failed, "
  34. "error = 0x%08X.\n",
  35. GetLastError()));
  36. }
  37. globals.io.hTimerQueue = NULL;
  38. // Undo initialized things.
  39. if (globals.initialized.ioCS) {
  40. DeleteCriticalSection(&globals.io.cs);
  41. globals.initialized.ioCS = FALSE;
  42. }
  43. if (globals.initialized.reaperContextCS) {
  44. DeleteCriticalSection(&globals.reaper.contextCS);
  45. globals.initialized.reaperContextCS = FALSE;
  46. }
  47. if (globals.initialized.reaperSocketCS) {
  48. DeleteCriticalSection(&globals.reaper.socketCS);
  49. globals.initialized.reaperSocketCS = FALSE;
  50. }
  51. if (globals.initialized.winsock) {
  52. WSACleanup();
  53. globals.initialized.winsock = FALSE;
  54. }
  55. // Clean up the service heap.
  56. if (globals.hServiceHeap != GetProcessHeap()) {
  57. // We used a private heap, destroying it will automatically deallocate
  58. // everything we used, including connections.
  59. HeapDestroy(globals.hServiceHeap);
  60. } else {
  61. // We had to use the process heap instead of a private heap,
  62. // cleanup remaining allocations.
  63. } // if (globals.hServiceHeap != GetProcessHeap())
  64. globals.hServiceHeap = NULL;
  65. // Unregister event-logging if necessary.
  66. if (globals.service.hEventLogSource != NULL)
  67. DeregisterEventSource(globals.service.hEventLogSource), globals.service.hEventLogSource = NULL;
  68. // Notify the service control manager that we've stopped.
  69. globals.service.status.dwCurrentState = SERVICE_STOPPED;
  70. SetServiceStatus(globals.service.hStatus, &globals.service.status);
  71. if (globals.reaper.numLeakedContexts) {
  72. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  73. "TftpdServiceCleanup(): Leaked %d contexts.\n",
  74. globals.reaper.numLeakedContexts));
  75. }
  76. if (globals.reaper.numLeakedSockets) {
  77. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  78. "TftpdServiceCleanup(): Leaked %d sockets.\n",
  79. globals.reaper.numLeakedSockets));
  80. }
  81. TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceCleanup(): Service stopped.\n"));
  82. } // TftpdServiceCleanup()
  83. void
  84. TftpdServiceAttemptCleanup() {
  85. TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceAttemptCleanup().\n"));
  86. InterlockedIncrement((PLONG)&globals.service.status.dwCheckPoint);
  87. SetServiceStatus(globals.service.hStatus, &globals.service.status);
  88. if ((globals.io.numBuffers != -1) || (globals.io.numContexts != -1))
  89. return;
  90. if (InterlockedCompareExchange((PLONG)&globals.service.shutdown, 2, 1) != 1)
  91. return;
  92. TftpdServiceCleanup();
  93. } // TftpdServiceAttemptCleanup()
  94. void
  95. TftpdShutdownService() {
  96. UINT x;
  97. TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdShutdownService().\n"));
  98. ASSERT(globals.service.status.dwCurrentState != SERVICE_STOPPED);
  99. // Set the shutdown flag.
  100. InterlockedExchange((PLONG)&globals.service.shutdown, 1);
  101. // Notify the service control manager that we're going to stop.
  102. globals.service.status.dwCurrentState = SERVICE_STOP_PENDING;
  103. globals.service.status.dwWaitHint = 5000;
  104. globals.service.status.dwCheckPoint = 0;
  105. SetServiceStatus(globals.service.hStatus, &globals.service.status);
  106. // Close the sockets.
  107. EnterCriticalSection(&globals.io.cs); {
  108. if (globals.io.master.s != INVALID_SOCKET)
  109. TftpdIoDestroySocketContext(&globals.io.master);
  110. if (globals.io.def.s != INVALID_SOCKET)
  111. TftpdIoDestroySocketContext(&globals.io.def);
  112. if (globals.io.mtu.s != INVALID_SOCKET)
  113. TftpdIoDestroySocketContext(&globals.io.mtu);
  114. if (globals.io.max.s != INVALID_SOCKET)
  115. TftpdIoDestroySocketContext(&globals.io.max);
  116. } LeaveCriticalSection(&globals.io.cs);
  117. // Empty out all the contexts from the hash table.
  118. for (x = 0; x < globals.parameters.hashEntries; x++) {
  119. EnterCriticalSection(&globals.hash.table[x].cs); {
  120. while (globals.hash.table[x].bucket.Flink != &globals.hash.table[x].bucket)
  121. TftpdContextKill(CONTAINING_RECORD(globals.hash.table[x].bucket.Flink,
  122. TFTPD_CONTEXT, linkage));
  123. } LeaveCriticalSection(&globals.hash.table[x].cs);
  124. } // for (unsigned int x = 0; x < globals.parameters.hashEntries; x++)
  125. // Empty out all the contexts from the leak list.
  126. EnterCriticalSection(&globals.reaper.contextCS); {
  127. PLIST_ENTRY entry;
  128. while ((entry = RemoveHeadList(&globals.reaper.leakedContexts)) !=
  129. &globals.reaper.leakedContexts) {
  130. PTFTPD_CONTEXT context = CONTAINING_RECORD(entry, TFTPD_CONTEXT, linkage);
  131. if (!TftpdContextFree(context)) {
  132. // Free the reference from it having been on the leak list.
  133. TftpdContextRelease(context);
  134. }
  135. globals.reaper.numLeakedContexts--;
  136. }
  137. } LeaveCriticalSection(&globals.reaper.contextCS);
  138. // Empty out all the sockets from the leak list.
  139. EnterCriticalSection(&globals.reaper.socketCS); {
  140. PLIST_ENTRY entry;
  141. while ((entry = RemoveHeadList(&globals.reaper.leakedSockets)) !=
  142. &globals.reaper.leakedSockets) {
  143. TftpdIoDestroySocketContext(CONTAINING_RECORD(entry, TFTPD_SOCKET, linkage));
  144. globals.reaper.numLeakedSockets--;
  145. }
  146. } LeaveCriticalSection(&globals.reaper.socketCS);
  147. InterlockedDecrement(&globals.io.numBuffers);
  148. InterlockedDecrement(&globals.io.numContexts);
  149. TftpdServiceAttemptCleanup();
  150. } // TftpdShutdownService()
  151. BOOL
  152. TftpdSetStartDirectory(char *path) {
  153. char expanded[MAX_PATH];
  154. int length;
  155. if (path == NULL)
  156. path = "\\tftpdroot";
  157. // Expand the string and leave room to insert a trailing '\\'.
  158. if ((length = ExpandEnvironmentStrings(path, expanded, sizeof(expanded) - 1)) == 0)
  159. return (FALSE);
  160. CopyMemory(globals.parameters.rootDirectory, expanded, length);
  161. if ((globals.parameters.rootDirectory[length - 1] != '\\') && (length < MAX_PATH))
  162. strcat(globals.parameters.rootDirectory, "\\");
  163. return (TRUE);
  164. } // TftpdSetStartDirectory()
  165. void
  166. TftpdReadRegistryParameters() {
  167. DWORD keyType, valueSize;
  168. char path[MAX_PATH];
  169. HKEY parameters = NULL;
  170. // Open the registry key which contains all the adjustable parameters
  171. // to the service. We will register for notification on it later incase
  172. // anything changes while we're running.
  173. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  174. "System\\CurrentControlSet\\Services\\Tftpd\\Parameters",
  175. 0, KEY_QUERY_VALUE, &parameters) != ERROR_SUCCESS)
  176. parameters = NULL;
  177. if (parameters == NULL) {
  178. globals.parameters.hashEntries = 256;
  179. globals.parameters.lowWaterMark = 5;
  180. globals.parameters.highWaterMark = 256;
  181. globals.parameters.maxRetries = 10;
  182. TftpdSetStartDirectory(NULL);
  183. strcpy(globals.parameters.validClients, "*.*.*.*");
  184. strcpy(globals.parameters.validReadFiles, "*");
  185. strcpy(globals.parameters.validMasters, "*.*.*.*");
  186. strcpy(globals.parameters.validWriteFiles, "*");
  187. return;
  188. }
  189. #if (DBG)
  190. // Initialize debug settings (if applicable) :
  191. keyType = 0;
  192. valueSize = sizeof(globals.parameters.debugFlags);
  193. if ((RegQueryValueEx(parameters, "DebugFlags", NULL, &keyType,
  194. (LPBYTE)&globals.parameters.debugFlags, &valueSize) != ERROR_SUCCESS) ||
  195. (keyType != REG_DWORD)) {
  196. globals.parameters.debugFlags = 0x00000000;
  197. }
  198. #endif // (DBG)
  199. keyType = 0;
  200. valueSize = sizeof(globals.parameters.hashEntries);
  201. if ((RegQueryValueEx(parameters, "HashEntries", NULL, &keyType,
  202. (LPBYTE)&globals.parameters.hashEntries, &valueSize) != ERROR_SUCCESS) ||
  203. (keyType != REG_DWORD) || !globals.parameters.hashEntries)
  204. globals.parameters.hashEntries = 256;
  205. if (globals.parameters.hashEntries < 1)
  206. globals.parameters.hashEntries = 1;
  207. keyType = 0;
  208. valueSize = sizeof(globals.parameters.lowWaterMark);
  209. if ((RegQueryValueEx(parameters, "LowWaterMark", NULL, &keyType,
  210. (LPBYTE)&globals.parameters.lowWaterMark, &valueSize) != ERROR_SUCCESS) ||
  211. (keyType != REG_DWORD) || !globals.parameters.lowWaterMark)
  212. globals.parameters.lowWaterMark = 5;
  213. keyType = 0;
  214. valueSize = sizeof(globals.parameters.highWaterMark);
  215. if ((RegQueryValueEx(parameters, "HighWaterMark", NULL, &keyType,
  216. (LPBYTE)&globals.parameters.highWaterMark, &valueSize) != ERROR_SUCCESS) ||
  217. (keyType != REG_DWORD) || !globals.parameters.highWaterMark)
  218. globals.parameters.highWaterMark = 256;
  219. keyType = 0;
  220. valueSize = sizeof(globals.parameters.maxRetries);
  221. if ((RegQueryValueEx(parameters, "MaxRetries", NULL, &keyType,
  222. (LPBYTE)&globals.parameters.maxRetries, &valueSize) != ERROR_SUCCESS) ||
  223. (keyType != REG_DWORD) || !globals.parameters.maxRetries)
  224. globals.parameters.maxRetries = 4;
  225. path[0] = '\0';
  226. keyType = 0;
  227. valueSize = sizeof(globals.parameters.rootDirectory);
  228. if ((RegQueryValueEx(parameters, "directory", NULL, &keyType, (LPBYTE)path, &valueSize) != ERROR_SUCCESS) ||
  229. (keyType != REG_SZ))
  230. TftpdSetStartDirectory(NULL);
  231. else
  232. TftpdSetStartDirectory(path);
  233. keyType = 0;
  234. valueSize = sizeof(globals.parameters.validClients);
  235. if ((RegQueryValueEx(parameters, "clients", NULL, &keyType,
  236. (LPBYTE)&globals.parameters.validClients, &valueSize) != ERROR_SUCCESS) ||
  237. (keyType != REG_SZ))
  238. strcpy(globals.parameters.validClients, "*.*.*.*");
  239. keyType = 0;
  240. valueSize = sizeof(globals.parameters.validReadFiles);
  241. if ((RegQueryValueEx(parameters, "readable", NULL, &keyType,
  242. (LPBYTE)&globals.parameters.validReadFiles, &valueSize) != ERROR_SUCCESS) ||
  243. (keyType != REG_SZ))
  244. strcpy(globals.parameters.validReadFiles, "*");
  245. keyType = 0;
  246. valueSize = sizeof(globals.parameters.validMasters);
  247. if ((RegQueryValueEx(parameters, "masters", NULL, &keyType,
  248. (LPBYTE)&globals.parameters.validMasters, &valueSize) != ERROR_SUCCESS) ||
  249. (keyType != REG_SZ))
  250. strcpy(globals.parameters.validMasters, "*.*.*.*");
  251. keyType = 0;
  252. valueSize = sizeof(globals.parameters.validWriteFiles);
  253. if ((RegQueryValueEx(parameters, "writable", NULL, &keyType,
  254. (LPBYTE)&globals.parameters.validWriteFiles, &valueSize) != ERROR_SUCCESS) ||
  255. (keyType != REG_SZ))
  256. strcpy(globals.parameters.validWriteFiles, "*");
  257. RegCloseKey(parameters);
  258. } // TftpdReadRegistryParameters()
  259. void WINAPI
  260. TftpdServiceHandler(
  261. DWORD dwOpcode
  262. );
  263. void WINAPI
  264. TftpdServiceMain(DWORD argc, PWSTR argv[]) {
  265. WSADATA wsaData;
  266. PSERVENT servent;
  267. SOCKADDR_IN addr;
  268. NTSTATUS status;
  269. UINT x;
  270. TftpdReadRegistryParameters();
  271. // Register the service control handler.
  272. if ((globals.service.hStatus = RegisterServiceCtrlHandler(TEXT("Tftpd"), TftpdServiceHandler)) == 0) {
  273. globals.service.status.dwWin32ExitCode = GetLastError();
  274. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  275. "ServiceMain: RegisterServiceCtrlHandler() failed, error 0x%08X.\n",
  276. globals.service.status.dwWin32ExitCode));
  277. TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
  278. goto stop_service;
  279. }
  280. // Immediately report that we are beginning to start up.
  281. globals.service.status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  282. globals.service.status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  283. globals.service.status.dwCurrentState = SERVICE_START_PENDING;
  284. SetServiceStatus(globals.service.hStatus, &globals.service.status);
  285. TFTPD_DEBUG((TFTPD_TRACE_SERVICE,
  286. "\nTftpdServiceMain: Parameters...\n"
  287. "\tDebug flags : 0x%08X\n"
  288. "\tHash table size : %d buckets\n"
  289. "\tBuffer low water-mark : %d buffers\n"
  290. "\tBuffer high water-mark : %d buffers\n"
  291. "\tMax retries : %d attempts\n"
  292. "\tRoot directory : %s\n"
  293. "\tValid client mask : %s\n"
  294. "\tValid read file mask : %s\n"
  295. "\tValid master mask : %s\n"
  296. "\tValid write file mask : %s\n",
  297. globals.parameters.debugFlags,
  298. globals.parameters.hashEntries,
  299. globals.parameters.lowWaterMark,
  300. globals.parameters.highWaterMark,
  301. globals.parameters.maxRetries,
  302. globals.parameters.rootDirectory,
  303. globals.parameters.validClients,
  304. globals.parameters.validReadFiles,
  305. globals.parameters.validMasters,
  306. globals.parameters.validWriteFiles));
  307. // Attempt to create the service's private heap.
  308. if ((globals.hServiceHeap = HeapCreate(0, 0, 0)) == NULL)
  309. globals.hServiceHeap = GetProcessHeap();
  310. __try { InitializeCriticalSection(&globals.io.cs); }
  311. __except (EXCEPTION_EXECUTE_HANDLER) {
  312. globals.service.status.dwWin32ExitCode = _exception_code();
  313. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  314. "InitializeCriticalSection() raised exception 0x%08X.\n",
  315. globals.service.status.dwWin32ExitCode));
  316. TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
  317. goto stop_service;
  318. }
  319. globals.initialized.ioCS = TRUE;
  320. __try { InitializeCriticalSection(&globals.reaper.contextCS); }
  321. __except (EXCEPTION_EXECUTE_HANDLER) {
  322. globals.service.status.dwWin32ExitCode = _exception_code();
  323. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  324. "InitializeCriticalSection() raised exception 0x%08X.\n",
  325. globals.service.status.dwWin32ExitCode));
  326. TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
  327. goto stop_service;
  328. }
  329. globals.initialized.reaperContextCS = TRUE;
  330. InitializeListHead(&globals.reaper.leakedContexts);
  331. __try { InitializeCriticalSection(&globals.reaper.socketCS); }
  332. __except (EXCEPTION_EXECUTE_HANDLER) {
  333. globals.service.status.dwWin32ExitCode = _exception_code();
  334. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  335. "InitializeCriticalSection() raised exception 0x%08X.\n",
  336. globals.service.status.dwWin32ExitCode));
  337. TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
  338. goto stop_service;
  339. }
  340. globals.initialized.reaperSocketCS = TRUE;
  341. InitializeListHead(&globals.reaper.leakedSockets);
  342. // Initialize Winsock.
  343. if (globals.service.status.dwWin32ExitCode = WSAStartup(MAKEWORD(2, 0), &wsaData)) {
  344. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  345. "ServiceMain: WSAStartup() failed, error 0x%08X.\n",
  346. globals.service.status.dwWin32ExitCode));
  347. TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
  348. goto stop_service;
  349. }
  350. globals.initialized.winsock = TRUE;
  351. // Initialize the socket contexts.
  352. globals.io.master.s = INVALID_SOCKET;
  353. globals.io.master.buffersize = TFTPD_DEF_BUFFER;
  354. globals.io.master.datasize = TFTPD_DEF_DATA;
  355. globals.io.master.lowWaterMark = globals.parameters.lowWaterMark;
  356. globals.io.master.highWaterMark = globals.parameters.highWaterMark;
  357. globals.io.def.s = INVALID_SOCKET;
  358. globals.io.def.buffersize = TFTPD_DEF_BUFFER;
  359. globals.io.def.datasize = TFTPD_DEF_DATA;
  360. globals.io.def.lowWaterMark = globals.parameters.lowWaterMark;
  361. globals.io.def.highWaterMark = globals.parameters.highWaterMark;
  362. globals.io.mtu.s = INVALID_SOCKET;
  363. globals.io.mtu.buffersize = TFTPD_MTU_BUFFER;
  364. globals.io.mtu.datasize = TFTPD_MTU_DATA;
  365. globals.io.mtu.lowWaterMark = globals.parameters.lowWaterMark;
  366. globals.io.mtu.highWaterMark = globals.parameters.highWaterMark;
  367. globals.io.max.s = INVALID_SOCKET;
  368. globals.io.max.buffersize = TFTPD_MAX_BUFFER;
  369. globals.io.max.datasize = TFTPD_MAX_DATA;
  370. globals.io.max.lowWaterMark = globals.parameters.lowWaterMark;
  371. globals.io.max.highWaterMark = globals.parameters.highWaterMark;
  372. // Initialize the context hash table.
  373. globals.hash.table =
  374. (PTFTPD_HASH_BUCKET)HeapAlloc(globals.hServiceHeap,
  375. HEAP_ZERO_MEMORY,
  376. (globals.parameters.hashEntries *
  377. sizeof(TFTPD_HASH_BUCKET)));
  378. if (globals.hash.table == NULL)
  379. goto stop_service;
  380. for (x = 0; x < globals.parameters.hashEntries; x++) {
  381. __try { InitializeCriticalSection(&globals.hash.table[x].cs); }
  382. __except (EXCEPTION_EXECUTE_HANDLER) {
  383. int y;
  384. globals.service.status.dwWin32ExitCode = _exception_code();
  385. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  386. "InitializeCriticalSection() raised exception 0x%08X.\n",
  387. globals.service.status.dwWin32ExitCode));
  388. TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
  389. for (y = (x - 1); y >= 0; y--)
  390. DeleteCriticalSection(&globals.hash.table[y].cs);
  391. HeapFree(globals.hServiceHeap, 0, globals.hash.table);
  392. globals.hash.table = NULL;
  393. goto stop_service;
  394. }
  395. InitializeListHead(&globals.hash.table[x].bucket);
  396. }
  397. globals.initialized.contextHashTable = TRUE;
  398. //
  399. // Start the thread pool :
  400. //
  401. // Create the timer queue for timeouts.
  402. globals.io.hTimerQueue = CreateTimerQueue();
  403. if (globals.io.hTimerQueue == NULL) {
  404. globals.service.status.dwWin32ExitCode = GetLastError();
  405. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  406. "ServiceMain: CreateTimerQueue() failed, error 0x%08X.\n",
  407. globals.service.status.dwWin32ExitCode));
  408. TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
  409. goto stop_service;
  410. }
  411. // Obtain the udp tftp service port to bind the master UDP service socket to.
  412. if ((servent = getservbyname("tftp", "udp")) == NULL) {
  413. globals.service.status.dwWin32ExitCode = WSAGetLastError();
  414. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  415. "ServiceMain: getservbyname() failed, error 0x%08X.\n",
  416. globals.service.status.dwWin32ExitCode));
  417. TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
  418. goto stop_service;
  419. }
  420. addr.sin_family = AF_INET;
  421. addr.sin_addr.s_addr = INADDR_ANY;
  422. addr.sin_port = servent->s_port;
  423. // Create the master UDP service socket.
  424. TftpdIoInitializeSocketContext(&globals.io.master, &addr, NULL);
  425. if (globals.io.master.s == INVALID_SOCKET) {
  426. globals.service.status.dwWin32ExitCode = GetLastError();
  427. TFTPD_DEBUG((TFTPD_DBG_SERVICE,
  428. "ServiceMain: TftpdIoInitializeSocketContext() failed, error 0x%08X.\n",
  429. globals.service.status.dwWin32ExitCode));
  430. TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
  431. goto stop_service;
  432. }
  433. // Notify the service control manager that we're ready to go.
  434. globals.service.status.dwCurrentState = SERVICE_RUNNING;
  435. SetServiceStatus(globals.service.hStatus, &globals.service.status);
  436. TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceMain(): Service running.\n"));
  437. return;
  438. stop_service :
  439. TftpdShutdownService();
  440. } // TftpdServiceMain()
  441. void WINAPI
  442. TftpdServiceHandler(DWORD dwOpcode) {
  443. switch (dwOpcode) {
  444. case SERVICE_CONTROL_INTERROGATE :
  445. SetServiceStatus(globals.service.hStatus, &globals.service.status);
  446. TFTPD_DEBUG((TFTPD_TRACE_SERVICE,
  447. "TftpdServiceHandler(SERVICE_CONTROL_INTERROGATE)...\n"
  448. "\tMax Clients : %d\n"
  449. "\tTimeouts : %d\n"
  450. "\tDrops : %d\n"
  451. "\tPrivate sockets : %d\n"
  452. "\tSorcerer's Apprentice : %d\n",
  453. globals.performance.maxClients,
  454. globals.performance.timeouts,
  455. globals.performance.drops,
  456. globals.performance.privateSockets,
  457. globals.performance.sorcerersApprentice));
  458. break;
  459. case SERVICE_CONTROL_STOP :
  460. TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceHandler: SERVICE_CONTROL_STOP.\n"));
  461. if (globals.service.shutdown) {
  462. if (globals.service.shutdown == 1)
  463. SetServiceStatus(globals.service.hStatus, &globals.service.status);
  464. return;
  465. }
  466. TftpdShutdownService();
  467. break;
  468. default :
  469. TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceHandler: Unknown request 0x%08X.\n", dwOpcode));
  470. } // switch (dwOpcode)
  471. } // TftpdServiceHandler()
  472. void __cdecl
  473. main(int argc, char *argv[]) {
  474. SERVICE_TABLE_ENTRY dispatch[] = {
  475. { TEXT("Tftpd"), (LPSERVICE_MAIN_FUNCTION)TftpdServiceMain },
  476. { NULL, NULL }
  477. };
  478. StartServiceCtrlDispatcher(dispatch);
  479. } // main()