daemonapp.inc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. {
  2. $Id: header,v 1.1 2000/07/13 06:33:45 michael Exp $
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 1999-2000 by the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. { Win32 implementation of service application }
  12. uses windows,jwawinsvc;
  13. Const
  14. CM_SERVICE_CONTROL_CODE = WM_USER+1;
  15. Resourcestring
  16. SErrRegisterHandler = 'Could not register control handler, error code %d';
  17. SErrNoControlContext = 'Not handling Control message without control context: (%d %d %d).';
  18. SControlCodeReceived = 'Control message received: (%d %d %d).';
  19. function StartServiceCtrlDispatcher(lpServiceStartTable: LPSERVICE_TABLE_ENTRY): BOOL; stdcall; external 'advapi32.dll' name 'StartServiceCtrlDispatcherA';
  20. function RegisterServiceCtrlHandlerEx(lpServiceName: LPCSTR;lpHandlerProc: LPHANDLER_FUNCTION_EX; lpContext: LPVOID): SERVICE_STATUS_HANDLE; stdcall;external 'advapi32.dll' name 'RegisterServiceCtrlHandlerExA';
  21. function SetServiceStatus(hServiceStatus: SERVICE_STATUS_HANDLE; const lpServiceStatus: SERVICE_STATUS): BOOL; stdcall; external 'advapi32.dll' name 'SetServiceStatus';
  22. { ---------------------------------------------------------------------
  23. Win32 entry points
  24. ---------------------------------------------------------------------}
  25. Function ServiceControlHandlerEntry(dwControl,dwEventType: DWord; lpEventData,lpContext : Pointer) : DWord; StdCall;
  26. begin
  27. If (Nil<>lpContext) then
  28. TDaemonController(lpContext).Controller(dwControl,dwEventType,lpEventData)
  29. else
  30. If Assigned(Application.Logger) then
  31. Application.Logger.Error(SerrNoControlContext,[dwControl,dwEventType,ptrint(lpEventData)]);
  32. end;
  33. Procedure ServiceMainEntry(Argc : DWord; Args : Pchar); stdcall;
  34. begin
  35. If Assigned(Application) then
  36. Application.Main(Argc,PPChar(Args));
  37. {$ifdef svcdebug}Debuglog('Main end');{$endif}
  38. end;
  39. { ---------------------------------------------------------------------
  40. TDaemonStartThread
  41. ---------------------------------------------------------------------}
  42. Type
  43. TDaemonStartThread = Class(TThread)
  44. FEntryTable : PServiceTableEntry;
  45. FLoopHandle : THandle;
  46. Public
  47. Constructor Create(T : PServiceTableEntry; LoopHandle : THandle);
  48. Procedure DoTerminate; override;
  49. Procedure Execute; override;
  50. Property ReturnValue;
  51. end;
  52. { TDaemonStartThread }
  53. constructor TDaemonStartThread.create(T: PServiceTableEntry; LoopHandle : THandle);
  54. begin
  55. FEntryTable:=T;
  56. FLoopHandle:=LoopHandle;
  57. FreeOnTerminate:=False;
  58. ReturnValue:=0;
  59. Inherited Create(False,DefaultStackSize*2);
  60. end;
  61. procedure TDaemonStartThread.DoTerminate;
  62. begin
  63. Inherited DoTerminate;
  64. If (FLoopHandle<>0) then
  65. PostMessage(FLoopHandle,WM_QUIT,0,0);
  66. end;
  67. procedure TDaemonStartThread.Execute;
  68. begin
  69. {$ifdef svcdebug}DebugLog('Calling service dispatcher');{$endif svcdebug}
  70. if StartServiceCtrlDispatcher(FEntryTable) then
  71. ReturnValue:=0
  72. else
  73. ReturnValue:=GetLastError;
  74. {$ifdef svcdebug}DebugLog('Called service dispatcher');{$endif svcdebug}
  75. end;
  76. { ---------------------------------------------------------------------
  77. TSCMData : private data of controller.
  78. ---------------------------------------------------------------------}
  79. Type
  80. TSCMData = Class(TObject)
  81. FHandle : SERVICE_STATUS_HANDLE;
  82. Constructor Create(AHandle : SERVICE_STATUS_HANDLE);
  83. end;
  84. Constructor TSCMData.Create(AHandle : SERVICE_STATUS_HANDLE);
  85. begin
  86. FHandle:=AHandle;
  87. end;
  88. { ---------------------------------------------------------------------
  89. TSMData : private data of Application.
  90. ---------------------------------------------------------------------}
  91. Type
  92. { TSMData }
  93. TSMData = Class(TObject)
  94. Private
  95. FHandle : SC_HANDLE;
  96. Public
  97. Constructor Create(AHandle : SC_HANDLE);
  98. Destructor Destroy; override;
  99. end;
  100. Constructor TSMData.Create(AHandle : SC_HANDLE);
  101. begin
  102. FHandle:=AHandle;
  103. end;
  104. destructor TSMData.Destroy;
  105. begin
  106. inherited;
  107. end;
  108. { ---------------------------------------------------------------------
  109. TCustomDaemonApplication
  110. ---------------------------------------------------------------------}
  111. const
  112. WinServiceTypes : array[TServiceType] of Integer
  113. = (SERVICE_WIN32_OWN_PROCESS, SERVICE_KERNEL_DRIVER,
  114. SERVICE_FILE_SYSTEM_DRIVER);
  115. WinStartTypes : array[TStartType] of Integer
  116. = (SERVICE_BOOT_START, SERVICE_SYSTEM_START,
  117. SERVICE_AUTO_START, SERVICE_DEMAND_START,
  118. SERVICE_DISABLED);
  119. WinErrorSeverities : array[TErrorSeverity] of Integer
  120. = (SERVICE_ERROR_IGNORE, SERVICE_ERROR_NORMAL,
  121. SERVICE_ERROR_SEVERE, SERVICE_ERROR_CRITICAL);
  122. // Careful, result of this function must be freed !!
  123. Function GetDependencies(D : TDependencies) : PChar;
  124. var
  125. I,L : Integer;
  126. P : PChar;
  127. begin
  128. Result:=Nil;
  129. L:=0;
  130. for i:=0 to D.Count-1 do
  131. Inc(L, Length(D[i].Name)+1+Ord(D[i].IsGroup));
  132. if (L<>0) then
  133. begin
  134. Inc(L); // For final null-terminator;
  135. GetMem(Result,L);
  136. P:=Result;
  137. for i:=0 to D.Count - 1 do
  138. begin
  139. if D[i].IsGroup then
  140. begin
  141. P^:=Char(SC_GROUP_IDENTIFIER);
  142. Inc(P);
  143. end;
  144. P:=StrECopy(P,PChar(D[i].Name));
  145. Inc(P);
  146. end;
  147. P^:=#0;
  148. end;
  149. end;
  150. Procedure TCustomDaemonApplication.SysInstallDaemon(Daemon : TCustomDaemon);
  151. Var
  152. SM,SV: SC_HANDLE;
  153. N,DN,E,LG,UN,UP : String;
  154. DD : TDaemonDef;
  155. ST,STT,ES: Integer;
  156. IDTag : DWord;
  157. PIDTag : LPDWord;
  158. PDeps,PN,PP : PChar;
  159. begin
  160. SM:=TSMData(FSysData).FHandle;
  161. DD:=Daemon.Definition;
  162. E:=Paramstr(0);
  163. If (Pos(' ',E)<>0) then
  164. E:='"'+E+'"';
  165. E:=E+' --run'; // Add --run argument;
  166. N:=DD.Name;
  167. DN:=DD.DisplayName;
  168. With DD.WinBindings do
  169. begin
  170. LG:=GroupName;
  171. UN:=UserName;
  172. If (UN='') then
  173. PN:=Nil
  174. else
  175. PN:=PChar(UN);
  176. UP:=Password;
  177. if (UP='') then
  178. PP:=Nil
  179. else
  180. PP:=PChar(UP);
  181. // ServiceType ST
  182. ST:=WinServiceTypes[ServiceType];
  183. if (doInteractive in DD.Options) and (ServiceType=stWin32) then
  184. ST:=ST or SERVICE_INTERACTIVE_PROCESS;
  185. // Actually, we should check count of enabled definitons only :/
  186. if (ServiceType=stWin32) and (FMapper.DaemonDefs.Count>1) then
  187. ST:=(ST xor SERVICE_WIN32_OWN_PROCESS) or SERVICE_WIN32_SHARE_PROCESS;
  188. // StartType STT
  189. STT:=WinStartTypes[StartType];
  190. if (StartType in [stBoot, stSystem]) and (ServiceType<>stDevice) then
  191. STT:=SERVICE_AUTO_START;
  192. IDTag:=IDTag;
  193. If (IDTag<>0) then
  194. PIDTag:=@IDTag
  195. else
  196. PIDTag:=Nil;
  197. ES:=WinErrorSeverities[ErrorSeverity];
  198. PDeps:=GetDependencies(Dependencies);
  199. end;
  200. Try
  201. flush(output);
  202. SV:=CreateService(SM, PChar(N), PChar(DN), SERVICE_ALL_ACCESS, ST, STT, ES,
  203. PChar(E), PChar(LG), PIDTag, PDeps, PN, PP);
  204. If (SV=0) then
  205. RaiseLastOSError;
  206. Try
  207. If (PIDTag<>Nil) then
  208. DD.WinBindings.IDTag:=IDTag;
  209. finally
  210. CloseServiceHandle(SV);
  211. end
  212. Finally
  213. If (PDeps<>Nil) then
  214. FreeMem(PDeps);
  215. end;
  216. end;
  217. procedure TCustomDaemonApplication.SysUnInstallDaemon(Daemon: TCustomDaemon);
  218. var
  219. SM,SV : SC_HANDLE;
  220. DN : String;
  221. begin
  222. SM:=TSMData(FSysData).FHandle;
  223. DN:=Daemon.Definition.Name;
  224. SV:=OpenService(SM,PChar(DN),SERVICE_ALL_ACCESS);
  225. if (SV=0) then
  226. RaiseLastOSError;
  227. try
  228. if Not DeleteService(SV) then
  229. RaiseLastOSError;
  230. finally
  231. CloseServiceHandle(SV);
  232. end;
  233. end;
  234. procedure TCustomDaemonApplication.SysStartUnInstallDaemons;
  235. Var
  236. SM : SC_HANDLE;
  237. begin
  238. SM:=OpenSCManager(Nil,Nil,SC_MANAGER_ALL_ACCESS);
  239. if (SM=0) then
  240. RaiseLastOSError;
  241. FSysData:=TSMData.Create(SM);
  242. end;
  243. procedure TCustomDaemonApplication.SysEndUnInstallDaemons;
  244. begin
  245. CloseServiceHandle(TSMData(FSysData).FHandle);
  246. FreeandNil(FSysData);
  247. end;
  248. procedure TCustomDaemonApplication.SysStartInstallDaemons;
  249. Var
  250. SM : SC_HANDLE;
  251. begin
  252. SM:=OpenSCManager(Nil,Nil,SC_MANAGER_ALL_ACCESS);
  253. if (SM=0) then
  254. RaiseLastOSError;
  255. FSysData:=TSMData.Create(SM);
  256. end;
  257. procedure TCustomDaemonApplication.SysEndInstallDaemons;
  258. begin
  259. CloseServiceHandle(TSMData(FSysData).FHandle);
  260. FreeandNil(FSysData);
  261. end;
  262. procedure TCustomDaemonApplication.SysStartRunDaemons;
  263. begin
  264. // Do nothing.
  265. end;
  266. Function TCustomDaemonApplication.RunGUIloop(P : Pointer) : integer;
  267. Const
  268. HandleOpts=WS_POPUP or WS_CAPTION or WS_CLIPSIBLINGS or WS_SYSMENU or WS_MINIMIZEBOX;
  269. Var
  270. T : TDaemonStartThread;
  271. Msg : TMsg;
  272. TClass: TWndClass;
  273. AWClass: TWndClass = (
  274. style: 0;
  275. lpfnWndProc: @DefWindowProc;
  276. cbClsExtra: 0;
  277. cbWndExtra: 0;
  278. hInstance: 0;
  279. hIcon: 0;
  280. hCursor: 0;
  281. hbrBackground: 0;
  282. lpszMenuName: nil;
  283. lpszClassName: 'TDaemonApplication');
  284. begin
  285. If (GUIHandle=0) then
  286. begin
  287. if not GetClassInfo(HInstance,AWClass.lpszClassName,TClass) then
  288. begin
  289. AWClass.hInstance := HInstance;
  290. if Windows.RegisterClass(AWClass) = 0 then
  291. DaemonError(SErrWindowClass);
  292. end;
  293. GUIHandle := CreateWindow(AWClass.lpszClassName, Pchar(Title),
  294. HandleOpts, 1,1, 0, 0, 0, 0, HInstance, nil);
  295. end;
  296. T:=TDaemonStartThread.Create(P,GUIHandle);
  297. Try
  298. If Assigned(GuiMainLoop) then
  299. GUIMainLoop
  300. else
  301. begin
  302. // Run a message loop.
  303. Msg.Message:=0;
  304. Repeat
  305. if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
  306. begin
  307. if (Msg.Message<>WM_QUIT) and (Msg.Message<>WM_ENDSESSION) then
  308. begin
  309. TranslateMessage(Msg);
  310. DispatchMessage(Msg);
  311. end
  312. else
  313. Terminate;
  314. end;
  315. Until Terminated;
  316. end;
  317. finally
  318. Result:=T.ReturnValue;
  319. T.Free;
  320. end;
  321. end;
  322. procedure TCustomDaemonApplication.SysEndRunDaemons;
  323. Var
  324. P : PServiceTableEntry;
  325. I,C : Integer;
  326. RV : Integer;
  327. begin
  328. GetMem(P,SizeOf(TServiceTableEntry)*FMapper.DaemonDefs.Count+1);
  329. Try
  330. C:=FMapper.DaemonDefs.Count;
  331. For I:=0 to C-1 do
  332. If Assigned(FMapper.DaemonDefs[i].Instance) then
  333. begin
  334. P[i].lpServiceName:=Pchar(FMapper.DaemonDefs[i].Name);
  335. P[i].lpServiceProc:=@ServiceMainEntry;
  336. end;
  337. // Set last entry to Nil.
  338. P[C].lpServiceName:=Nil;
  339. P[C].lpServiceProc:=Nil;
  340. If IsConsole then
  341. begin
  342. {$ifdef svcdebug}DebugLog('Starting ctrl dispatcher');{$endif svcdebug}
  343. Try
  344. if StartServiceCtrlDispatcher(P) then
  345. begin
  346. {$ifdef svcdebug}DebugLog('Return of dispatcher OK');{$endif svcdebug}
  347. RV:=0;
  348. end
  349. else
  350. begin
  351. RV:=GetLastError;
  352. {$ifdef svcdebug}DebugLog('Return of dispatcher NOK');{$endif svcdebug}
  353. end;
  354. except
  355. On E : Exception do
  356. begin
  357. {$ifdef svcdebug}Debuglog('Caught exception : '+E.MEssage);{$endif svcdebug}
  358. Raise;
  359. end;
  360. end;
  361. end
  362. else
  363. begin
  364. RV:=RunGuiLoop(P);
  365. end;
  366. {$ifdef svcdebug}DebugLog('SysRun Terminating');{$endif svcdebug}
  367. Terminate;
  368. If (RV<>0) then
  369. Logger.Error(SErrServiceManagerStartFailed,[SysErrorMessage(RV)]);
  370. Finally
  371. FreeMem(P);
  372. end;
  373. end;
  374. procedure TCustomDaemonApplication.RemoveController(
  375. AController: TDaemonController);
  376. begin
  377. FreeAndNil(AController.FDaemon);
  378. AController.Free;
  379. end;
  380. { ---------------------------------------------------------------------
  381. TDaemonThread
  382. ---------------------------------------------------------------------}
  383. procedure TDaemonThread.StartServiceExecute;
  384. Var
  385. Msg : TMsg;
  386. begin
  387. PeekMessage(Msg,0,WM_USER,WM_USER,PM_NOREMOVE);
  388. end;
  389. procedure TDaemonThread.CheckControlMessage(WaitForMessage : Boolean);
  390. Var
  391. Msg : TMsg;
  392. StopLoop : Boolean;
  393. begin
  394. StopLoop:=False;
  395. Repeat
  396. StopLoop:=Terminated and WaitForMessage;
  397. If Not StopLoop then
  398. begin
  399. If WaitForMessage Then
  400. StopLoop:=Not GetMessage(Msg,0,0,0)
  401. else
  402. StopLoop:=Not PeekMessage(Msg,0,0,0,PM_REMOVE);
  403. If Not StopLoop then
  404. begin
  405. If (Msg.hwnd<>0) or (Msg.Message<>CM_SERVICE_CONTROL_CODE) then
  406. DispatchMessage(Msg)
  407. else
  408. HandleControlCode(Msg.wParam);
  409. end;
  410. end;
  411. Until StopLoop;
  412. end;
  413. { ---------------------------------------------------------------------
  414. TDaemonController
  415. ---------------------------------------------------------------------}
  416. procedure TDaemonController.StartService;
  417. begin
  418. Main(0,Nil);
  419. end;
  420. procedure TDaemonController.Main(Argc: DWord; Args: PPChar);
  421. Var
  422. T : TThread;
  423. H : SERVICE_STATUS_HANDLE;
  424. I : Integer;
  425. begin
  426. For I:=0 to Argc-1 do
  427. FParams.Add(StrPas(Args[I]));
  428. H:=RegisterServiceCtrlHandlerEx(Args[0],@ServiceControlHandlerEntry,Self);
  429. if (H=0) then
  430. Application.Logger.Error(SErrRegisterHandler,[getlasterror]);
  431. FSysData:=TSCMData.Create(H);
  432. FDaemon.Status:=csStartPending;
  433. Try
  434. T:=TDaemonThread.Create(FDaemon);
  435. T.Resume;
  436. T.WaitFor;
  437. FreeAndNil(T);
  438. FDaemon.FThread:=Nil;
  439. except
  440. On E : Exception do
  441. FDaemon.Logmessage(Format(SErrDaemonStartFailed,[FDaemon.Definition.Name,E.Message]));
  442. end;
  443. end;
  444. procedure TDaemonController.Controller(ControlCode, EventType: DWord;
  445. EventData: Pointer);
  446. Var
  447. TID : THandle;
  448. begin
  449. if Assigned(FDaemon.FThread) then
  450. begin
  451. TID:=FDaemon.FThread.ThreadID;
  452. If FDaemon.FThread.Suspended then
  453. FDaemon.FThread.Resume;
  454. PostThreadMessage(TID,CM_SERVICE_CONTROL_CODE,ControlCode,EventType);
  455. end;
  456. end;
  457. function TDaemonController.ReportStatus: Boolean;
  458. Function GetAcceptedCodes : Integer;
  459. begin
  460. Result := SERVICE_ACCEPT_SHUTDOWN;
  461. if doAllowStop in FDAemon.Definition.Options then
  462. Result := Result or SERVICE_ACCEPT_STOP;
  463. if doAllowPause in FDAemon.Definition.Options then
  464. Result := Result or SERVICE_ACCEPT_PAUSE_CONTINUE;
  465. end;
  466. Var
  467. S : String;
  468. DD : TDaemonDef;
  469. SS : TServiceStatus;
  470. WB : TWinBindings;
  471. Const
  472. WinServiceStatus : array[TCurrentStatus] of Integer
  473. = (SERVICE_STOPPED, SERVICE_START_PENDING,
  474. SERVICE_STOP_PENDING, SERVICE_RUNNING,
  475. SERVICE_CONTINUE_PENDING, SERVICE_PAUSE_PENDING,
  476. SERVICE_PAUSED);
  477. PendingStatus : set of TCurrentStatus
  478. = [csStartPending, csStopPending,
  479. csContinuePending,csPausePending];
  480. begin
  481. If not Assigned(FDaemon) then
  482. begin
  483. Application.Logger.Error(SErrNoDaemonForStatus,[Name]);
  484. Exit;
  485. end;
  486. DD:=FDaemon.Definition;
  487. If not Assigned(DD) then
  488. begin
  489. Application.Logger.Error(SErrNoDaemonDefForStatus,[Name]);
  490. Exit;
  491. end;
  492. DD.LogStatusReport:=True;
  493. {$ifndef svcdebug}
  494. If DD.LogStatusReport then
  495. {$endif svcdebug}
  496. With FDaemon do
  497. begin
  498. S:=Format(SDaemonStatus,[Definition.DisplayName, CurrentStatusNames[Status]]);
  499. Application.Logger.Info(S);
  500. {$ifdef svcdebug}DebugLog(S);{$endif svcdebug}
  501. end;
  502. FillChar(SS,SizeOf(SS),0);
  503. WB:=DD.WinBindings;
  504. with SS do
  505. begin
  506. dwWaitHint := WB.WaitHint;
  507. dwServiceType :=WinServiceTypes[WB.ServiceType];
  508. if (FDaemon.Status=csStartPending) then
  509. dwControlsAccepted := 0
  510. else
  511. dwControlsAccepted := GetAcceptedCodes;
  512. if (FDaemon.Status in PendingStatus) and (FDaemon.Status = LastStatus) then
  513. Inc(FCheckPoint)
  514. else
  515. FCheckPoint := 0;
  516. dwCheckPoint:=FCheckPoint;
  517. FLastStatus := FDaemon.Status;
  518. dwCurrentState := WinServiceStatus[FDaemon.Status];
  519. dwServiceSpecificExitCode:=WB.ErrCode;
  520. if (WB.ErrCode<>0) then
  521. dwWin32ExitCode:=ERROR_SERVICE_SPECIFIC_ERROR
  522. else
  523. dwWin32ExitCode := WB.Win32ErrCode;
  524. if not SetServiceStatus(TSCMData(FSysData).FHandle, SS) then
  525. Application.Logger.Error(SysErrorMessage(GetLastError));
  526. end;
  527. end;
  528. Procedure TDaemonController.ThreadTerminated(Sender : TObject);
  529. begin
  530. end;
  531. { ---------------------------------------------------------------------
  532. Global initialization/Finalization
  533. ---------------------------------------------------------------------}
  534. Procedure SysInitDaemonApp;
  535. begin
  536. end;
  537. Procedure SysDoneDaemonApp;
  538. begin
  539. end;