debugserverintf.pp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2003 by the Free Pascal development team
  4. Interface for debug server.
  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. {$mode objfpc}
  12. {$h+}
  13. unit debugserverintf;
  14. Interface
  15. Uses
  16. msgintf,baseunix,classes,sockets,sysutils;
  17. Const
  18. MsgTypes : Array[-1..3] of string =
  19. ('Disconnect','Information','Warning','Error','Identify');
  20. Type
  21. Thandle = Longint; // Abstraction for easier porting.
  22. TClient = Class(TObject)
  23. Handle : THandle;
  24. Peer : ShortString;
  25. Data : Pointer;
  26. end;
  27. TDebugEvent = Record
  28. Client : TClient;
  29. LogCode : Integer;
  30. TimeStamp : TDateTime;
  31. Event : String;
  32. end;
  33. Var
  34. FClients : TList;
  35. Accepting : Boolean;
  36. Quit : Boolean;
  37. DebugLogCallback : Procedure (Const Event : TDebugEvent);
  38. DebugObjLogCallBack : Procedure (Const Event : TDebugEvent) of Object;
  39. CloseConnectionCallBack : Procedure (Client : TClient);
  40. CloseObjConnectionCallBack : Procedure (Client : TClient) of Object;
  41. Procedure OpenDebugServer;
  42. Procedure CloseDebugServer;
  43. Function ClientFromHandle (AHandle : THandle) : TClient;
  44. Procedure ReadMessage(Handle : THandle);
  45. Procedure ReadMessageEvent(Handle : THandle; Var Event : TDebugEvent);
  46. Function CheckNewConnection : TClient;
  47. procedure CloseConnection(Client : TClient);
  48. Procedure CloseClientHandle(Handle : THandle);
  49. ResourceString
  50. SClientLog = 'Client log %d';
  51. SEvent = 'Event';
  52. SMessage = 'Message';
  53. SStopAccepting = 'Stop accepting new connections';
  54. SStartAccepting = 'Start accepting new connections';
  55. SErrSocketFailed = 'Creation of socket failed: %s';
  56. SErrBindFailed = 'Binding of socket failed: %s';
  57. SErrListenFailed = 'Listening on port #%d failed: %s';
  58. SErrAcceptFailed = 'Could not accept a client connection: %d';
  59. SClosingConnection = 'Closing connection.';
  60. SErrFailedToSetSignalHandler = 'Failed to set signal handler.';
  61. SPeerAt = 'Peer at %d';
  62. Implementation
  63. Function ClientFromHandle (AHandle : THandle) : TClient;
  64. Var
  65. I : Longint;
  66. begin
  67. Result:=Nil;
  68. I:=0;
  69. With FClients do
  70. While (I<Count) and (Result=Nil) do
  71. Begin
  72. If TClient(Items[i]).Handle=AHandle then
  73. Result:=TClient(Items[i]);
  74. Inc(I);
  75. end;
  76. end;
  77. { ---------------------------------------------------------------------
  78. Communications handling: Unix Socket setup
  79. ---------------------------------------------------------------------}
  80. Var
  81. FSocket : Integer;
  82. Procedure SetupUnixSocket;
  83. var
  84. Flags,AddrLen : Integer;
  85. FUnixAddr : TUnixSockAddr;
  86. FFileName : String;
  87. Quit : Boolean;
  88. begin
  89. FFileName:=DebugSocket;
  90. FSocket:=FPSocket(AF_UNIX,SOCK_STREAM,0);
  91. If FSocket<0 Then
  92. Raise Exception.Create(SErrSocketFailed);
  93. Flags:=fpFCntl(FSOCket,F_GETFL);
  94. Flags:=Flags or O_NONBLOCK;
  95. fpFCntl(FSocket,F_SETFL,Flags);
  96. Str2UnixSockAddr(FFilename,FUnixAddr,AddrLen);
  97. If FPBind(FSocket,@FUnixAddr,AddrLen)<>0 then
  98. Raise Exception.CreateFmt(SErrBindFailed,[FFileName]);
  99. If (fpListen(FSocket,5)<>0) then
  100. Raise Exception.CreateFmt(SErrListenFailed,[FSocket]);
  101. FClients:=TList.Create;
  102. Accepting:=True;
  103. end;
  104. Procedure DestroyUnixSocket;
  105. Var
  106. C : TClient;
  107. begin
  108. If Assigned(FClients) then
  109. begin
  110. With FClients do
  111. While Count>0 do
  112. begin
  113. C:=TClient(Items[Count-1]);
  114. FileClose(C.Handle);
  115. C.Free;
  116. Delete(Count-1);
  117. end;
  118. FileClose(FSocket);
  119. DeleteFile(DebugSocket);
  120. end;
  121. end;
  122. { ---------------------------------------------------------------------
  123. Communications handling: Inet Socket setup
  124. ---------------------------------------------------------------------}
  125. Procedure SetupInetSocket(Aport : Word);
  126. var
  127. Flags,AddrLen : Integer;
  128. FInetAddr : TInetSockAddr;
  129. FFileName : String;
  130. Quit : Boolean;
  131. begin
  132. FSocket:=FPSocket(AF_INET,SOCK_STREAM,0);
  133. If FSocket<0 Then
  134. Raise Exception.Create(SErrSocketFailed);
  135. Flags:=fpFCntl(FSocket,F_GETFL);
  136. Flags:=Flags or O_NONBLOCK;
  137. fpFCntl(FSocket,F_SETFL,Flags);
  138. FInetAddr.Family := AF_INET;
  139. Writeln('Using port : ',APort);
  140. FInetAddr.Port := Swap(APort);
  141. FInetAddr.Addr := 0;
  142. If FPBind(FSocket,@FInetAddr,SizeOf(FInetAddr))<>0 then
  143. Raise Exception.CreateFmt(SErrBindFailed,[FFileName]);
  144. If fpListen(FSocket,5)<>0 then
  145. Raise Exception.CreateFmt(SErrListenFailed,[FSocket]);
  146. end;
  147. Procedure DestroyInetSocket;
  148. Var
  149. C : TClient;
  150. begin
  151. If Assigned(FClients) then
  152. begin
  153. With FClients do
  154. While Count>0 do
  155. begin
  156. C:=TClient(Items[Count-1]);
  157. FileClose(C.Handle);
  158. C.Free;
  159. Delete(Count-1);
  160. end;
  161. FileClose(FSocket);
  162. end;
  163. end;
  164. { ---------------------------------------------------------------------
  165. Communications handling: Public interface
  166. ---------------------------------------------------------------------}
  167. Procedure OpenDebugServer;
  168. begin
  169. Case DebugConnection of
  170. dcUnix : SetupUnixSocket;
  171. dcInet : SetupInetSocket(DebugPort);
  172. end;
  173. FClients:=TList.Create;
  174. Accepting:=True;
  175. end;
  176. Procedure CloseDebugServer;
  177. begin
  178. Accepting:=False;
  179. Case DebugConnection of
  180. dcUnix : DestroyUnixSocket;
  181. dcInet : DestroyInetSocket;
  182. end;
  183. FClients.Free;
  184. FClients:=Nil;
  185. end;
  186. { ---------------------------------------------------------------------
  187. Communications handling: Connection handling
  188. ---------------------------------------------------------------------}
  189. Function GetNewConnection : THandle;
  190. Var
  191. ClientAddr: TUnixSockAddr;
  192. L : Integer;
  193. begin
  194. If Accepting then
  195. begin
  196. L:=SizeOf(ClientAddr);
  197. Result:=fpAccept(FSocket,@ClientAddr,@L);
  198. If (Result<0) Then
  199. if (Errno<>ESYSEAgain) then
  200. Raise Exception.CreateFmt(SErrAcceptFailed,[FSocket])
  201. else
  202. Result:=-1
  203. {$ifdef debug}
  204. else
  205. Writeln('New connection detected at ',Result)
  206. {$endif debug}
  207. end
  208. else
  209. Result:=-1;
  210. end;
  211. Function CheckNewConnection : TClient;
  212. Var
  213. NC : THandle;
  214. begin
  215. NC:=GetNewConnection;
  216. If (NC=-1) then
  217. Result:=Nil
  218. else
  219. begin
  220. Result:=TClient.Create;
  221. Result.Handle:=NC;
  222. {$ifdef debug}
  223. Writeln('Added new client', nc, ' at : ',FClients.Add(Result));
  224. {$else}
  225. FClients.Add(Result);
  226. {$endif debug}
  227. end;
  228. end;
  229. Procedure CloseClientHandle(Handle : THandle);
  230. begin
  231. fpShutDown(Handle,2);
  232. FileClose(Handle);
  233. end;
  234. Procedure CloseConnection(Client : TClient);
  235. Var
  236. I : longint;
  237. C : TClient;
  238. begin
  239. If Assigned(Client) then
  240. begin
  241. If Assigned(CloseConnectionCallBack) then
  242. CloseConnectionCallBack(Client);
  243. If Assigned(CloseObjConnectionCallBack) then
  244. CloseObjConnectionCallBack(Client);
  245. CloseClientHandle(Client.Handle);
  246. FClients.Remove(Client);
  247. Client.Free;
  248. end;
  249. end;
  250. { ---------------------------------------------------------------------
  251. Message handling
  252. ---------------------------------------------------------------------}
  253. Function MsgToEvent(AHandle: THandle; ALogCode : Integer; ATimeStamp : TDateTime; AEvent : String) : TDebugEvent;
  254. begin
  255. With Result do
  256. begin
  257. Client:=ClientFromHandle(AHandle);
  258. If (Client<>Nil) then
  259. begin
  260. If (ALogCode=lctIdentify) then
  261. Client.Peer:=AEvent;
  262. end;
  263. LogCode:=ALogCode;
  264. TimeStamp:=ATimeStamp;
  265. Event:=AEvent;
  266. end;
  267. end;
  268. Procedure LogEvent(Event : TDebugEvent);
  269. begin
  270. if Assigned(DebugLogCallback) then
  271. DebugLogCallBack(Event);
  272. If Assigned(DebugObjLogCallBack) then
  273. DebugObjLogCallBack(Event);
  274. end;
  275. Procedure ReadMessageEvent(Handle : THandle; Var Event : TDebugEvent);
  276. Var
  277. FDebugMessage : TDebugMessage;
  278. msgSize : Integer;
  279. begin
  280. Try
  281. With FDebugMessage do
  282. begin
  283. // Select reports read ready when closed, so check for this.
  284. If (FileRead(Handle,msgType,SizeOf(Integer))=0) or (MsgType=-1) then
  285. begin
  286. event:=MsgToEvent(Handle,lctStop,Now,SClosingConnection);
  287. If Assigned(Event.Client) then
  288. CloseConnection(Event.Client)
  289. else
  290. CloseClientHandle(Handle);
  291. end
  292. else
  293. begin
  294. FileRead(Handle,msgTimeStamp,sizeof(TDateTime));
  295. FileRead(Handle,MsgSize,SizeOf(Integer));
  296. SetLength(Msg,MsgSize);
  297. FileRead(Handle,Msg[1],MsgSize);
  298. Event:=MsgToEvent(Handle,msgType,msgTimeStamp,Msg);
  299. end
  300. end;
  301. except
  302. On E : Exception do
  303. Event:=MsgToEvent(Handle,lctError,Now,E.Message);
  304. end;
  305. end;
  306. Procedure ReadMessage(Handle : THandle);
  307. Var
  308. Event : TDebugEvent;
  309. begin
  310. ReadMessageEvent(Handle,Event);
  311. LogEvent(Event);
  312. end;
  313. end.